// 페이지가 완전히 로딩된 후 실행되는 이벤트 리스너입니다. document.addEventListener('DOMContentLoaded', () => { const catSelect = document.getElementById('category_code'); // 카테고리(대분류) 셀렉트 박스 const blocks = document.querySelectorAll('.cat-block'); // 카테고리별로 동적으로 보여질 입력 폼 블록들 const catGroup = document.getElementById('category_group'); // 카테고리 구분(중분류) 셀렉트 박스 const goalBtn = document.getElementById('btn-goal'); // 학습목표 등록 모달창을 여는 통계/목표 버튼 /** * 사용자가 선택한 카테고리에 맞춰 하단 입력 폼 영역을 동적으로 노출하거나 숨기는 함수입니다. */ function applyToggle() { let name = '마이클래스'; // 기본 카테고리는 마이클래스로 설정합니다. // 카테고리 셀렉트가 있을 경우 현재 선택된 옵션의 data-name 속성을 읽어옵니다. if (catSelect) { const opt = catSelect.options[catSelect.selectedIndex]; name = opt && opt.dataset && opt.dataset.name ? opt.dataset.name : name; } // 준비된 폼 블록들(.cat-block)을 순회하며, 카테고리 이름과 일치하는 블록만 보여줍니다. blocks.forEach(b => { const cat = b.getAttribute('data-cat') || ''; const cats = cat.split(','); if (cats.includes(name)) b.classList.remove('hidden'); else b.classList.add('hidden'); }); const isIS = (name === '인사이트'); const isLD = (name === '리더십'); const isContainer = document.getElementById('issue_type_is_container'); const ldContainer = document.getElementById('issue_type_ld_container'); const isSelect = document.getElementById('issue_type_code_is'); const ldSelect = document.getElementById('issue_type_code_ld'); const offerIdContainer = document.getElementById('offer_id_container'); if (isContainer && ldContainer && isSelect && ldSelect) { if (isIS) { isContainer.classList.remove('hidden'); ldContainer.classList.add('hidden'); isSelect.disabled = false; ldSelect.disabled = true; } else if (isLD) { isContainer.classList.add('hidden'); ldContainer.classList.remove('hidden'); isSelect.disabled = true; ldSelect.disabled = false; } } if (offerIdContainer) { if (isIS) { offerIdContainer.classList.remove('hidden'); } else { offerIdContainer.classList.add('hidden'); } } const sortOrderContainer = document.getElementById('sort_order_container'); if (sortOrderContainer) { if (['CA10001', 'CA10002', 'CA10003'].includes(catSelect.value)) { sortOrderContainer.style.display = 'block'; } else { sortOrderContainer.style.display = 'none'; } } const memoBtn = document.getElementById('btn-memo-open'); if (memoBtn) { if (catSelect && catSelect.value === 'CA10001') { memoBtn.classList.remove('hidden'); } else { memoBtn.classList.add('hidden'); } } } function toggleImageUpload() { const isBizTrend = (catSelect && catSelect.value === 'CA10006'); const isPart1 = (catGroup && catGroup.value === 'CA200B01'); const imgContainer = document.getElementById('image_upload_container'); if (imgContainer) { if (isBizTrend && isPart1) { imgContainer.classList.remove('hidden'); } else { imgContainer.classList.add('hidden'); document.getElementById('image_name_input').value = ''; document.getElementById('image_name_label').textContent = '대표이미지등록(N)'; } } // 사내도서여부: 대표이미지등록과 동일 조건(비즈트렌드 + 1부)일 때만 표시 const bookYnContainer = document.getElementById('book_yn_container'); if (bookYnContainer) { if (isBizTrend && isPart1) { bookYnContainer.classList.remove('hidden'); } else { bookYnContainer.classList.add('hidden'); const bookYnChk = document.getElementById('book_yn'); if (bookYnChk) bookYnChk.checked = false; } } } window.previewImageUpload = function(input) { const label = document.getElementById('image_name_label'); if (input.files && input.files[0]) { const file = input.files[0]; if (file.size > 4 * 1024 * 1024) { alert("용량이 4MB 이하인 이미지 파일만 등록 가능합니다."); input.value = ''; label.textContent = '대표이미지등록(N)'; return; } if (!file.type.startsWith('image/')) { alert("이미지 파일만 등록 가능합니다."); input.value = ''; label.textContent = '대표이미지등록(N)'; return; } label.textContent = '대표이미지등록(Y)'; } else { label.textContent = '대표이미지등록(N)'; } }; /** * 대분류 카테고리가 변경되었을 때, 해당하는 중분류 값을 서버에서 불러와 셀렉트 박스에 세팅하는 함수입니다. */ async function loadCategoryGroup() { if (!catSelect || !catGroup) return; // 요소가 없으면 실행하지 않습니다. const code = catSelect.value; try { // 서버에서 카테고리 그룹(중분류) API 데이터를 비동기로 통신하여 가져옵니다. const res = await fetch(`../bbs/code_list.php?type=category_group&desc01=${encodeURIComponent(code)}`); const data = await res.json(); // 기존 옵션 초기화 후 기본 '선택' 옵션을 추가합니다. catGroup.innerHTML = ''; const def = document.createElement('option'); def.value = ''; def.textContent = '선택'; catGroup.appendChild(def); // 받아온 데이터 리스트를 옵션 태그로 추가합니다. (data.items || []).forEach(it => { const o = document.createElement('option'); o.value = it.code; o.textContent = it.name; catGroup.appendChild(o); }); } catch (e) { // 오류 발생 시 '선택' 옵션만 노출되도록 초기화합니다. catGroup.innerHTML = ''; } } /** * 특정 기준년도에 등록된 활성 상태의 학습목표 목록을 불러와 학습목표코드 셀렉트 박스에 세팅하는 함수입니다. */ async function loadGoals() { const yearInput = document.querySelector('input[name="base_year"]'); const goalSel = document.getElementById('goal_code'); if (!yearInput || !goalSel) return; // 카테고리가 마이클래스가 아닐 때는 학습목표코드를 조회하지 않습니다. if (catSelect) { const opt = catSelect.options[catSelect.selectedIndex]; const catName = opt && opt.dataset && opt.dataset.name ? opt.dataset.name : ''; if (catName !== '마이클래스') { goalSel.innerHTML = ''; return; } } // 연도 필드가 비어있다면 올해 연도를 기본으로 사용합니다. const year = yearInput.value || new Date().getFullYear(); // 카테고리구분(중분류)을 함께 기준으로 사용합니다. (분기 정보와 매핑) const catGroupSelect = document.getElementById('category_group'); const categoryGroup = catGroupSelect ? (catGroupSelect.value || '') : ''; try { const res = await fetch(`../bbs/code_list.php?type=learning_goal&year=${year}&category_group=${encodeURIComponent(categoryGroup)}`); const data = await res.json(); goalSel.innerHTML = ''; (data.items || []).forEach(it => { const o = document.createElement('option'); o.value = it.code; o.textContent = it.name; goalSel.appendChild(o); }); } catch (e) { goalSel.innerHTML = ''; } } const yearInput = document.querySelector('input[name="base_year"]'); if (yearInput) { yearInput.addEventListener('change', loadGoals); yearInput.addEventListener('blur', loadGoals); } // 카테고리구분이 변경되면 학습목표 코드 리스트도 재조회합니다. if (catGroup) { catGroup.addEventListener('change', () => { loadGoals(); toggleImageUpload(); }); } if (catSelect) { catSelect.addEventListener('change', () => { applyToggle(); loadCategoryGroup().then(() => { loadGoals(); toggleImageUpload(); }); }); applyToggle(); loadCategoryGroup().then(() => { loadGoals(); toggleImageUpload(); }); } const offerIdInput = document.querySelector('input[name="offer_id"]'); const isOfferCheck = document.querySelector('input[name="is_offer"]'); if (offerIdInput && isOfferCheck) { offerIdInput.addEventListener('input', () => { isOfferCheck.checked = offerIdInput.value.trim() !== ''; }); } const editBtns = document.querySelectorAll('.btn-edit'); const modalTitle = document.querySelector('#upload-modal h3'); const modalForm = document.querySelector('#upload-modal form'); const contentIdInput = document.querySelector('input[name="content_id"]'); // 수정 버튼들을 순회하며 이벤트 핸들러를 등록합니다. editBtns.forEach(btn => { btn.addEventListener('click', async () => { // 버튼에 바인딩된 해당 행의 JSON 데이터를 가져옵니다. const row = JSON.parse(btn.getAttribute('data-row')); const contentId = row && row.content_id ? row.content_id : ''; if (!contentId) { alert('콘텐츠 ID가 없습니다.'); return; } // content_id 기준으로 DB에서 최신 데이터 조회 let detail = null; try { const dRes = await fetch(`../bbs/code_list.php?type=content_detail&content_id=${encodeURIComponent(contentId)}`); const dData = await dRes.json(); if (!dData.success || !dData.item) { alert('콘텐츠 조회 실패: ' + (dData.message || '알 수 없는 오류')); return; } detail = dData.item; } catch (e) { alert('콘텐츠 조회 중 오류가 발생했습니다.'); return; } // 모달 제목을 '콘텐츠 수정'으로 변경합니다. if (modalTitle) modalTitle.textContent = '콘텐츠 수정'; // 콘텐츠 등록/수정 모달을 보여줍니다. document.getElementById('upload-modal').classList.remove('hidden'); // 삭제 버튼 보여주기 const deleteBtn = document.getElementById('btn-delete'); if (deleteBtn) deleteBtn.classList.remove('hidden'); // 선택된 행의 카테고리 정보로 폼을 세팅합니다. if (catSelect) { catSelect.value = detail.category_code || row.category_code; catSelect.disabled = true; // 카테고리 수정 불가 (읽기 모드) document.getElementById('category_code_hidden').value = row.category_code; applyToggle(); // 폼 요소를 토글합니다. await loadCategoryGroup(); // 해당하는 중분류 목록을 불러옵니다. if (catGroup) { catGroup.value = detail.category_group || row.category_group || ''; } toggleImageUpload(); // 카테고리 구분이 세팅된 후 다시 노출 제어 실행 } // 동적 노출 폼에 데이터를 바인딩합니다. const catName = catSelect.options[catSelect.selectedIndex]?.dataset.name || ''; if (catName === '마이클래스') { const yearInput = document.querySelector('input[name="base_year"]'); if (yearInput && (detail.base_year || row.base_year)) { yearInput.value = detail.base_year || row.base_year; } await loadGoals(); const goalCodeSelect = document.getElementById('goal_code'); if (goalCodeSelect && (detail.goal_code || row.goal_code)) { goalCodeSelect.value = detail.goal_code || row.goal_code; } } else if (catName === '법정교육') { const yearLaw = document.querySelector('input[name="base_year_law"]'); if (yearLaw && (detail.base_year || row.base_year)) yearLaw.value = detail.base_year || row.base_year; } else if (catName === '비즈트렌드') { const sDateBzt = document.querySelector('.cat-block[data-cat="비즈트렌드"] input[name="start_date_bzt"]'); if (sDateBzt && (detail.start_date || row.start_date)) sDateBzt.value = detail.start_date || row.start_date; // 사내도서여부 체크 상태 복원 (비즈트렌드+1부 조건에서만 표시됨) const bookYnChk = document.getElementById('book_yn'); if (bookYnChk) bookYnChk.checked = ((detail.book_yn ?? row.book_yn) === '1'); } else if (catName === '인사이트' || catName === '리더십') { const isIS = (catName === '인사이트'); const isContainer = document.getElementById('issue_type_is_container'); const ldContainer = document.getElementById('issue_type_ld_container'); const isSelect = document.getElementById('issue_type_code_is'); const ldSelect = document.getElementById('issue_type_code_ld'); const offerIdContainer = document.getElementById('offer_id_container'); if (isIS) { if (isContainer) isContainer.classList.remove('hidden'); if (ldContainer) ldContainer.classList.add('hidden'); if (isSelect) { isSelect.disabled = false; isSelect.value = detail.issue_type_code || ''; } if (ldSelect) ldSelect.disabled = true; if (offerIdContainer) offerIdContainer.classList.remove('hidden'); } else { if (isContainer) isContainer.classList.add('hidden'); if (ldContainer) ldContainer.classList.remove('hidden'); if (isSelect) isSelect.disabled = true; if (ldSelect) { ldSelect.disabled = false; ldSelect.value = detail.issue_type_code || ''; } if (offerIdContainer) offerIdContainer.classList.add('hidden'); } const isOffer = document.querySelector('input[name="is_offer"]'); if (isOffer) isOffer.checked = (detail.is_offer === '1'); const offerId = document.querySelector('input[name="offer_id"]'); if (offerId) offerId.value = detail.offer_id || ''; const desc3In = document.querySelector('textarea[name="description3"]'); if (desc3In) desc3In.value = detail.description3 || row.description3 || ''; } // 콘텐츠 기본 폼 속성들에 값을 채워넣습니다. if (contentIdInput) contentIdInput.value = detail.content_id || row.content_id; const titleIn = document.querySelector('input[name="title"]'); if (titleIn) titleIn.value = detail.title || row.title || ''; const descIn = document.querySelector('textarea[name="description"]'); if (descIn) descIn.value = detail.description || row.description || ''; const desc1In = document.querySelector('input[name="description1"]'); if (desc1In) desc1In.value = detail.description1 || row.description1 || ''; const desc2In = document.querySelector('input[name="description2"]'); if (desc2In) desc2In.value = detail.description2 || row.description2 || ''; const sortOrderIn = document.querySelector('input[name="sort_order"]'); if (sortOrderIn) sortOrderIn.value = detail.sort_order || row.sort_order || ''; const urlIn = document.querySelector('input[name="content_url"]'); if (urlIn) urlIn.value = detail.content_url || row.content_url || ''; // content_tm (영상 길이) 복원 const contentTmIn = document.getElementById('content_tm_input'); if (contentTmIn) { const tm = detail.content_tm || row.content_tm || ''; contentTmIn.value = tm; // 길이 표시 업데이트 updateDurationDisplay(tm ? parseInt(tm) : 0); } const imageLabel = document.getElementById('image_name_label'); if (imageLabel) { if (detail.image_name || row.image_name) { imageLabel.textContent = '대표이미지등록(Y)'; } else { imageLabel.textContent = '대표이미지등록(N)'; } } // Keyword binding using API const kwBtns = document.querySelectorAll('.kw-btn'); const kwCountDisplay = document.getElementById('keyword-count-display'); if (kwBtns) { kwBtns.forEach(btn => { btn.dataset.active = 'false'; const iconWrap = btn.querySelector('.kw-icon'); if (iconWrap) iconWrap.innerHTML = ''; }); try { const res = await fetch(`../bbs/code_list.php?type=content_keywords&content_id=${encodeURIComponent(row.content_id)}`); const data = await res.json(); const kws = data.items || []; kws.forEach(k => { const b = Array.from(kwBtns).find(btn => btn.dataset.code === k); if (b) { b.dataset.active = 'true'; const iconWrap = b.querySelector('.kw-icon'); if (iconWrap) iconWrap.innerHTML = ''; } }); if (kwCountDisplay) kwCountDisplay.textContent = kws.length; updateKeywordInput(); } catch (e) { console.error('Failed to load keywords', e); if (kwCountDisplay) kwCountDisplay.textContent = '0'; updateKeywordInput(); } } }); }); const goalForm = document.getElementById('goal-form'); if (goalForm) { goalForm.addEventListener('submit', async (e) => { e.preventDefault(); const formData = new FormData(goalForm); try { const resp = await fetch(goalForm.action, { method: 'POST', body: formData }); const data = await resp.json(); if (data.success) { alert('저장되었습니다.'); loadGoalGrid(); resetGoalForm(); } else { alert('저장 실패: ' + (data.message || '알 수 없는 오류')); } } catch (err) { alert('오류가 발생했습니다.'); } }); } // 콘텐츠 수정 모달 AJAX Submit (저장 후 모달 유지) const contentForm = document.querySelector('#upload-modal form'); if (contentForm) { contentForm.addEventListener('submit', async (e) => { e.preventDefault(); // 기본 페이지 이동 방지 const formData = new FormData(contentForm); const isNew = !formData.get('content_id'); // content_id가 없으면 새 콘텐츠 try { const resp = await fetch(contentForm.action, { method: 'POST', body: formData }); const data = await resp.json(); if (data.success) { alert('저장되었습니다.'); if (isNew) { // 새 콘텐츠 추가 시 모달 닫기 document.getElementById('upload-modal').classList.add('hidden'); location.reload(); // 페이지 새로고침으로 그리드 업데이트 } // 수정 시 모달 유지 (새로고침 없이) } else { alert('저장 실패: ' + (data.message || '알 수 없는 오류')); } } catch (err) { alert('저장에 실패했습니다.'); } }); } // 삭제 버튼 이벤트 const deleteBtn = document.getElementById('btn-delete'); if (deleteBtn) { deleteBtn.addEventListener('click', async () => { if (!confirm('정말로 이 콘텐츠를 삭제하시겠습니까?')) return; const contentId = document.querySelector('input[name="content_id"]').value; if (!contentId) return; try { const resp = await fetch('../bbs/content_delete.php', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: `content_id=${encodeURIComponent(contentId)}` }); const data = await resp.json(); if (data.success) { alert('삭제되었습니다.'); document.getElementById('upload-modal').classList.add('hidden'); location.reload(); // 페이지 새로고침으로 그리드 업데이트 } else { alert('삭제 실패: ' + (data.message || '알 수 없는 오류')); } } catch (err) { alert('삭제에 실패했습니다.'); } }); } // Keyword Selection Logic const kwBtns = document.querySelectorAll('.kw-btn'); const kwInput = document.getElementById('kw_selected'); // Updated ID for modal hidden input function updateKeywordInput() { const activeKws = Array.from(kwBtns) .filter(btn => btn.dataset.active === 'true') .map(btn => btn.dataset.code); if (kwInput) { kwInput.value = activeKws.join(','); } } kwBtns.forEach(btn => { btn.addEventListener('click', () => { const isActive = btn.dataset.active === 'true'; btn.dataset.active = !isActive; const iconWrap = btn.querySelector('.kw-icon'); if (iconWrap) { iconWrap.innerHTML = !isActive ? '' : ''; } updateKeywordInput(); }); }); // content_url 입력 필드 blur 시 자동 영상 정보 조회 const contentUrlInput = document.getElementById('content_url_input'); if (contentUrlInput) { contentUrlInput.addEventListener('blur', () => { const val = contentUrlInput.value.trim(); if (val) fetchYouTubeInfo(); }); } }); /** * 신규 콘텐츠 등록 모달을 여는 함수입니다. (기존 데이터 초기화) */ function openNewModal() { document.getElementById('upload-modal').classList.remove('hidden'); const modalTitle = document.querySelector('#upload-modal h3'); if (modalTitle) modalTitle.textContent = '새 콘텐츠 등록'; const modalForm = document.querySelector('#upload-modal form'); if (modalForm) modalForm.reset(); const contentIdInput = document.querySelector('input[name="content_id"]'); if (contentIdInput) contentIdInput.value = ''; const imageInput = document.getElementById('image_name_input'); if (imageInput) imageInput.value = ''; const imageLabel = document.getElementById('image_name_label'); if (imageLabel) imageLabel.textContent = '대표이미지등록(N)'; // content_tm 및 YouTube 상태 초기화 const contentTmIn = document.getElementById('content_tm_input'); if (contentTmIn) contentTmIn.value = ''; const ytStatus = document.getElementById('yt-fetch-status'); if (ytStatus) ytStatus.classList.add('hidden'); const durDisplay = document.getElementById('yt-duration-display'); if (durDisplay) durDisplay.classList.add('hidden'); // 삭제 버튼 숨기기 const deleteBtn = document.getElementById('btn-delete'); if (deleteBtn) deleteBtn.classList.add('hidden'); const kwBtns = document.querySelectorAll('.kw-btn'); const kwCountDisplay = document.getElementById('keyword-count-display'); if (kwBtns) { kwBtns.forEach(btn => { btn.dataset.active = 'false'; const iconWrap = btn.querySelector('.kw-icon'); if (iconWrap) iconWrap.innerHTML = ''; }); } if (kwCountDisplay) kwCountDisplay.textContent = '0'; const kwSelected = document.getElementById('kw_selected'); if (kwSelected) kwSelected.value = ''; // 카테고리를 다시 선택 상태로 트리거하여 폼의 동적 노출 상태를 초기화합니다. const catSelect = document.getElementById('category_code'); if (catSelect) { catSelect.disabled = false; // 새 콘텐츠 등록 시에는 카테고리 선택 가능 document.getElementById('category_code_hidden').value = catSelect.value; catSelect.dispatchEvent(new Event('change')); } } /** * 학습목표 등록(및 리스트 조회) 모달을 여는 함수입니다. */ function openGoalModal() { document.getElementById('goal-modal').classList.remove('hidden'); loadGoalGrid(); resetGoalForm(); } /** * 학습목표 모달을 닫는 함수입니다. */ function closeGoalModal() { document.getElementById('goal-modal').classList.add('hidden'); } /** * 학습목표 목록을 Ajax로 불러와 표(Grid) 형식으로 그려주는 함수입니다. */ async function loadGoalGrid() { const yearInput = document.getElementById('goal_search_year'); const quarterInput = document.getElementById('goal_search_quarter'); if (!yearInput) return; const year = yearInput.value || new Date().getFullYear(); const quarter = quarterInput ? quarterInput.value : ''; const gridBody = document.getElementById('goal-grid-body'); if (!gridBody) return; // 데이터를 불러오기 전 로딩 상태 표시 gridBody.innerHTML = '로딩 중...'; try { // API에 기준년도와 분기 데이터를 포함해 호출합니다. const res = await fetch(`../bbs/code_list.php?type=learning_goal_all&year=${year}&quarter=${encodeURIComponent(quarter)}`); const data = await res.json(); gridBody.innerHTML = ''; // 결과 데이터가 없는 경우 if (!data.items || data.items.length === 0) { gridBody.innerHTML = '검색된 학습목표가 없습니다.'; return; } // 결과가 있는 경우 행 단위로 HTML을 생성하여 테이블을 그립니다. data.items.forEach((it, idx) => { const tr = document.createElement('tr'); tr.className = 'hover:bg-teal-50/30 transition'; const isActiveHtml = it.is_active === '1' ? '' : '-'; tr.innerHTML = ` ${idx + 1} ${escapeHtml(it.quarter_name || it.quarter || '-')} ${escapeHtml(it.goal_no_name || it.goal_no || '-')} ${it.goal_code} ${escapeHtml(it.title || '')} ${isActiveHtml} ${escapeHtml(it.remarks || '')} `; gridBody.appendChild(tr); }); } catch (e) { gridBody.innerHTML = '오류가 발생했습니다.'; } } function editGoalRow(row) { document.getElementById('goal_form_code').value = row.goal_code; document.getElementById('goal_form_title').value = row.title || ''; const _titleLen = document.getElementById('goal_title_len'); if (_titleLen) _titleLen.textContent = (row.title || '').length; document.getElementById('goal_form_quarter').value = row.quarter || ''; const goalNoSel = document.getElementById('goal_form_goal_no'); if (goalNoSel) goalNoSel.value = row.goal_no || ''; document.getElementById('goal_form_remarks').value = row.remarks || ''; document.getElementById('goal_form_active').checked = (row.is_active === '1'); const sort = document.getElementById('goal_form_sort'); if (sort) sort.value = row.sort_order || ''; const searchYear = document.getElementById('goal_search_year').value; document.getElementById('goal_form_year').value = searchYear; document.getElementById('btn-goal-delete').classList.remove('hidden'); document.getElementById('btn-goal-new').classList.remove('hidden'); } function resetGoalForm() { document.getElementById('goal_form_code').value = ''; document.getElementById('goal_form_title').value = ''; const _titleLenR = document.getElementById('goal_title_len'); if (_titleLenR) _titleLenR.textContent = '0'; document.getElementById('goal_form_quarter').value = ''; const goalNoSel = document.getElementById('goal_form_goal_no'); if (goalNoSel) goalNoSel.value = ''; document.getElementById('goal_form_remarks').value = ''; document.getElementById('goal_form_active').checked = true; const sort = document.getElementById('goal_form_sort'); if (sort) sort.value = ''; const searchYear = document.getElementById('goal_search_year').value; document.getElementById('goal_form_year').value = searchYear; document.getElementById('btn-goal-delete').classList.add('hidden'); document.getElementById('btn-goal-new').classList.add('hidden'); } async function deleteGoal() { const code = document.getElementById('goal_form_code').value; if (!code) return; if (!confirm('정말 삭제하시겠습니까?')) return; const form = document.getElementById('goal-form'); const formData = new FormData(form); try { await fetch('../bbs/goal_delete.php', { method: 'POST', body: formData }); alert('삭제되었습니다.'); loadGoalGrid(); resetGoalForm(); } catch (err) { alert('오류가 발생했습니다.'); } } function escapeHtml(unsafe) { return (unsafe || '').toString() .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } function openKeywordModal() { const contentIdInput = document.querySelector('input[name="content_id"]'); if (!contentIdInput || !contentIdInput.value) { alert("새 콘텐츠를 저장 후 키워드를 등록해주세요"); return; } const kwContentId = document.getElementById('kw_content_id'); if (kwContentId) kwContentId.value = contentIdInput.value; // Update hidden input before opening const kwInput = document.getElementById('kw_selected'); const kwBtns = document.querySelectorAll('.kw-btn'); if (kwInput) { kwInput.value = Array.from(kwBtns).filter(btn => btn.dataset.active === 'true').map(btn => btn.dataset.code).join(','); } document.getElementById('keyword-modal').classList.remove('hidden'); } function closeKeywordModal() { document.getElementById('keyword-modal').classList.add('hidden'); } /** * 키워드 모달에서 저장 버튼 클릭 시 호출되는 함수입니다. * 선택된 키워드 코드 목록을 AJAX로 서버에 저장합니다. */ async function saveKeywords(event) { if (event) event.preventDefault(); const form = document.getElementById('keyword-form'); if (!form) return; // 현재 활성화된 키워드 코드를 수집하여 hidden input에 세팅합니다. const kwBtns = document.querySelectorAll('.kw-btn'); const kwInput = document.getElementById('kw_selected'); if (kwInput) { kwInput.value = Array.from(kwBtns) .filter(btn => btn.dataset.active === 'true') .map(btn => btn.dataset.code) .join(','); } const formData = new FormData(form); try { const res = await fetch('../bbs/keyword_save.php', { method: 'POST', body: formData }); const data = await res.json(); if (data.success) { // 메인 모달의 키워드 카운트 표시를 업데이트합니다. const kwCountDisplay = document.getElementById('keyword-count-display'); const activeCount = Array.from(kwBtns).filter(btn => btn.dataset.active === 'true').length; if (kwCountDisplay) kwCountDisplay.textContent = activeCount; alert('키워드가 저장되었습니다.'); closeKeywordModal(); } else { alert('저장 실패: ' + (data.message || '알 수 없는 오류')); } } catch (e) { alert('오류가 발생했습니다.'); } } function openMemoModal() { const contentIdInput = document.querySelector('input[name="content_id"]'); if (!contentIdInput || !contentIdInput.value) { alert('콘텐츠 수정에서만 사용 가능합니다.'); return; } const contentId = contentIdInput.value; const idLabel = document.getElementById('memo-content-id'); const memoContentId = document.getElementById('memo_form_content_id'); const memoSeq = document.getElementById('memo_form_seq'); const memoTitle = document.getElementById('memo_form_title'); const memoActive = document.getElementById('memo_form_active'); const delBtn = document.getElementById('btn-memo-delete'); const gridBody = document.getElementById('memo-grid-body'); if (idLabel) idLabel.textContent = contentId; if (memoContentId) memoContentId.value = contentId; if (memoSeq) memoSeq.value = ''; if (memoTitle) memoTitle.value = ''; if (memoActive) memoActive.checked = true; if (delBtn) delBtn.classList.add('hidden'); if (gridBody) gridBody.innerHTML = '로딩 중...'; document.getElementById('memo-modal').classList.remove('hidden'); loadMemoGrid(); } function closeMemoModal() { document.getElementById('memo-modal').classList.add('hidden'); } async function loadMemoGrid() { const contentId = document.getElementById('memo_form_content_id')?.value || ''; const gridBody = document.getElementById('memo-grid-body'); if (!contentId || !gridBody) return; gridBody.innerHTML = '로딩 중...'; try { const res = await fetch(`../bbs/code_list.php?type=content_memos&content_id=${encodeURIComponent(contentId)}`); const data = await res.json(); if (!data.success) { gridBody.innerHTML = '조회에 실패했습니다.'; return; } const items = data.items || []; if (items.length === 0) { gridBody.innerHTML = '등록된 포스트잇이 없습니다.'; return; } gridBody.innerHTML = ''; items.forEach(it => { const tr = document.createElement('tr'); const activeHtml = it.is_active === '1' ? '' : '-'; tr.innerHTML = ` ${it.seq ?? ''} ${escapeHtml(it.title || '')} ${activeHtml} `; gridBody.appendChild(tr); }); } catch (e) { gridBody.innerHTML = '오류가 발생했습니다.'; } } function newMemo() { const memoSeq = document.getElementById('memo_form_seq'); const memoTitle = document.getElementById('memo_form_title'); const memoActive = document.getElementById('memo_form_active'); const delBtn = document.getElementById('btn-memo-delete'); if (memoSeq) memoSeq.value = ''; if (memoTitle) memoTitle.value = ''; if (memoActive) memoActive.checked = true; if (delBtn) delBtn.classList.add('hidden'); } function editMemo(row) { const memoSeq = document.getElementById('memo_form_seq'); const memoTitle = document.getElementById('memo_form_title'); const memoActive = document.getElementById('memo_form_active'); const delBtn = document.getElementById('btn-memo-delete'); if (memoSeq) memoSeq.value = row.seq ?? ''; if (memoTitle) memoTitle.value = row.title || ''; if (memoActive) memoActive.checked = (row.is_active === '1'); if (delBtn) delBtn.classList.remove('hidden'); } async function saveMemo() { const form = document.getElementById('memo-form'); if (!form) return; const formData = new FormData(form); try { const res = await fetch('../bbs/memo_save.php', { method: 'POST', body: formData }); const data = await res.json(); if (data.success) { alert('저장되었습니다.'); await loadMemoGrid(); // 저장 후에는 수정 상태로 전환(삭제 버튼 활성) const memoSeq = document.getElementById('memo_form_seq'); if (memoSeq && !memoSeq.value && data.seq) memoSeq.value = data.seq; const delBtn = document.getElementById('btn-memo-delete'); if (delBtn) delBtn.classList.remove('hidden'); } else { alert('저장 실패: ' + (data.message || '알 수 없는 오류')); } } catch (e) { alert('오류가 발생했습니다.'); } } async function deleteMemo() { const contentId = document.getElementById('memo_form_content_id')?.value || ''; const seq = document.getElementById('memo_form_seq')?.value || ''; if (!contentId || !seq) return; if (!confirm('정말 삭제하시겠습니까?')) return; try { const fd = new FormData(); fd.set('content_id', contentId); fd.set('seq', seq); const res = await fetch('../bbs/memo_delete.php', { method: 'POST', body: fd }); const data = await res.json(); if (data.success) { alert('삭제되었습니다.'); await loadMemoGrid(); newMemo(); } else { alert('삭제 실패: ' + (data.message || '알 수 없는 오류')); } } catch (e) { alert('오류가 발생했습니다.'); } } function openRecommendModal() { const goalCode = document.getElementById('goal_form_code')?.value || ''; if (!goalCode) { alert('학습목표를 선택 후 사용 가능합니다.'); return; } const goalCodeLabel = document.getElementById('recommend-goal-code'); const recommendGoalCode = document.getElementById('recommend_form_goal_code'); const recommendSeq = document.getElementById('recommend_form_seq'); const recommendTitle = document.getElementById('recommend_form_title'); const recommendActive = document.getElementById('recommend_form_active'); const delBtn = document.getElementById('btn-recommend-delete'); const gridBody = document.getElementById('recommend-grid-body'); if (goalCodeLabel) goalCodeLabel.textContent = goalCode; if (recommendGoalCode) recommendGoalCode.value = goalCode; if (recommendSeq) recommendSeq.value = ''; if (recommendTitle) recommendTitle.value = ''; const recommendTitle2b = document.getElementById('recommend_form_title2'); if (recommendTitle2b) recommendTitle2b.value = ''; if (recommendActive) recommendActive.checked = true; if (delBtn) delBtn.classList.add('hidden'); if (gridBody) gridBody.innerHTML = '로딩 중...'; document.getElementById('recommend-modal').classList.remove('hidden'); loadRecommendGrid(); } function closeRecommendModal() { document.getElementById('recommend-modal').classList.add('hidden'); } async function loadRecommendGrid() { const goalCode = document.getElementById('recommend_form_goal_code')?.value || ''; const gridBody = document.getElementById('recommend-grid-body'); if (!goalCode || !gridBody) return; gridBody.innerHTML = '로딩 중...'; try { const res = await fetch(`../bbs/code_list.php?type=goal_recommends&goal_code=${encodeURIComponent(goalCode)}`); const data = await res.json(); if (!data.success) { gridBody.innerHTML = '조회에 실패했습니다.'; return; } const items = data.items || []; if (items.length === 0) { gridBody.innerHTML = '등록된 추천이유가 없습니다.'; return; } gridBody.innerHTML = ''; items.forEach(it => { const tr = document.createElement('tr'); const activeHtml = it.is_active === '1' ? '' : '-'; tr.innerHTML = ` ${it.seq ?? ''} ${escapeHtml(it.title || '')} ${escapeHtml(it.title2 || '')} ${activeHtml} `; gridBody.appendChild(tr); }); } catch (e) { gridBody.innerHTML = '오류가 발생했습니다.'; } } function newRecommend() { const recommendSeq = document.getElementById('recommend_form_seq'); const recommendTitle = document.getElementById('recommend_form_title'); const recommendTitle2 = document.getElementById('recommend_form_title2'); const recommendActive = document.getElementById('recommend_form_active'); const delBtn = document.getElementById('btn-recommend-delete'); if (recommendSeq) recommendSeq.value = ''; if (recommendTitle) recommendTitle.value = ''; if (recommendTitle2) recommendTitle2.value = ''; if (recommendActive) recommendActive.checked = true; if (delBtn) delBtn.classList.add('hidden'); } function editRecommend(row) { const recommendSeq = document.getElementById('recommend_form_seq'); const recommendTitle = document.getElementById('recommend_form_title'); const recommendTitle2 = document.getElementById('recommend_form_title2'); const recommendActive = document.getElementById('recommend_form_active'); const delBtn = document.getElementById('btn-recommend-delete'); if (recommendSeq) recommendSeq.value = row.seq ?? ''; if (recommendTitle) recommendTitle.value = row.title || ''; if (recommendTitle2) recommendTitle2.value = row.title2 || ''; if (recommendActive) recommendActive.checked = (row.is_active === '1'); if (delBtn) delBtn.classList.remove('hidden'); } async function saveRecommend() { const form = document.getElementById('recommend-form'); if (!form) return; const formData = new FormData(form); try { const res = await fetch('../bbs/recommend_save.php', { method: 'POST', body: formData }); const data = await res.json(); if (data.success) { alert('저장되었습니다.'); await loadRecommendGrid(); // 저장 후에는 수정 상태로 전환(삭제 버튼 활성) const recommendSeq = document.getElementById('recommend_form_seq'); if (recommendSeq && !recommendSeq.value && data.seq) recommendSeq.value = data.seq; const delBtn = document.getElementById('btn-recommend-delete'); if (delBtn) delBtn.classList.remove('hidden'); } else { alert('저장 실패: ' + (data.message || '알 수 없는 오류')); } } catch (e) { alert('오류가 발생했습니다.'); } } async function deleteRecommend() { const goalCode = document.getElementById('recommend_form_goal_code')?.value || ''; const seq = document.getElementById('recommend_form_seq')?.value || ''; if (!goalCode || !seq) return; if (!confirm('정말 삭제하시겠습니까?')) return; try { const fd = new FormData(); fd.set('goal_code', goalCode); fd.set('seq', seq); const res = await fetch('../bbs/recommend_delete.php', { method: 'POST', body: fd }); const data = await res.json(); if (data.success) { alert('삭제되었습니다.'); await loadRecommendGrid(); newRecommend(); } else { alert('삭제 실패: ' + (data.message || '알 수 없는 오류')); } } catch (e) { alert('오류가 발생했습니다.'); } } /** * YouTube URL 또는 영상 ID를 입력하면 서버 프록시(youtube_info.php)를 통해 * 영상 설명(요약)과 길이(초)를 가져와 폼에 자동 세팅합니다. */ async function fetchYouTubeInfo() { const urlInput = document.getElementById('content_url_input'); const descInput = document.getElementById('description_input'); const contentTmIn = document.getElementById('content_tm_input'); const fetchBtn = document.getElementById('btn-yt-fetch'); if (!urlInput) return; const raw = urlInput.value.trim(); if (!raw) { showYtStatus('유튜브 URL 또는 영상 ID를 입력해주세요.', 'error'); return; } // 버튼 로딩 상태 if (fetchBtn) { fetchBtn.disabled = true; fetchBtn.innerHTML = ' 불러오는 중...'; } showYtStatus('영상 정보를 불러오는 중...', 'loading'); try { const res = await fetch(`../bbs/youtube_info.php?video_id=${encodeURIComponent(raw)}`); const data = await res.json(); if (!data.success) { showYtStatus('⚠ ' + (data.message || '영상 정보를 가져올 수 없습니다.'), 'error'); return; } // 콘텐츠설명 자동 입력 (기존 값이 있으면 확인 후 덮어쓰기) if (descInput) { const existing = descInput.value.trim(); if (existing) { if (confirm('콘텐츠설명이 이미 입력되어 있습니다.\n영상 정보로 덮어쓰시겠습니까?')) { descInput.value = data.description || ''; } } else { descInput.value = data.description || ''; } } // content_tm (영상 길이, 초 단위) hidden input 세팅 if (contentTmIn) { contentTmIn.value = data.content_tm || 0; } // 콘텐츠설명 라벨 옆 영상 길이 표시 updateDurationDisplay(data.content_tm || 0); // 성공 메시지 const ytTitle = data.yt_title ? ` — ${data.yt_title}` : ''; showYtStatus(`✓ 로드 완료${ytTitle} (영상 길이: ${data.duration_fmt || ''})`, 'success'); } catch (e) { showYtStatus('⚠ 서버 통신 오류가 발생했습니다.', 'error'); } finally { if (fetchBtn) { fetchBtn.disabled = false; fetchBtn.innerHTML = '영상 정보 가져오기'; } } } /** * 영상 길이(초)를 '분:초' 형식으로 콘텐츠설명 라벨 옆에 표시합니다. */ function updateDurationDisplay(seconds) { const display = document.getElementById('yt-duration-display'); const text = document.getElementById('yt-duration-text'); if (!display || !text) return; if (seconds && seconds > 0) { const m = Math.floor(seconds / 60); const s = seconds % 60; text.textContent = `${m}분 ${String(s).padStart(2, '0')}초`; display.classList.remove('hidden', 'text-gray-300', 'font-normal'); display.classList.add('text-teal-600', 'font-bold'); } else { display.classList.add('hidden'); display.classList.remove('text-teal-600', 'font-bold'); } } /** * YouTube API 상태 메시지를 표시합니다. * type: 'success' | 'error' | 'loading' */ function showYtStatus(msg, type) { const div = document.getElementById('yt-fetch-status'); const span = document.getElementById('yt-fetch-msg'); if (!div || !span) return; span.textContent = msg; div.classList.remove('hidden', 'text-green-600', 'text-red-500', 'text-gray-500'); if (type === 'success') div.classList.add('text-green-600'); else if (type === 'error') div.classList.add('text-red-500'); else div.classList.add('text-gray-500'); }