feat(14-1): show sum of foul points per player per round in log table
- Table now groups turns into rounds (Aufnahmen) and displays sum of all foul points (including penalties) for each player in each round - Improves clarity and accuracy of move log for 14/1 Refs #26
This commit is contained in:
@@ -295,3 +295,43 @@
|
||||
.log-entry:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.game-log-table-container {
|
||||
margin: 2.5rem auto 0 auto;
|
||||
max-width: 100vw;
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
background: #181818;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 2px 16px rgba(0,0,0,0.12);
|
||||
padding: 1.5rem 1rem;
|
||||
}
|
||||
.game-log-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 1.05rem;
|
||||
background: #222;
|
||||
color: #fff;
|
||||
}
|
||||
.game-log-table th, .game-log-table td {
|
||||
border: 1px solid #444;
|
||||
padding: 0.5rem 0.7rem;
|
||||
text-align: center;
|
||||
}
|
||||
.game-log-table th {
|
||||
background: #333;
|
||||
font-weight: 700;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
.game-log-table tr:nth-child(even) td {
|
||||
background: #232323;
|
||||
}
|
||||
.game-log-table tr:nth-child(odd) td {
|
||||
background: #181818;
|
||||
}
|
||||
.game-log-table .log-player-col {
|
||||
background: #222;
|
||||
color: #ff9800;
|
||||
font-size: 1.15rem;
|
||||
border-bottom: 2px solid #ff9800;
|
||||
}
|
||||
@@ -21,23 +21,80 @@ const StartingPlayerModal = ({ players, onSelect, onCancel }) => (
|
||||
</div>
|
||||
);
|
||||
|
||||
const GameLog = ({ log }) => {
|
||||
if (!log || log.length === 0) {
|
||||
return null;
|
||||
const GameLogTable = ({ log, players }) => {
|
||||
if (!log || log.length === 0) return null;
|
||||
// Only turn and foul entries
|
||||
const turnEntries = log.filter(e => e.type === 'turn');
|
||||
const foulEntries = log.filter(e => e.type === 'foul');
|
||||
// Group into rounds (Aufnahmen): each round = one turn per player, in order
|
||||
const rounds = [];
|
||||
for (let i = 0; i < turnEntries.length; i += players.length) {
|
||||
rounds.push(turnEntries.slice(i, i + players.length));
|
||||
}
|
||||
// Helper: for each player/round, sum fouls between previous and current turn
|
||||
function getFoulSum(playerName, turnIdx, turnEntry) {
|
||||
// Find previous turn index for this player
|
||||
let prevTurnIdx = -1;
|
||||
for (let i = turnIdx - 1; i >= 0; --i) {
|
||||
if (turnEntries[i].player === playerName) {
|
||||
prevTurnIdx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Find fouls for this player between prevTurnIdx and turnIdx
|
||||
let fouls = foulEntries.filter(f => {
|
||||
// Find index of this foul in log
|
||||
const foulLogIdx = log.indexOf(f);
|
||||
// Find log index of previous turn and current turn
|
||||
const prevTurnLogIdx = prevTurnIdx >= 0 ? log.indexOf(turnEntries[prevTurnIdx]) : -1;
|
||||
const currTurnLogIdx = log.indexOf(turnEntry);
|
||||
return f.player === playerName && foulLogIdx > prevTurnLogIdx && foulLogIdx < currTurnLogIdx;
|
||||
});
|
||||
// Sum totalDeduction for all fouls
|
||||
return fouls.reduce((sum, f) => sum + (f.totalDeduction || 0), 0);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles['game-log']}>
|
||||
<h4 className={styles['log-title']}>Game Log</h4>
|
||||
<ul className={styles['log-list']}>
|
||||
{log.slice().reverse().map((entry, index) => (
|
||||
<li key={index} className={styles['log-entry']}>
|
||||
{entry.type === 'rerack' && `Re-Rack (+${entry.ballsAdded} balls).`}
|
||||
{entry.foul && `Foul by ${entry.player}: ${entry.foul} (${entry.totalDeduction} pts).`}
|
||||
{entry.ballsPotted !== undefined && `${entry.player}: ${entry.ballsPotted} balls potted. Score: ${entry.newScore}`}
|
||||
</li>
|
||||
<div className={styles['game-log-table-container']}>
|
||||
<h4 className={styles['log-title']}>Aufnahmen</h4>
|
||||
<table className={styles['game-log-table']}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Aufnahme</th>
|
||||
{players.map((p, idx) => (
|
||||
<th key={p.name} colSpan={3} className={styles['log-player-col']}>{p.name}</th>
|
||||
))}
|
||||
</ul>
|
||||
</tr>
|
||||
<tr>
|
||||
<th></th>
|
||||
{players.map((p, idx) => [
|
||||
<th key={p.name + '-balls'}>Bälle</th>,
|
||||
<th key={p.name + '-foul'}>Foul</th>,
|
||||
<th key={p.name + '-score'}>Score</th>
|
||||
])}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{rounds.map((round, roundIdx) => (
|
||||
<tr key={roundIdx}>
|
||||
<td>{roundIdx + 1}</td>
|
||||
{players.map((p, idx) => {
|
||||
const entry = round.find(e => e.player === p.name);
|
||||
if (entry) {
|
||||
const turnIdx = turnEntries.indexOf(entry);
|
||||
const foulSum = getFoulSum(p.name, turnIdx, entry);
|
||||
return [
|
||||
<td key={p.name + '-balls'}>{entry.ballsPotted}</td>,
|
||||
<td key={p.name + '-foul'}>{foulSum > 0 ? foulSum : ''}</td>,
|
||||
<td key={p.name + '-score'}>{entry.newScore}</td>
|
||||
];
|
||||
} else {
|
||||
return [<td key={p.name + '-balls'}></td>,<td key={p.name + '-foul'}></td>,<td key={p.name + '-score'}></td>];
|
||||
}
|
||||
})}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -206,7 +263,7 @@ const GameDetail141 = ({ game, onUpdate, onUndo, onForfeit, onBack }) => {
|
||||
<button onClick={() => handleFoul('break')} className={styles['foul-btn']}>Break Foul (-2)</button>
|
||||
</div>
|
||||
|
||||
<GameLog log={game.log} />
|
||||
<GameLogTable log={game.log} players={game.players} />
|
||||
|
||||
<div className={styles['game-detail-controls']}>
|
||||
<button className="btn" onClick={onUndo} disabled={!game.undoStack || game.undoStack.length === 0}>Undo</button>
|
||||
|
||||
Reference in New Issue
Block a user