fpl-solver / frontend /src /components /DraftsComparisonTable.jsx
AnayShukla's picture
Clean Production Release
f7cecf3
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>
);
};