/** * ===================== * 사용페이지 : 마이페이지 * 작 성 자 : ERP기획팀 moon * 최초작성일자: 26.03.20. * ===================== */ (function () { /** * 마이페이지 초기 진입 시 실행 * - 학습활동 데이터 로드 * - 콘텐츠 탭 클릭 재렌더링 이벤트 연결 * - 최초 로드 완료 후 활성 탭 기준으로 리스트를 다시 렌더링한다 */ document.addEventListener("DOMContentLoaded", function () { // 연도 select 변경 이벤트 연결 bindYearSelectChange(); // 학습활동 초기 데이터 조회 loadMypageInit(); // 콘텐츠 탭 클릭 시 재렌더링 이벤트 연결 bindContentTabReload(); }); /** * 모든 리소스 로드 완료 후 실행 * - 퍼블리싱용 mypage.js 초기화가 끝난 뒤 * 현재 활성 탭 기준으로 콘텐츠 리스트를 다시 렌더링한다 */ window.addEventListener("load", function () { setTimeout(function () { reloadActiveContentTab(); }, 150); }); //JS 1단계 (가장 먼저) /** * 마이페이지 초기 데이터 로드 함수 * - 서버에서 학습활동 관련 데이터를 가져온다 * - 연도 select 및 총 학습시간을 화면에 반영한다 */ function loadMypageInit(year = '') { fetch('/edu/ajax/get_init_data_for_mypage.php?year=' + year) .then(res => res.json()) .then(data => { //----------------------- // 실패 시 종료 if (!data.success) return; //----------------------- // 1. 연도 select 구성 renderYearSelect(data.year_list, data.selected_year); //----------------------- // 2. 총 학습시간 표시 renderMyTotalMinutes(data.my_total_minutes); //----------------------- // 3. 모바일 총 학습시간 표시 renderMobileMyTotalMinutes(data.my_total_minutes); //----------------------- // 4. 전체평균 표시 renderAvgTotalMinutes(data.avg_total_minutes); //----------------------- // 5. 상단 총 학습시간 게이지 표시 renderMyTotalGauge(data.my_total_minutes); //----------------------- // 6. 마이클래스 항목 표시 renderMyClassActivity(data.activity_items); // 7. 온보딩 학습활동 데이터를 화면에 반영 renderOnboardingActivity(data.activity_items); //----------------------- // 8. 법정교육 학습활동 데이터를 화면에 반영 renderLegalEducationActivity(data.activity_items); //----------------------- // 9. 리더십 학습활동 데이터를 화면에 반영 renderLeadershipActivity(data.activity_items); //----------------------- // 10. 인사이트 학습활동 데이터를 화면에 반영 renderInsightActivity(data.activity_items); //----------------------- // 11. 비즈트렌드 학습활동 데이터를 화면에 반영 renderBizTrendActivity(data.activity_items); }); } //JS 2단계 (연도 select 채우기) function renderYearSelect(yearList, selectedYear) { const select = document.getElementById('mypage-year-select'); if (!select) return; select.innerHTML = ''; yearList.forEach(year => { const option = document.createElement('option'); option.value = year; option.textContent = year + '년'; if (year == selectedYear) { option.selected = true; } select.appendChild(option); }); } //AJAX 응답의 my_total_minutes를 #mypage-total-minutes에 넣기 /** * 총 학습시간(분)을 화면에 표시하는 함수 * - 서버에서 받은 my_total_minutes 값을 * #mypage-total-minutes 영역에 표시한다. * - 숫자는 천단위 콤마 포맷 적용 */ function renderMyTotalMinutes(totalMinutes) { // 대상 DOM 가져오기 const el = document.getElementById('mypage-total-minutes'); if (!el) return; // 숫자 변환 및 예외 방어 (null, undefined 대비) const value = parseInt(totalMinutes, 10) || 0; // 화면에 출력 (예: 1,200) el.textContent = value.toLocaleString(); } /** * 모바일 총 학습시간(분)을 화면에 표시하는 함수 * - 서버에서 받은 my_total_minutes 값을 * #mobile-mypage-total-minutes 영역에 표시한다. * - 숫자는 천단위 콤마 포맷 적용 */ function renderMobileMyTotalMinutes(totalMinutes) { // 대상 DOM 가져오기 const el = document.getElementById('mobile-mypage-total-minutes'); if (!el) return; // 숫자 변환 및 예외 방어 const value = parseInt(totalMinutes, 10) || 0; // 화면에 출력 el.textContent = value.toLocaleString(); } /** * 전체평균 학습시간(분)을 화면에 표시하는 함수 * - 서버에서 받은 avg_total_minutes 값을 * #mypage-avg-minutes 영역에 표시한다. * - 숫자는 천단위 콤마 포맷 적용 */ function renderAvgTotalMinutes(avgTotalMinutes) { // 대상 DOM 가져오기 const el = document.getElementById('mypage-avg-minutes'); if (!el) return; // 숫자 변환 및 예외 방어 const value = parseInt(avgTotalMinutes, 10) || 0; // 화면에 출력 el.textContent = value.toLocaleString(); } /** * 총 학습시간 게이지를 화면에 반영하는 함수 * - 서버에서 받은 my_total_minutes 값을 기준으로 * #mypage-total-gauge의 width 값을 변경한다. * - 최대 기준은 40시간(2400분)으로 계산한다. * - 2400분을 초과해도 게이지는 100%를 넘지 않도록 제한한다. */ function renderMyTotalGauge(totalMinutes) { // 대상 DOM 가져오기 const el = document.getElementById('mypage-total-gauge'); if (!el) return; // 총 학습시간(분) 숫자 변환 const value = parseInt(totalMinutes, 10) || 0; // 게이지 최대 기준값 (40시간 = 2400분) const maxMinutes = 2400; // 퍼센트 계산 let percent = Math.round((value / maxMinutes) * 100); // 0 ~ 100 범위로 제한 if (percent < 0) percent = 0; if (percent > 100) percent = 100; // 게이지 너비 반영 el.style.width = percent + '%'; } /** * 연도 select 변경 이벤트를 연결하는 함수 * - 사용자가 연도를 변경하면 * 선택한 연도값으로 마이페이지 초기 데이터를 다시 조회한다. */ function bindYearSelectChange() { // 연도 select DOM 가져오기 const select = document.getElementById('mypage-year-select'); if (!select) return; // 연도 변경 이벤트 연결 select.addEventListener('change', function () { // 현재 선택된 연도 가져오기 const selectedYear = this.value || ''; // 선택 연도 기준으로 데이터 다시 조회 loadMypageInit(selectedYear); }); } /** * 마이클래스(CA10001) 학습활동 데이터를 화면에 반영하는 함수 * - activity_items 배열에서 마이클래스 항목만 찾아서 * 해당 li[data-category="CA10001"] 영역의 수치를 갱신한다. * - 학습시간, 퍼센트, 게이지, 전체 콘텐츠 개수를 반영한다. */ function renderMyClassActivity(activityItems) { // activity_items가 배열이 아니면 종료 if (!Array.isArray(activityItems)) return; // 마이클래스 항목 찾기 const item = activityItems.find(function (row) { return row.category_code === 'CA10001'; }); if (!item) return; // 마이클래스 li 찾기 const li = document.querySelector('.activity-list li[data-category="CA10001"]'); if (!li) return; // 숫자 변환 및 예외 방어 const totalWatchTm = parseInt(item.total_watch_tm, 10) || 0; const percent = parseInt(item.percent, 10) || 0; const totalCount = parseInt(item.master_total_cnt, 10) || 0; // 1. 우측 상단 표시값 갱신 // 예: 74분 (33%) const valueEl = li.querySelector('.activity-value'); if (valueEl) { valueEl.innerHTML = '' + totalWatchTm.toLocaleString() + '분 (' + percent + '%)'; } // 2. 게이지 너비 갱신 const gaugeEl = li.querySelector('.gauge-fill'); if (gaugeEl) { gaugeEl.style.width = percent + '%'; } // 3. 분모 표시 갱신 // 예: 6개 const endEl = li.querySelector('.gauge-end'); if (endEl) { endEl.textContent = totalCount.toLocaleString() + '개'; } } /** * 온보딩(CA10002) 학습활동 데이터를 화면에 반영하는 함수 * - 학습시간, 퍼센트, 게이지, 분모를 표시 * - 온보딩 기간일 경우에만 "필수 시청 날짜"를 노출한다. */ function renderOnboardingActivity(activityItems) { // 배열 방어 if (!Array.isArray(activityItems)) return; // 온보딩 항목 찾기 const item = activityItems.find(function (row) { return row.category_code === 'CA10002'; }); if (!item) return; // 해당 li 찾기 const li = document.querySelector('.activity-list li[data-category="CA10002"]'); if (!li) return; // 값 파싱 const totalWatchTm = parseInt(item.total_watch_tm, 10) || 0; const percent = parseInt(item.percent, 10) || 0; const totalCount = parseInt(item.master_total_cnt, 10) || 0; // 1. 학습시간 + 퍼센트 표시 const valueEl = li.querySelector('.activity-value'); if (valueEl) { valueEl.innerHTML = '' + totalWatchTm.toLocaleString() + '분 (' + percent + '%)'; } // 2. 게이지 const gaugeEl = li.querySelector('.gauge-fill'); if (gaugeEl) { gaugeEl.style.width = percent + '%'; } // 3. 분모 표시 const endEl = li.querySelector('.gauge-end'); if (endEl) { endEl.textContent = totalCount.toLocaleString() + '개'; } // 4. 온보딩 안내문구 제어 const noteEl = li.querySelector('.activity-note'); const dateEl = li.querySelector('.onboarding-due-date'); if (noteEl && dateEl) { // 날짜 표시 (yy.mm.dd 형식) if (item.onboarding_due_date) { const d = new Date(item.onboarding_due_date); if (!isNaN(d.getTime())) { const yy = String(d.getFullYear()).slice(2); const mm = String(d.getMonth() + 1).padStart(2, '0'); const dd = String(d.getDate()).padStart(2, '0'); dateEl.textContent = yy + '.' + mm + '.' + dd; } } // 온보딩 기간 여부에 따라 표시/숨김 if (item.is_onboarding_period === 'Y') { noteEl.style.display = ''; } else { noteEl.style.display = 'none'; } } } /** * 법정교육(CA10003) 학습활동 데이터를 화면에 반영하는 함수 * - 학습시간, 퍼센트, 게이지, 분모를 표시한다. * - activity_items 배열에서 법정교육 항목만 찾아 * 해당 li[data-category="CA10003"] 영역을 갱신한다. */ function renderLegalEducationActivity(activityItems) { // 배열이 아니면 종료 if (!Array.isArray(activityItems)) return; // 법정교육 항목 찾기 const item = activityItems.find(function (row) { return row.category_code === 'CA10003'; }); if (!item) return; // 해당 li 찾기 const li = document.querySelector('.activity-list li[data-category="CA10003"]'); if (!li) return; // 숫자 변환 및 예외 방어 const totalWatchTm = parseInt(item.total_watch_tm, 10) || 0; const percent = parseInt(item.percent, 10) || 0; const totalCount = parseInt(item.master_total_cnt, 10) || 0; // 1. 우측 상단 표시값 갱신 // 예: 30분 (20%) const valueEl = li.querySelector('.activity-value'); if (valueEl) { valueEl.innerHTML = '' + totalWatchTm.toLocaleString() + '분 (' + percent + '%)'; } // 2. 게이지 너비 갱신 const gaugeEl = li.querySelector('.gauge-fill'); if (gaugeEl) { gaugeEl.style.width = percent + '%'; } // 3. 분모 표시 갱신 // 예: 6개 const endEl = li.querySelector('.gauge-end'); if (endEl) { endEl.textContent = totalCount.toLocaleString() + '개'; } } /** * 리더십(CA10004) 학습활동 데이터를 화면에 반영하는 함수 * - 학습시간, 게이지, 분모(분)를 표시한다. * - activity_items 배열에서 리더십 항목만 찾아 * 해당 li[data-category="CA10004"] 영역을 갱신한다. */ function renderLeadershipActivity(activityItems) { // 배열이 아니면 종료 if (!Array.isArray(activityItems)) return; // 리더십 항목 찾기 const item = activityItems.find(function (row) { return row.category_code === 'CA10004'; }); if (!item) return; // 해당 li 찾기 const li = document.querySelector('.activity-list li[data-category="CA10004"]'); if (!li) return; // 숫자 변환 및 예외 방어 const totalWatchTm = parseInt(item.total_watch_tm, 10) || 0; const percent = parseInt(item.percent, 10) || 0; const totalContentTm = parseInt(item.total_content_tm, 10) || 0; // 1. 우측 상단 표시값 갱신 // 예: 60분 const valueEl = li.querySelector('.activity-value'); if (valueEl) { valueEl.innerHTML = '' + totalWatchTm.toLocaleString() + '분'; } // 2. 게이지 너비 갱신 const gaugeEl = li.querySelector('.gauge-fill'); if (gaugeEl) { gaugeEl.style.width = percent + '%'; } // 3. 분모 표시 갱신 // 예: 240분 const endEl = li.querySelector('.gauge-end'); if (endEl) { endEl.textContent = totalContentTm.toLocaleString() + '분'; } } /** * 인사이트(CA10005) 학습활동 데이터를 화면에 반영하는 함수 * - 학습시간, 게이지, 분모(분)를 표시한다. * - activity_items 배열에서 인사이트 항목만 찾아 * 해당 li[data-category="CA10005"] 영역을 갱신한다. */ function renderInsightActivity(activityItems) { // 배열이 아니면 종료 if (!Array.isArray(activityItems)) return; // 인사이트 항목 찾기 const item = activityItems.find(function (row) { return row.category_code === 'CA10005'; }); if (!item) return; // 해당 li 찾기 const li = document.querySelector('.activity-list li[data-category="CA10005"]'); if (!li) return; // 숫자 변환 및 예외 방어 const totalWatchTm = parseInt(item.total_watch_tm, 10) || 0; const percent = parseInt(item.percent, 10) || 0; const totalContentTm = parseInt(item.total_content_tm, 10) || 0; // 1. 우측 상단 표시값 갱신 // 예: 43분 const valueEl = li.querySelector('.activity-value'); if (valueEl) { valueEl.innerHTML = '' + totalWatchTm.toLocaleString() + '분'; } // 2. 게이지 너비 갱신 const gaugeEl = li.querySelector('.gauge-fill'); if (gaugeEl) { gaugeEl.style.width = percent + '%'; } // 3. 분모 표시 갱신 // 예: 90분 const endEl = li.querySelector('.gauge-end'); if (endEl) { endEl.textContent = totalContentTm.toLocaleString() + '분'; } } /** * 비즈트렌드(CA10006) 학습활동 데이터를 화면에 반영하는 함수 * - 학습시간, 게이지, 분모(분)를 표시한다. * - activity_items 배열에서 비즈트렌드 항목만 찾아 * 해당 li[data-category="CA10006"] 영역을 갱신한다. */ function renderBizTrendActivity(activityItems) { // 배열이 아니면 종료 if (!Array.isArray(activityItems)) return; // 비즈트렌드 항목 찾기 const item = activityItems.find(function (row) { return row.category_code === 'CA10006'; }); if (!item) return; // 해당 li 찾기 const li = document.querySelector('.activity-list li[data-category="CA10006"]'); if (!li) return; // 숫자 변환 및 예외 방어 const totalWatchTm = parseInt(item.total_watch_tm, 10) || 0; const percent = parseInt(item.percent, 10) || 0; const totalContentTm = parseInt(item.total_content_tm, 10) || 0; // 1. 우측 상단 표시값 갱신 // 예: 23분 const valueEl = li.querySelector('.activity-value'); if (valueEl) { valueEl.innerHTML = '' + totalWatchTm.toLocaleString() + '분'; } // 2. 게이지 너비 갱신 const gaugeEl = li.querySelector('.gauge-fill'); if (gaugeEl) { gaugeEl.style.width = percent + '%'; } // 3. 분모 표시 갱신 // 예: 80분 const endEl = li.querySelector('.gauge-end'); if (endEl) { endEl.textContent = totalContentTm.toLocaleString() + '분'; } }//renderBizTrendActivity /** * 시청중인 콘텐츠 목록을 서버에서 조회하여 화면에 표시하는 함수 * - watching 탭 기준으로 목록 HTML을 받아 * #mypage-watching-list 영역에 삽입한다. * - mypage.js가 먼저 탭/패널 초기화를 수행한 뒤, * 이 함수가 실제 콘텐츠 목록을 최종 반영하는 역할을 한다. */ function loadWatchingContentList() { // 시청중 목록 영역 가져오기 const listEl = document.getElementById('mypage-watching-list'); if (!listEl) return; // 서버에 시청중 탭 목록 요청 fetch('/edu/ajax/get_video_list_for_mypage.php?tab=watching&page=1') .then(function (res) { return res.json(); }) .then(function (data) { // 응답 성공 여부 확인 if (!data.success) return; // 서버에서 내려준 HTML을 최종 반영 listEl.innerHTML = data.html || ''; // 디버깅 확인용 console.log('[watching] 최종 렌더링 완료:', listEl.querySelectorAll('.content-card').length); }) .catch(function (error) { console.error('시청중 콘텐츠 목록을 불러오지 못했습니다.', error); }); } /** * 콘텐츠 탭 클릭 이벤트 감지 후 * 해당 탭에 맞는 데이터를 다시 렌더링하는 함수 * * - mypage.js가 탭 전환을 처리한 이후 실행된다 * - 각 탭(watching / completed / saved)에 따라 * AJAX 호출을 다시 수행하여 리스트를 덮어쓴다 */ function bindContentTabReload() { // 모든 탭 버튼 가져오기 const tabs = document.querySelectorAll('.content-tab'); tabs.forEach(function (tabBtn) { tabBtn.addEventListener('click', function () { const tab = this.getAttribute('data-tab'); // 약간의 딜레이를 줘서 mypage.js 처리 이후 실행 setTimeout(function () { if (tab === 'watching') { loadWatchingContentList(); } else if (tab === 'completed') { loadCompletedContentList(); } else if (tab === 'saved') { loadSavedContentList(); } console.log('[탭 재렌더링]', tab); }, 100); }); }); } /** * 시청완료 콘텐츠 목록 로드 */ function loadCompletedContentList() { const listEl = document.querySelector('#panel-completed .content-grid'); if (!listEl) return; fetch('/edu/ajax/get_video_list_for_mypage.php?tab=completed&page=1') .then(res => res.json()) .then(function (data) { if (!data.success) return; listEl.innerHTML = data.html || ''; console.log('[completed] 렌더링 완료'); }) .catch(function (e) { console.error('completed 로딩 실패', e); }); } /** * 저장한 콘텐츠 목록 로드 */ function loadSavedContentList() { const listEl = document.querySelector('#panel-saved .content-grid'); if (!listEl) return; fetch('/edu/ajax/get_video_list_for_mypage.php?tab=saved&page=1') .then(res => res.json()) .then(function (data) { if (!data.success) return; listEl.innerHTML = data.html || ''; console.log('[saved] 렌더링 완료'); }) .catch(function (e) { console.error('saved 로딩 실패', e); }); } /** * 현재 활성화된 콘텐츠 탭을 기준으로 * 해당 탭의 리스트를 다시 렌더링하는 함수 * * - 최초 페이지 진입 시 사용 * - mypage.js가 탭/패널 초기화를 끝낸 뒤 실행되어야 한다 */ function reloadActiveContentTab() { // 현재 active 상태의 탭 버튼 찾기 const activeTab = document.querySelector('.content-tab.active'); if (!activeTab) return; // 활성 탭 종류 확인 const tab = activeTab.getAttribute('data-tab'); // 탭 종류에 따라 해당 리스트 다시 조회 if (tab === 'watching') { loadWatchingContentList(); } else if (tab === 'completed') { loadCompletedContentList(); } else if (tab === 'saved') { loadSavedContentList(); } console.log('[초기 활성 탭 재렌더링]', tab); } })();