feat: add score prediction and clear actions to BattleTable

- Add "Clear Current Round" and "Clear All Groups" buttons to reset specific or entire match results.
- Implement a summary Table showing current scores, predicted scores after the latest round, and score differentials.
- Import necessary Ant Design components (Button, Table) and icons (ClearOutlined).
- Refactor `buildMatchGroupTable` to return full arrays instead of filtering out falsy values.
- Disable row hover effect in GroupMember table for cleaner appearance.
This commit is contained in:
kyuuseiryuu 2026-03-11 00:48:44 +09:00
parent bb4ca8ae40
commit 56159de837
2 changed files with 57 additions and 3 deletions

View File

@ -1,9 +1,10 @@
import type { BasePlayer } from "../types";
import { useCallback, useEffect, useMemo, useState } from "react";
import { calculate, getRoundTable } from "../utils/common";
import { Card, Divider, Flex, Space } from "antd";
import { Button, Card, Divider, Flex, Space, Table } from "antd";
import styled from "styled-components";
import { cloneDeep } from "lodash";
import { ClearOutlined } from "@ant-design/icons";
const StyledContainer = styled(Flex)`
.winer {
@ -67,7 +68,7 @@ function BattleTable({
});
return cloneDeep([left, right]);
});
return roundTable.filter(Boolean);
return roundTable;
}, []);
const [matchGroupTable, setMatchGroupTable] = useState<ReturnType<typeof buildMatchGroupTable>>([]);
@ -82,6 +83,29 @@ function BattleTable({
}
setMatchGroupTable(buildMatchGroupTable(playerList));
}, []);
const handleClear = useCallback((left?: (BasePlayer | null | undefined)[], right?: (BasePlayer | null | undefined)[]) => {
left?.forEach((l, i) => {
const r = right?.[i];
const key = `${l?.uid}-${r?.uid}`;
resultMap.delete(key);
});
setMatchGroupTable(buildMatchGroupTable(playerList));
}, [matchGroupTable, playerList]);
const handleClearAll = useCallback(() => {
matchGroupTable.forEach(([left, right]) => {
left?.forEach((l, i) => {
const r = right?.[i];
const key = `${l?.uid}-${r?.uid}`;
resultMap.delete(key);
});
});
setMatchGroupTable(buildMatchGroupTable(playerList));
}, [matchGroupTable, playerList]);
const scoreDiff = useMemo(() => {
const [left = [], right = []] = matchGroupTable?.[matchGroupTable.length - 1] ?? [];
const newScoreMap = Object.fromEntries([...left, ...right].map(e => [e?.uid, e?.score]));
return newScoreMap;
}, [playerList, matchGroupTable]);
useEffect(() => {
const roundTable = buildMatchGroupTable(playerList);
setMatchGroupTable(roundTable);
@ -91,7 +115,12 @@ function BattleTable({
<StyledContainer vertical gap={12} justify="center" align="center">
{matchGroupTable.map(([left, right], i) => {
return (
<Card size="small" key={`round-${i}`} title={`Round: ${i + 1}`}>
<Card
size="small"
key={`round-${i}`}
title={`Round: ${i + 1}`}
extra={<Button type="link" onClick={() => handleClear(left, right)} icon={<ClearOutlined />}></Button>}
>
{left?.map((l, i) => {
const r = right?.[i];
const key = `${l?.uid}-${r?.uid}`;
@ -115,6 +144,30 @@ function BattleTable({
</Card>
);
})}
<Table
title={() => (
<Flex align="center" justify="space-between">
<span></span>
<Button type="link" icon={<ClearOutlined />} onClick={handleClearAll}></Button>
</Flex>
)}
showHeader={false}
pagination={false}
rowHoverable={false}
size="small"
dataSource={playerList}
rowKey={e => e.uid}
style={{ minWidth: 300 }}
columns={[
{ dataIndex: 'name' },
{ dataIndex: 'score' },
{ dataIndex: 'uid', render: (uid, { score }) => scoreDiff[uid] || score },
{ dataIndex: 'uid', align: 'right', render: (uid, { score }) => {
const diff = +(scoreDiff[uid] || score) - +score;
return diff > 0 ? `+${diff}` : diff;
} },
]}
/>
</StyledContainer>
);
}

View File

@ -30,6 +30,7 @@ export const GroupMember: React.FC<Props> = props => {
rowKey={e => `${e.id}-${e.name}-${e.score}`}
showHeader={false}
pagination={false}
rowHoverable={false}
dataSource={props.players}
columns={[
{ dataIndex: '_', render: (_, __, i) => `(${i + 1})` },