From 73fad758e1f6cf6b927298c4ab2d266e61d42098 Mon Sep 17 00:00:00 2001 From: Juan Date: Sat, 13 Jun 2026 04:49:20 +0200 Subject: [PATCH 1/2] feat(roster): add quick-swap for injured players and academy transfer list Implements suggestions 1 & 3 from issue #300. 1. Quick-swap button: active lineup players with < 30% condition now show a red left border + alert icon. A RefreshCw button auto-replaces them with the highest-OVR bench player of the same role via set_active_lineup. 2. Academy transfer list: added ShoppingCart toggle button on each academy player row, using the existing toggle_transfer_list command. Players listed for transfer appear in the Transfer Market tab. --- src/ui-v2/dashboard/tabs/SquadTabV2.tsx | 29 +++++++- src/ui-v2/dashboard/tabs/YouthTabV2.tsx | 92 +++++++++++++++++-------- 2 files changed, 91 insertions(+), 30 deletions(-) diff --git a/src/ui-v2/dashboard/tabs/SquadTabV2.tsx b/src/ui-v2/dashboard/tabs/SquadTabV2.tsx index 11d2c6c1..0a25b520 100644 --- a/src/ui-v2/dashboard/tabs/SquadTabV2.tsx +++ b/src/ui-v2/dashboard/tabs/SquadTabV2.tsx @@ -8,6 +8,7 @@ import { ShoppingCart, User, Loader2, + RefreshCw, } from "lucide-react"; import type { GameStateData, PlayerSelectionOptions } from "@/store/gameStore"; @@ -77,6 +78,7 @@ export function SquadTabV2({ const [sortDir, setSortDir] = useState<"asc" | "desc">("asc"); const [search, setSearch] = useState(""); const [savingSlot, setSavingSlot] = useState(null); + const [swappingSlot, setSwappingSlot] = useState(null); const handleToggleTransfer = useCallback(async (playerId: string) => { try { @@ -308,8 +310,10 @@ export function SquadTabV2({ const morale = player.morale; const annualWage = player.wage; - return ( -
{ if (!(e.target as HTMLElement).closest("select,button")) onSelectPlayer(player.id); }} className="flex cursor-pointer items-center gap-3 px-4 py-3 transition-colors hover:bg-muted/30"> + return ( +
{ if (!(e.target as HTMLElement).closest("select,button")) onSelectPlayer(player.id); }} className={cn("flex cursor-pointer items-center gap-3 px-4 py-3 transition-colors hover:bg-muted/30", + condition < 30 && "border-l-2 border-l-red-500/60 bg-red-500/5" + )}>
{roleLabel}
@@ -326,6 +330,11 @@ export function SquadTabV2({ )} + {condition < 30 && ( + + + + )}
handleSetRoleSub(slot.index, e.target.value)} + className="max-w-[160px] rounded-md border border-border/60 bg-muted/40 pl-1.5 pr-6 py-0.5 text-[11px] text-foreground" + > + + {benchPlayers + .filter((bp) => !activeLineupIds.includes(bp.id)) + .sort((a, b) => { + const aMatch = resolvePlayerLolRole(a) === slot.role ? 0 : 1; + const bMatch = resolvePlayerLolRole(b) === slot.role ? 0 : 1; + return aMatch - bMatch || calculateLolOvr(b) - calculateLolOvr(a); + }) + .map((bp) => ( + + ))} + + {sub && sub.condition < 30 && ( + + + + )} + + ); + })()} +
); })}