Spaces:
Running
Running
| import React from "react"; | |
| import { Trash2 } from "lucide-react"; | |
| export const DraftsComparisonTable = ({ | |
| drafts, horizonGWs, activeDraftId, globalPlayers, | |
| setActiveDraftId, getValidLayout, availableGWs, | |
| baselineFt, ftAtStartOfGw, setDrafts | |
| }) => { | |
| if (!drafts || drafts.length === 0) return null; | |
| const handleDeleteDraft = (e, id) => { | |
| e.stopPropagation(); // Prevents the row click from triggering | |
| if (drafts.length <= 1) return; | |
| const nextDrafts = drafts.filter(d => d.id !== id); | |
| setDrafts(nextDrafts); | |
| if (activeDraftId === id) setActiveDraftId(nextDrafts[0].id); | |
| }; | |
| const getDraftGwState = (draft, gw) => { | |
| if (draft.cachedEvs && draft.cachedEvs[gw]) { | |
| return draft.cachedEvs[gw]; | |
| } | |
| const chip = draft.chipsByGw?.[gw]; | |
| const capMult = chip === "tc" ? 3 : 2; | |
| let squad = []; | |
| let capId = null; | |
| if (gw === draft.activeGW) { | |
| squad = draft.teamData; | |
| capId = draft.captainId; | |
| } else { | |
| const lock = draft.manualOverrides?.[gw]; | |
| if (lock && lock.ids && lock.ids.length === 15) { | |
| squad = lock.ids.map(id => draft.teamData?.find(p => String(p.ID) === String(id)) || globalPlayers.find(p => String(p.ID) === String(id))).filter(Boolean); | |
| capId = lock.cap; | |
| if (squad.length !== 15) { | |
| const opt = getValidLayout(draft.teamData, gw); | |
| if (opt) { squad = opt.optimalArray; capId = opt.cap; } | |
| } | |
| } else { | |
| const opt = getValidLayout(draft.teamData, gw); | |
| if (opt) { squad = opt.optimalArray; capId = opt.cap; } | |
| } | |
| } | |
| let gwPts = 0; | |
| if (squad && squad.length === 15) { | |
| squad.slice(0, 11).forEach(p => { | |
| if (!p.isBlank) gwPts += (Number(p[`${gw}_Pts`]) || 0) * (String(p.ID) === String(capId) ? capMult : 1); | |
| }); | |
| let ofIdx = 0; | |
| squad.slice(11, 15).forEach(p => { | |
| if (!p.isBlank) { | |
| if (chip === "bb") gwPts += (Number(p[`${gw}_Pts`]) || 0); | |
| else if (p.Pos === "G") gwPts += (Number(p[`${gw}_Pts`]) || 0) * 0.04; | |
| else { gwPts += (Number(p[`${gw}_Pts`]) || 0) * ([0.17, 0.05, 0.02][ofIdx] || 0.02); ofIdx++; } | |
| } | |
| }); | |
| } | |
| const ftStart = ftAtStartOfGw(gw, availableGWs, baselineFt, draft.transfersByGw || {}, draft.chipsByGw || {}); | |
| const moves = draft.transfersByGw?.[gw]?.count || 0; | |
| const isChipFree = chip === "wc" || chip === "fh"; | |
| const hits = isChipFree ? 0 : Math.max(0, moves - ftStart); | |
| return { ev: gwPts - (hits * 4), chip, hits, ftStart, moves, isChipFree }; | |
| }; | |
| return ( | |
| <div className="w-full bg-[#0a0f1c] border border-[#2a2d5c] rounded-xl shadow-[0_0_30px_rgba(42,45,92,0.4)] overflow-hidden mb-6"> | |
| <div className="overflow-x-auto custom-scrollbar"> | |
| <table className="w-full text-center font-mono whitespace-nowrap"> | |
| <thead> | |
| <tr className="text-slate-400 text-[9px] uppercase tracking-widest border-b border-[#2a2d5c] bg-[#050811]"> | |
| <th className="py-2 px-4 text-left font-black w-40">Timeline</th> | |
| {horizonGWs.map(gw => ( | |
| <th key={gw} className="py-2 px-3 border-l border-[#1a1c3a]">GW{gw}</th> | |
| ))} | |
| <th className="py-2 px-4 text-emerald-400 font-black border-l border-[#2a2d5c] w-24 shadow-[inset_10px_0_20px_rgba(0,0,0,0.2)]">Total EV</th> | |
| </tr> | |
| </thead> | |
| <tbody className="divide-y divide-[#1a1c3a]"> | |
| {drafts.map((draft) => { | |
| const isActive = draft.id === activeDraftId; | |
| const rowData = horizonGWs.map(gw => getDraftGwState(draft, gw)); | |
| const totalEv = rowData.reduce((sum, d) => sum + d.ev, 0); | |
| return ( | |
| <tr | |
| key={draft.id} | |
| onClick={() => setActiveDraftId(draft.id)} | |
| className={`transition-all cursor-pointer group ${isActive ? "bg-[#1e2247]/60" : "bg-[#0a0f1c] hover:bg-[#151833]"}`} | |
| > | |
| <td className="py-2.5 px-4 border-r border-[#1a1c3a]"> | |
| <div className="flex items-center justify-between w-32"> | |
| <div className={`font-bold text-[11px] truncate transition-colors ${isActive ? "text-white" : "text-slate-400 group-hover:text-slate-200"}`}> | |
| {draft.name} | |
| </div> | |
| {/* FIX: Native Flexbox Delete Button (always clickable, visually clean) */} | |
| {drafts.length > 1 && ( | |
| <button | |
| onClick={(e) => handleDeleteDraft(e, draft.id)} | |
| className="p-1.5 text-slate-500 hover:text-red-400 hover:bg-red-500/10 rounded-md transition-all flex-shrink-0" | |
| title="Delete Timeline" | |
| > | |
| <Trash2 size={12} /> | |
| </button> | |
| )} | |
| </div> | |
| </td> | |
| {rowData.map((d, i) => ( | |
| <td key={i} className="py-2 px-3 border-l border-[#1a1c3a] relative"> | |
| <div className={`text-[15px] font-black tracking-tight drop-shadow-md ${isActive ? (d.ev > 55 ? 'text-[#818cf8]' : 'text-indigo-200') : 'text-slate-500'}`}> | |
| {d.ev.toFixed(1)} | |
| </div> | |
| <div className="flex justify-center items-center gap-1.5 mt-1"> | |
| <span className="text-[8px] font-black uppercase text-[#c084fc] w-3 text-left">{d.chip || ""}</span> | |
| <span className={`text-[8.5px] font-bold flex items-center gap-1.5 ${isActive ? 'text-indigo-300' : 'text-slate-600'}`}> | |
| <span>{d.hits > 0 ? `-${d.hits*4}` : "-"}</span> | |
| <span>{d.isChipFree ? `${d.moves}/∞` : `${d.moves}/${d.ftStart}`}</span> | |
| </span> | |
| </div> | |
| </td> | |
| ))} | |
| <td className={`py-2 px-4 border-l border-[#2a2d5c] shadow-[inset_10px_0_20px_rgba(0,0,0,0.2)] ${isActive ? 'bg-[#050811]/40' : 'bg-[#050811]/80'}`}> | |
| <div className={`text-lg font-black drop-shadow-[0_0_10px_rgba(52,211,153,0.3)] ${isActive ? 'text-emerald-400' : 'text-emerald-700'}`}> | |
| {totalEv.toFixed(1)} | |
| </div> | |
| </td> | |
| </tr> | |
| ); | |
| })} | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| ); | |
| }; |