161 lines
5.2 KiB
TypeScript
161 lines
5.2 KiB
TypeScript
import { Link } from 'react-router-dom'
|
||
import { getTeamDisplayName } from '../lib/match'
|
||
import type { LoadStatus, RoundGroup } from '../types'
|
||
|
||
type TeamSelectionPageProps = {
|
||
areaAInput: string
|
||
areaBInput: string
|
||
groups: RoundGroup[]
|
||
groupSource: 'idle' | 'db' | 'manual'
|
||
loadMessage: string
|
||
loadStatus: LoadStatus
|
||
selectedGroupId: number | null
|
||
targetDate: string
|
||
onAreaAInputChange: (value: string) => void
|
||
onAreaBInputChange: (value: string) => void
|
||
onGenerateManualGroups: () => void
|
||
onLoadGroupsFromDb: () => void
|
||
onSelectGroup: (groupId: number) => void
|
||
onTargetDateChange: (value: string) => void
|
||
onUseGroup: (groupId: number) => void
|
||
}
|
||
|
||
export function TeamSelectionPage({
|
||
areaAInput,
|
||
areaBInput,
|
||
groups,
|
||
groupSource,
|
||
loadMessage,
|
||
loadStatus,
|
||
selectedGroupId,
|
||
targetDate,
|
||
onAreaAInputChange,
|
||
onAreaBInputChange,
|
||
onGenerateManualGroups,
|
||
onLoadGroupsFromDb,
|
||
onSelectGroup,
|
||
onTargetDateChange,
|
||
onUseGroup,
|
||
}: TeamSelectionPageProps) {
|
||
const hasGroups = groups.length > 0
|
||
const showInlineStatus = loadStatus !== 'idle' && loadStatus !== 'loaded' && Boolean(loadMessage)
|
||
const sourceLabel =
|
||
groupSource === 'db' ? '資料庫載入' : groupSource === 'manual' ? '手動產生' : '尚未建立'
|
||
|
||
return (
|
||
<section className="page-grid">
|
||
{loadStatus === 'loaded' && loadMessage ? (
|
||
<div className="floating-status-bubble" role="status" aria-live="polite">
|
||
{loadMessage}
|
||
</div>
|
||
) : null}
|
||
|
||
<article className="panel">
|
||
<div className="selection-shell">
|
||
<div className="selection-toolbar">
|
||
<label className="field">
|
||
<span>指定日期</span>
|
||
<input
|
||
type="date"
|
||
value={targetDate}
|
||
onChange={(event) => onTargetDateChange(event.target.value)}
|
||
/>
|
||
</label>
|
||
|
||
<div className="button-stack">
|
||
<button className="primary-button" type="button" onClick={onLoadGroupsFromDb}>
|
||
從資料庫讀取
|
||
</button>
|
||
<button className="secondary-button" type="button" onClick={onGenerateManualGroups}>
|
||
手動產生分組
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
{showInlineStatus ? (
|
||
<div className={`status-banner status-banner-${loadStatus}`}>{loadMessage}</div>
|
||
) : null}
|
||
|
||
<div className="double-grid">
|
||
<label className="field">
|
||
<span>A 區名單</span>
|
||
<textarea
|
||
placeholder="每行一位球員"
|
||
value={areaAInput}
|
||
onChange={(event) => onAreaAInputChange(event.target.value)}
|
||
/>
|
||
</label>
|
||
|
||
<label className="field">
|
||
<span>B 區名單</span>
|
||
<textarea
|
||
placeholder="每行一位球員"
|
||
value={areaBInput}
|
||
onChange={(event) => onAreaBInputChange(event.target.value)}
|
||
/>
|
||
</label>
|
||
</div>
|
||
|
||
<div className="selection-hint">
|
||
<span>分組來源:{sourceLabel}</span>
|
||
</div>
|
||
</div>
|
||
</article>
|
||
|
||
<article className="panel">
|
||
<div className="group-head">
|
||
<div>
|
||
<p className="panel-kicker">Step 2</p>
|
||
<h2>選擇要上場的組別</h2>
|
||
</div>
|
||
{selectedGroupId ? <span className="winner-badge">目前第 {selectedGroupId} 組</span> : null}
|
||
</div>
|
||
|
||
<div className="group-board">
|
||
{hasGroups ? (
|
||
groups.map((group) => (
|
||
<article
|
||
key={group.id}
|
||
className={`group-card ${selectedGroupId === group.id ? 'group-card-active' : ''}`}
|
||
>
|
||
<div className="group-head">
|
||
<div>
|
||
<p className="panel-kicker">第 {group.id} 組</p>
|
||
<h3>本組對戰名單</h3>
|
||
</div>
|
||
<div className="group-actions">
|
||
<button
|
||
className="secondary-button"
|
||
type="button"
|
||
onClick={() => onSelectGroup(group.id)}
|
||
>
|
||
選擇這組
|
||
</button>
|
||
<Link className="primary-button inline-link" to="/scoreboard" onClick={() => onUseGroup(group.id)}>
|
||
進入記分板
|
||
</Link>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="team-stage-grid">
|
||
{group.teams.map((team) => (
|
||
<article key={`${group.id}-${team.id}`} className="team-stage-card">
|
||
<span className="team-index">隊伍 {team.id}</span>
|
||
<div className="team-name">{getTeamDisplayName(team)}</div>
|
||
</article>
|
||
))}
|
||
</div>
|
||
</article>
|
||
))
|
||
) : (
|
||
<div className="empty-state">
|
||
<h3>目前還沒有分組</h3>
|
||
<p>先從資料庫讀取指定日期,或輸入 A、B 區名單後手動建立。</p>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</article>
|
||
</section>
|
||
)
|
||
}
|