/* ========================================================================== PREMIUM RANDOM GROUP SELECTOR - APPLICATION LOGIC ========================================================================== */ document.addEventListener('DOMContentLoaded', () => { // ------------------------------------------------------------------------ // State Variables // ------------------------------------------------------------------------ let allMembers = []; let remainingMembers = []; let completedGroups = []; let isDrawing = false; // Member Table Array State let memberTableData = []; // Custom Groups State let customGroups = [ { id: 1, name: '그룹 1', count: 5 }, { id: 2, name: '그룹 2', count: 4 }, { id: 3, name: '그룹 3', count: 3 } ]; let customIdCounter = 4; // ------------------------------------------------------------------------ // DOM Elements // ------------------------------------------------------------------------ const settingsModal = document.getElementById('settings-modal'); const btnOpenModal = document.getElementById('btn-open-modal'); const btnOpenModalSub = document.getElementById('btn-open-modal-sub'); const btnCloseModal = document.getElementById('btn-close-modal'); const btnSaveModal = document.getElementById('btn-save-modal'); const modalTabBtns = document.querySelectorAll('.modal-tab-btn'); const tabPanes = document.querySelectorAll('.tab-pane'); const currentModeIndicator = document.getElementById('current-mode-indicator'); const btnModeTable = document.getElementById('btn-mode-table'); const btnModeText = document.getElementById('btn-mode-text'); const tableViewContainer = document.getElementById('table-view-container'); const textViewContainer = document.getElementById('text-view-container'); const memberTableBody = document.getElementById('member-table-body'); const tableCountBadge = document.getElementById('table-count-badge'); const btnAddRow = document.getElementById('btn-add-row'); const btnClearTable = document.getElementById('btn-clear-table'); const memberInput = document.getElementById('member-input'); const btnPreset49num = document.getElementById('btn-preset-49num'); const btnPreset49names = document.getElementById('btn-preset-49names'); const statTotal = document.getElementById('stat-total'); const statRemaining = document.getElementById('stat-remaining'); const statGroups = document.getElementById('stat-groups'); const modePerGroup = document.getElementById('mode-per-group'); const modeTotalGroups = document.getElementById('mode-total-groups'); const modeCustomGroups = document.getElementById('mode-custom-groups'); const uniformModeControls = document.getElementById('uniform-mode-controls'); const modeSizeLabel = document.getElementById('mode-size-label'); const groupSizeVal = document.getElementById('group-size-val'); const btnSizeMinus = document.getElementById('btn-size-minus'); const btnSizePlus = document.getElementById('btn-size-plus'); const customBuilderContainer = document.getElementById('custom-builder-container'); const customGroupsList = document.getElementById('custom-groups-list'); const btnAddCustomGroup = document.getElementById('btn-add-custom-group'); const customTotalBadge = document.getElementById('custom-total-badge'); const toggleSound = document.getElementById('toggle-sound'); const btnDrawGroup = document.getElementById('btn-draw-group'); const btnDrawAll = document.getElementById('btn-draw-all'); const drawBtnText = document.getElementById('draw-btn-text'); const drawingGroupTitle = document.getElementById('drawing-group-title'); const drawingStatusText = document.getElementById('drawing-status-text'); const drawingCardsContainer = document.getElementById('drawing-cards-container'); const groupsContainer = document.getElementById('groups-container'); const emptyResults = document.getElementById('empty-results'); const btnCopyResults = document.getElementById('btn-copy-results'); const btnResetAll = document.getElementById('btn-reset-all'); // ------------------------------------------------------------------------ // Sample Data (49 Names) // ------------------------------------------------------------------------ const SAMPLE_49_NAMES = [ '김민준', '이서준', '박도윤', '최예준', '정시우', '강하준', '조주원', '윤지호', '장지후', '임준서', '한건우', '오우진', '서선우', '신연우', '권유준', '황은우', '안민재', '송현우', '류유찬', '홍지훈', '고지석', '문이준', '양정우', '손유진', '배승현', '백승민', '허시안', '유지원', '남지원', '심현준', '노이도', '하도현', '곽태경', '성하진', '차윤성', '주원준', '우재원', '구지한', '신유현', '임서진', '민서안', '진율', '지성현', '엄태윤', '채하민', '원도윤', '천서우', '방지호', '공유준' ]; // ------------------------------------------------------------------------ // Member Table Logic & Synchronization // ------------------------------------------------------------------------ // Toggle Table vs Text View Mode btnModeTable.addEventListener('click', () => { syncTextToTableData(); btnModeTable.classList.add('active'); btnModeText.classList.remove('active'); tableViewContainer.classList.remove('hidden'); textViewContainer.classList.add('hidden'); renderMemberTable(); }); btnModeText.addEventListener('click', () => { syncTableDataToText(); btnModeText.classList.add('active'); btnModeTable.classList.remove('active'); textViewContainer.classList.remove('hidden'); tableViewContainer.classList.add('hidden'); }); function renderMemberTable() { memberTableBody.innerHTML = ''; tableCountBadge.textContent = `총 ${memberTableData.length}명`; memberTableData.forEach((name, idx) => { const tr = document.createElement('tr'); const tdIdx = document.createElement('td'); tdIdx.className = 'table-idx'; tdIdx.textContent = idx + 1; const tdName = document.createElement('td'); const input = document.createElement('input'); input.type = 'text'; input.className = 'table-row-input'; input.value = name; input.addEventListener('change', (e) => { memberTableData[idx] = e.target.value.trim(); syncTableDataToText(); }); tdName.appendChild(input); const tdAction = document.createElement('td'); tdAction.style.textAlign = 'center'; const btnRemove = document.createElement('button'); btnRemove.type = 'button'; btnRemove.className = 'btn-remove-row'; btnRemove.textContent = '✕'; btnRemove.title = '삭제'; btnRemove.addEventListener('click', () => { memberTableData.splice(idx, 1); renderMemberTable(); syncTableDataToText(); }); tdAction.appendChild(btnRemove); tr.appendChild(tdIdx); tr.appendChild(tdName); tr.appendChild(tdAction); memberTableBody.appendChild(tr); }); } function syncTextToTableData() { const rawText = memberInput.value; const list = rawText .split(/[\n,]/) .map(name => name.trim()) .filter(name => name.length > 0); memberTableData = [...list]; } function syncTableDataToText() { const cleaned = memberTableData.filter(n => n && n.trim().length > 0); memberInput.value = cleaned.join('\n'); } // Add Row in Table btnAddRow.addEventListener('click', () => { memberTableData.push(`참가자 ${memberTableData.length + 1}`); renderMemberTable(); syncTableDataToText(); const wrapper = document.querySelector('.member-table-wrapper'); if (wrapper) wrapper.scrollTop = wrapper.scrollHeight; }); // Clear Table btnClearTable.addEventListener('click', () => { memberTableData = []; memberInput.value = ''; renderMemberTable(); }); // Presets in Modal btnPreset49num.addEventListener('click', () => { const numbers = Array.from({ length: 49 }, (_, i) => `${i + 1}번`); memberTableData = [...numbers]; syncTableDataToText(); renderMemberTable(); }); btnPreset49names.addEventListener('click', () => { memberTableData = [...SAMPLE_49_NAMES]; syncTableDataToText(); renderMemberTable(); }); // ------------------------------------------------------------------------ // Modal Control Logic // ------------------------------------------------------------------------ function openModal() { settingsModal.classList.remove('hidden'); } // Save/Apply btnSaveModal.addEventListener('click', () => { if (!textViewContainer.classList.contains('hidden')) { syncTextToTableData(); } else { syncTableDataToText(); } loadMembersFromTableOrInput(); updateModeUI(); closeModal(); }); function closeModal() { settingsModal.classList.add('hidden'); } btnOpenModal.addEventListener('click', openModal); if (btnOpenModalSub) btnOpenModalSub.addEventListener('click', openModal); btnCloseModal.addEventListener('click', closeModal); settingsModal.addEventListener('click', (e) => { if (e.target === settingsModal) { closeModal(); } }); document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && !settingsModal.classList.contains('hidden')) { closeModal(); } }); // Modal Tabs modalTabBtns.forEach(btn => { btn.addEventListener('click', () => { const targetTab = btn.getAttribute('data-tab'); modalTabBtns.forEach(b => b.classList.remove('active')); tabPanes.forEach(p => p.classList.remove('active')); btn.classList.add('active'); document.getElementById(targetTab).classList.add('active'); }); }); // ------------------------------------------------------------------------ // Mode Toggle & Settings // ------------------------------------------------------------------------ modePerGroup.addEventListener('change', updateModeUI); modeTotalGroups.addEventListener('change', updateModeUI); modeCustomGroups.addEventListener('change', updateModeUI); function updateModeUI() { let modeText = ''; if (modeCustomGroups.checked) { uniformModeControls.classList.add('hidden'); customBuilderContainer.classList.remove('hidden'); modeText = '추첨 방식: 🛠️ 그룹별 명수 개별 지정 (커스텀)'; } else { uniformModeControls.classList.remove('hidden'); customBuilderContainer.classList.add('hidden'); if (modePerGroup.checked) { modeSizeLabel.textContent = '한 그룹당 인원수'; modeText = `추첨 방식: 그룹 당 ${groupSizeVal.value}명`; } else { modeSizeLabel.textContent = '생성할 총 그룹 수'; modeText = `추첨 방식: 총 ${groupSizeVal.value}개 그룹 균등`; } } if (currentModeIndicator) { currentModeIndicator.textContent = modeText; } } // Stepper Buttons btnSizeMinus.addEventListener('click', () => { let val = parseInt(groupSizeVal.value) || 1; if (val > 1) { groupSizeVal.value = val - 1; updateModeUI(); } }); btnSizePlus.addEventListener('click', () => { let val = parseInt(groupSizeVal.value) || 1; if (val < 100) { groupSizeVal.value = val + 1; updateModeUI(); } }); groupSizeVal.addEventListener('change', updateModeUI); // Custom Groups Builder UI btnAddCustomGroup.addEventListener('click', () => { customGroups.push({ id: customIdCounter++, name: `그룹 ${customGroups.length + 1}`, count: 3 }); renderCustomGroupsBuilder(); }); function renderCustomGroupsBuilder() { customGroupsList.innerHTML = ''; customGroups.forEach((cg, idx) => { const row = document.createElement('div'); row.className = 'custom-group-row'; const nameInput = document.createElement('input'); nameInput.type = 'text'; nameInput.className = 'custom-group-name'; nameInput.value = cg.name; nameInput.addEventListener('change', (e) => { cg.name = e.target.value || `그룹 ${idx + 1}`; }); const stepper = document.createElement('div'); stepper.className = 'number-stepper sm-stepper'; stepper.innerHTML = ` `; const inputVal = stepper.querySelector('.input-cg-val'); stepper.querySelector('.btn-cg-minus').addEventListener('click', () => { if (cg.count > 1) { cg.count--; inputVal.value = cg.count; updateCustomTotalBadge(); } }); stepper.querySelector('.btn-cg-plus').addEventListener('click', () => { cg.count++; inputVal.value = cg.count; updateCustomTotalBadge(); }); inputVal.addEventListener('change', (e) => { let v = parseInt(e.target.value) || 1; cg.count = Math.max(1, v); inputVal.value = cg.count; updateCustomTotalBadge(); }); const btnRemove = document.createElement('button'); btnRemove.type = 'button'; btnRemove.className = 'btn-remove-custom-group'; btnRemove.textContent = '✕'; btnRemove.title = '삭제'; btnRemove.addEventListener('click', () => { if (customGroups.length <= 1) { alert('최소 1개 이상의 그룹 설정이 필요합니다.'); return; } customGroups = customGroups.filter(item => item.id !== cg.id); renderCustomGroupsBuilder(); }); row.appendChild(nameInput); row.appendChild(stepper); row.appendChild(btnRemove); customGroupsList.appendChild(row); }); updateCustomTotalBadge(); } function updateCustomTotalBadge() { const totalCount = customGroups.reduce((sum, item) => sum + item.count, 0); customTotalBadge.textContent = `총 설정 인원: ${totalCount}명`; } // Sound Toggle toggleSound.addEventListener('change', (e) => { window.soundEffects.enabled = e.target.checked; }); // Action Buttons btnDrawGroup.addEventListener('click', () => drawNextGroup(false)); btnDrawAll.addEventListener('click', () => drawNextGroup(true)); btnResetAll.addEventListener('click', resetAllData); btnCopyResults.addEventListener('click', copyResultsToClipboard); // ------------------------------------------------------------------------ // Core Functions // ------------------------------------------------------------------------ function loadMembersFromTableOrInput() { const list = memberTableData.filter(name => name && name.trim().length > 0); if (list.length === 0) { const numbers = Array.from({ length: 49 }, (_, i) => `${i + 1}번`); memberTableData = [...numbers]; syncTableDataToText(); renderMemberTable(); allMembers = [...numbers]; remainingMembers = [...numbers]; } else { allMembers = [...list]; remainingMembers = [...list]; } completedGroups = []; updateUIStats(); renderGroups(); } function resetAllData() { allMembers = []; remainingMembers = []; completedGroups = []; isDrawing = false; updateUIStats(); renderGroups(); // Reset Stage to Initial Placeholder drawingGroupTitle.textContent = 'READY'; if (drawingStatusText) drawingStatusText.textContent = '추첨 대기 중'; drawingCardsContainer.className = 'cards-grid grid-size-lg'; drawingCardsContainer.innerHTML = `
상단의 [다음 1개 그룹 추첨] 버튼을 눌러 그룹 편성을 시작하세요.