import { useEffect, useState } from 'react' import './App.css' type Participant = { id: string name: string isPlaceholder: boolean } type Team = { id: number playerA: Participant playerB: Participant } type RoundResult = { id: number teams: Team[] } type SaveState = 'idle' | 'saving' | 'saved' | 'error' const STORAGE_KEYS = { areaA: 'badminton-match-hub::area-a', areaB: 'badminton-match-hub::area-b', } as const const PLACEHOLDER_NAME = '那個' const TOTAL_ROUNDS = 3 const defaultAreaA = ['建喵', '柏威', '景涵', 'Gary', '昱翔'] const defaultAreaB = ['小念', '玟瑄', 'RuRu', '肉肉', '蓉蓉'] function App() { const [areaAInput, setAreaAInput] = useState(() => loadRoster(STORAGE_KEYS.areaA, defaultAreaA), ) const [areaBInput, setAreaBInput] = useState(() => loadRoster(STORAGE_KEYS.areaB, defaultAreaB), ) const [targetDate, setTargetDate] = useState(() => formatDateInputValue()) const [results, setResults] = useState([]) const [error, setError] = useState('') const [saveState, setSaveState] = useState('idle') const [saveMessage, setSaveMessage] = useState('') useEffect(() => { window.localStorage.setItem(STORAGE_KEYS.areaA, areaAInput) }, [areaAInput]) useEffect(() => { window.localStorage.setItem(STORAGE_KEYS.areaB, areaBInput) }, [areaBInput]) const parsedAreaA = parseRoster(areaAInput) const parsedAreaB = parseRoster(areaBInput) const teamCount = Math.max(parsedAreaA.length, parsedAreaB.length) const placeholderCountA = Math.max(0, teamCount - parsedAreaA.length) const placeholderCountB = Math.max(0, teamCount - parsedAreaB.length) function generateGroups() { if (parsedAreaA.length === 0 || parsedAreaB.length === 0) { setResults([]) setError('A 區與 B 區都至少要輸入 1 位成員。') setSaveState('idle') setSaveMessage('') return } const prepared = prepareParticipants(parsedAreaA, parsedAreaB) const nextResults = Array.from({ length: TOTAL_ROUNDS }, (_, index) => ({ id: index + 1, teams: createRoundTeams(prepared.areaA, prepared.areaB, index), })) setResults(nextResults) setError('') setSaveState('idle') setSaveMessage('已產生配對,請按「上傳資料」。') } async function uploadResults() { if (results.length === 0) { setSaveState('error') setSaveMessage('請先產生配對結果,再上傳資料。') return } if (!targetDate) { setSaveState('error') setSaveMessage('請先選擇目標日期。') return } setSaveState('saving') setSaveMessage('上傳資料到資料庫中...') try { await saveMatchResults(convertDateToKey(targetDate), parsedAreaA, parsedAreaB, results) setSaveState('saved') setSaveMessage(`已同步到資料庫:${convertDateToKey(targetDate)}`) } catch (saveError) { setSaveState('error') setSaveMessage( saveError instanceof Error ? saveError.message : '資料庫儲存失敗,請稍後再試。', ) } } function resetDemo() { setAreaAInput(defaultAreaA.join('\n')) setAreaBInput(defaultAreaB.join('\n')) setResults([]) setError('') setSaveState('idle') setSaveMessage('') } return (

羽毛球隊伍配對

羽毛球分組配對器

輸入 A 區與 B 區名單後,系統會產生 3 組完整配對。 每一隊都是 1 位 A 區成員搭配 1 位 B 區成員; 若某一區人數不足,會自動補入「那個」。

{saveMessage ? (

{saveMessage}

) : null}
A 區人數 {parsedAreaA.length}
B 區人數 {parsedAreaB.length}

摘要

每組共 {teamCount} 隊,A 區補位 {placeholderCountA} 人,B 區補位 {placeholderCountB} 人。

名單輸入

輸入 A 區與 B 區名單