/** * Atend.js * * 출석체크 관련 기능을 관리하는 모듈 * 사용자가 매일 출석체크를 하고 이에 따른 보상을 받을 수 있는 기능 구현 */ const Atend = (function() { // 전역 변수로 관리 let attendanceDays = []; // 현재 날짜 정보 초기화 const currentDate = new Date(); // 현재 날짜 객체 const currentMonth = currentDate.getMonth(); // 현재 월 (0-11) const currentYear = currentDate.getFullYear(); // 현재 연도 const currentDay = currentDate.getDate(); // 현재 일자 const daysInMonth = new Date(currentYear, currentMonth + 1, 0).getDate(); // 현재 월의 총 일수 const firstDayOfMonth = new Date(currentYear, currentMonth, 1).getDay(); // 현재 월의 첫 날 요일 (0: 일요일, 6: 토요일) // DOM 요소 참조 const daysContainer = document.querySelector(".days"); // 달력 일자를 표시할 컨테이너 const checkAttendanceButton = document.getElementById("check-attendance"); // 출석체크 버튼 const checkNotificationButton = document.getElementById("check-notification"); // 알림 신청/취소 버튼 // 월 이름 배열 - 한글로 표시할 월 이름 const monthNames = ["1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월"]; // 출석 일자 배열 - 서버에서 받아온 데이터에서 공백과 대괄호 등 불필요한 문자 제거 const trimmedAttendanceDays = trimArrayElements(attendanceDays); // 1. attendanceDays 원본 데이터 확인 console.log('attendanceDays:', attendanceDays); // 2. 처리된 데이터 확인 console.log('trimmedAttendanceDays:', trimmedAttendanceDays); // 3. 현재 날짜 형식 확인 const today = new Date(); const formattedToday = `${today.getFullYear()}-${(today.getMonth() + 1).toString().padStart(2, '0')}-${today.getDate().toString().padStart(2, '0')}`; console.log('Today formatted:', formattedToday); /** * 초기화 함수 * 페이지 로드 시 호출되어 달력과 관련 기능을 초기화 */ function init() { updateMonthTitle(); // 월 제목 업데이트 generateCalendar(); // 달력 생성 addEventListeners(); // 이벤트 리스너 등록 getNotificationStatus(); // 알림 상태 조회 getLatestAttendanceInfo(); // 여기서 출석 데이터 로드 및 달력 업데이트 } /** * 월 제목 업데이트 함수 * 현재 월을 한글로 화면에 표시 */ function updateMonthTitle() { document.querySelector(".month-tit").textContent = monthNames[currentMonth]; } /** * 달력 생성 함수 * 빈 셀과 일자를 생성하여 달력 완성 */ function generateCalendar() { generateEmptyCells(); // 달력 시작 전 빈 셀 생성 generateDays(); // 일자 셀 생성 } /** * 달력 앞부분 빈 셀 생성 함수 * 월의 첫 날 요일에 맞게 앞부분에 빈 셀 생성 */ function generateEmptyCells() { for (let i = 0; i < firstDayOfMonth; i++) { const emptyCell = document.createElement("div"); // 빈 div 요소 생성 daysContainer.appendChild(emptyCell); // 달력 컨테이너에 추가 } } /** * 달력 일자 생성 함수 * 현재 월의 모든 일자를 생성하고 출석 상태에 따라 클래스 추가 */ function generateDays() { for (let day = 1; day <= daysInMonth; day++) { const calendarDay = document.createElement("div"); // 날짜 셀 생성 calendarDay.textContent = day; // 날짜 텍스트 설정 // 날짜 형식 포맷팅 (YYYY-MM-DD) const formattedDay = formatDate(currentYear, currentMonth + 1, day); // 출석한 날인 경우 success 클래스 추가 if (trimmedAttendanceDays.includes(formattedDay)) { calendarDay.classList.add("success"); } // 오늘 날짜인 경우 today 클래스 추가 if (day === currentDay) { calendarDay.classList.add("today"); } // 달력 컨테이너에 날짜 셀 추가 daysContainer.appendChild(calendarDay); } } /** * 이벤트 리스너 등록 함수 * 출석체크 및 알림 버튼에 이벤트 핸들러 연결 */ function addEventListeners() { checkAttendanceButton.addEventListener("click", handleAttendanceCheck); // 출석체크 버튼 클릭 이벤트 checkNotificationButton.addEventListener("click", handleNotification); // 알림 신청/취소 버튼 클릭 이벤트 } /** * 출석체크 처리 함수 * 사용자가 출석체크 버튼 클릭 시 실행되는 핸들러 */ function handleAttendanceCheck() { console.log('handleAttendanceCheck'); // 오늘 날짜를 YYYY-MM-DD 형식으로 포맷팅 const formattedToday = formatDate(currentYear, currentMonth + 1, currentDay); // 오늘 출석체크를 아직 하지 않은 경우 if (!trimmedAttendanceDays.includes(formattedToday)) { // 출석 횟수 계산 (현재 출석일 수 + 1) const attendanceCount = trimmedAttendanceDays.length + 1; // 보상 코드 결정 (10일, 20일, 월말에 따라 다른 보상) const bnfRwdCateDvCd = getBenefitRewardCode(attendanceCount); // 출석 정보 저장 요청 saveAttendance(formattedToday, bnfRwdCateDvCd); } else { // 이미 출석체크한 경우 모달 표시 openModal('modal-already-checked'); } } /** * 출석 정보 저장 함수 * 서버에 출석 정보 저장 요청 * @param {string} formattedToday - YYYY-MM-DD 형식의 날짜 * @param {string} bnfRwdCateDvCd - 보상 카테고리 코드 */ function saveAttendance(formattedToday, bnfRwdCateDvCd) { $.ajax({ url: '/sopmal/bnft/participateAttendanceCheck', // 출석체크 API 엔드포인트 method: 'POST', // HTTP 메서드 contentType: 'application/json; charset=UTF-8', // 요청 콘텐츠 타입 data: JSON.stringify({ bnfInnb: screenInfo.bnfInnb, bnfPrtDt: formattedToday, // 출석일자 bnfRwdCateDvCd: bnfRwdCateDvCd, // 보상 카테고리 코드 bnfRwdGodsDvCd: '010' // 보상 상품 코드 (010: 마일리지) }), success: function(response) { var data = JSON.parse(response.resBody.result); //console.log(data); if (data.success) { // 출석체크 성공 처리 handleAttendanceSuccess(formattedToday)(data); } else { // 실패 메시지 표시 - alert를 showCustomAlert로 변경 showCustomAlert(data.message || '출석체크에 실패했습니다.'); } }, error: handleAttendanceError // 에러 핸들러 }); } /** * 출석 저장 성공 처리 함수 * 출석체크 성공 시 UI 업데이트 및 모달 표시 * @param {string} formattedToday - YYYY-MM-DD 형식의 날짜 * @returns {Function} - 실제 성공 처리를 수행할 콜백 함수 */ function handleAttendanceSuccess(formattedToday) { return function(data) { // 출석일 배열에 오늘 날짜 추가 trimmedAttendanceDays.push(formattedToday); // 달력 UI 업데이트 updateCalendarUI(); // 출석 완료 모달 표시 (응답 데이터 전달) showAttendanceModal(data); // 최신 출석 정보 조회 (출석 횟수, 적립 마일리지 등) getLatestAttendanceInfo(); }; } /** * 알림용 모달 표시 함수 * alert 대신 모달을 사용하여 메시지 표시 * @param {string} message - 표시할 메시지 */ function showCustomAlert(message) { $('#modal-alert .microModal__content p').text(message); // 기존 이벤트 모두 제거 $('#modal-alert .microModal__btn-primary').off('click'); $('#modal-alert .microModal__overlay').off('click'); // 확인 버튼 클릭 이벤트 $('#modal-alert .microModal__btn-primary').on('click', function(e) { e.preventDefault(); e.stopPropagation(); console.log("모달 확인 버튼 클릭"); MicroModal.close('modal-alert'); }); // 오버레이 클릭 시 닫기 $('#modal-alert .microModal__overlay').on('click', function(e) { if (e.target === this) { console.log("모달 오버레이 클릭"); MicroModal.close('modal-alert'); } }); // ESC 키로 닫기 $(document).off('keydown.modalAlert').on('keydown.modalAlert', function(e) { if (e.keyCode === 27) { // ESC 키 console.log("ESC 키로 모달 닫기"); MicroModal.close('modal-alert'); $(document).off('keydown.modalAlert'); } }); console.log("모달 표시:", message); MicroModal.show('modal-alert'); } /** * 출석 저장 실패 처리 함수 * 출석체크 저장 실패 시 에러 메시지 표시 * @param {Object} xhr - XMLHttpRequest 객체 * @param {string} status - 상태 텍스트 * @param {string} error - 에러 메시지 */ function handleAttendanceError(xhr, status, error) { console.error('Failed to save attendance:', error); // alert를 showCustomAlert로 변경 showCustomAlert('출석체크를 저장하는 데 실패했습니다. 다시 시도해주세요.'); } /** * 달력 UI 업데이트 함수 * 출석체크 성공 후 오늘 날짜 셀에 성공 표시 추가 */ function updateCalendarUI() { // 오늘 날짜에 해당하는 셀 찾기 const todayElement = Array.from(daysContainer.children).find(child => child.textContent == currentDay); if (todayElement) { // 출석 성공 클래스 추가 todayElement.classList.add("success"); } } /** * 출석 모달 표시 함수 * 출석 횟수에 따라 다른 보상 모달 표시 * @param {Object} data - 서버 응답 데이터 (receivedRewards 포함) */ function showAttendanceModal(data) { const attendanceCount = data.currentAttendanceCount || trimmedAttendanceDays.length; // 10일 출석 달성 if (attendanceCount === 10) { updateModalWithRewardInfo('modal-2', data.receivedRewards); openModal('modal-2'); } // 20일 출석 달성 else if (attendanceCount === 20) { updateModalWithRewardInfo('modal-3', data.receivedRewards); openModal('modal-3'); } // 한 달 모두 출석 달성 else if (attendanceCount === daysInMonth) { updateModalWithRewardInfo('modal-4', data.receivedRewards); openModal('modal-4'); } // 일반 출석 else { openModal('modal-1'); } } /** * 모달에 보상 정보 업데이트 함수 * 서버에서 받은 보상 정보로 모달 내용을 동적으로 업데이트 * @param {string} modalId - 모달 ID * @param {Array} receivedRewards - 받은 보상 목록 */ function updateModalWithRewardInfo(modalId, receivedRewards) { if (!receivedRewards || receivedRewards.length === 0) { return; } const modal = document.getElementById(modalId); if (!modal) { return; } const subTitleElement = modal.querySelector('.sub_tit'); if (!subTitleElement) { return; } // 쿠폰 보상이 있는 경우 모달 내용 업데이트 const couponReward = receivedRewards.find(reward => reward.couponName); if (couponReward) { if (modalId === 'modal-2' || modalId === 'modal-3') { // 10회, 20회 출석 시 쿠폰 이름 동적 표시 subTitleElement.textContent = `${couponReward.couponName}이 발급되었습니다.`; } } // 100% 출석 보상의 경우 (modal-4) if (modalId === 'modal-4') { const mileageReward = receivedRewards.find(reward => reward.mileageAmount); let rewardText = ''; if (mileageReward) { rewardText += `축하마일리지 ${mileageReward.mileageAmount}원 적립완료`; } else { rewardText += '축하마일리지 적립완료'; } rewardText += '\n특별경품 자동응모 완료'; // 쿠폰도 있는 경우 추가 if (couponReward) { rewardText += `\n${couponReward.couponName} 발급완료`; } subTitleElement.innerHTML = rewardText.replace(/\n/g, '
'); } } /** * 최신 출석 정보 조회 함수 * 서버에서 최신 출석 횟수와 적립 마일리지 정보 조회 */ function getLatestAttendanceInfo() { $.ajax({ url: '/sopmal/bnft/getLatestAttendanceInfo', method: 'POST', data: JSON.stringify({ bnfInnb: screenInfo.bnfInnb }), contentType: 'application/json; charset=utf-8', success: function(response) { // 출석 정보 UI 업데이트 updateAttendanceInfo(response.attendanceCount, response.totalAttendanceMileage); // 출석 날짜로 달력 업데이트 if (response.attendanceDays) { updateCalendarWithAttendanceDays(response.attendanceDays); } }, error: function(xhr, status, error) { console.error('Failed to fetch latest attendance info:', error); } }); } /** * 출석 정보 UI 업데이트 함수 * 화면에 출석 횟수와 누적 마일리지 표시 * @param {number} attendanceCount - 출석 횟수 * @param {number} totalAttendanceMileage - 누적 마일리지 */ function updateAttendanceInfo(attendanceCount, totalAttendanceMileage) { document.querySelector(".cnt1").textContent = `${attendanceCount}회`; // 출석 횟수 표시 document.querySelector(".cnt2").textContent = `${totalAttendanceMileage}원`; // 누적 마일리지 표시 } /** * 배열 요소 공백 제거 함수 * 배열의 각 요소에서 대괄호와 공백 제거 * @param {Array} arr - 처리할 배열 * @returns {Array} - 처리된 배열 */ function trimArrayElements(arr) { return arr.map(element => element.replace(/[\[\]\s]/g, '')); } /** * 날짜 포맷팅 함수 * 연, 월, 일을 YYYY-MM-DD 형식으로 변환 * @param {number} year - 연도 * @param {number} month - 월 (1-12) * @param {number} day - 일 * @returns {string} - YYYY-MM-DD 형식의 날짜 문자열 */ function formatDate(year, month, day) { return `${year}-${month.toString().padStart(2, '0')}-${day.toString().padStart(2, '0')}`; } /** * 보상 코드 결정 함수 * 출석 횟수에 따라 다른 보상 코드 반환 * @param {number} attendanceCount - 출석 횟수 * @returns {string} - 보상 카테고리 코드 */ function getBenefitRewardCode(attendanceCount) { if (attendanceCount === 10) return '01020'; // 10일 출석 보상 if (attendanceCount === 20) return '01030'; // 20일 출석 보상 if (attendanceCount === daysInMonth) return '01040'; // 한 달 모두 출석 보상 return '01010'; // 일반 출석 보상 } /** * 알림 상태 조회 함수 * 서버에서 사용자의 알림 신청 상태 조회 */ function getNotificationStatus() { $.ajax({ url: '/sopmal/bnft/getNotificationStatus', // 알림 상태 조회 API method: 'POST', contentType: 'application/json', data: JSON.stringify({ bnfInnb: screenInfo.bnfInnb }), // 혜택 고유번호 success: function(response) { // 알림 버튼 UI 업데이트 updateNotificationButton(response.notificationStatus); }, error: function(xhr, status, error) { console.error('알림 상태 조회 실패:', error); } }); } /** * 알림 버튼 업데이트 함수 * 알림 상태에 따라 버튼 텍스트와 데이터 속성 변경 * @param {boolean} isNotificationOn - 알림 신청 여부 */ function updateNotificationButton(isNotificationOn) { const $notificationBtn = $('#check-notification'); const $iconElement = $notificationBtn.find('i.ico_alarm'); // 알림 상태에 따라 버튼 텍스트 변경 if (isNotificationOn) { // 알림 활성화 상태: '알림 신청 완료' 표시 및 취소 액션으로 설정 $notificationBtn.contents().filter(function() { return this.nodeType === 3; // 텍스트 노드만 선택 }).replaceWith('알림 신청 완료'); $notificationBtn.data('action', 'cancel'); } else { // 알림 비활성화 상태: '알림 신청' 표시 및 신청 액션으로 설정 $notificationBtn.contents().filter(function() { return this.nodeType === 3; // 텍스트 노드만 선택 }).replaceWith('알림 신청'); $notificationBtn.data('action', 'apply'); } // 알림 아이콘이 없으면 추가 if ($iconElement.length === 0) { $notificationBtn.prepend(''); } } /** * 알림 신청/취소 처리 함수 * 사용자가 알림 버튼 클릭 시 신청 또는 취소 요청 처리 */ function handleNotification() { // 현재 버튼의 액션 상태 확인 (신청 또는 취소) var action = $('#check-notification').data('action'); // 액션에 따른 API URL 결정 const url = action === 'apply' ? '/sopmal/bnft/applyNotification' : '/sopmal/bnft/cancelNotification'; $.ajax({ url: url, method: 'POST', contentType: 'application/json', data: JSON.stringify({ bnfInnb: screenInfo.bnfInnb }), // 혜택 고유번호 success: function(response) { if (response.success) { // 성공 메시지 표시 (신청 또는 취소에 따라 다름) $('#modal-alert .microModal__content p').text(action === 'apply' ? '알림 신청이 완료되었습니다.' : '알림 취소가 완료되었습니다.'); MicroModal.show('modal-alert'); // 알림 상태 다시 조회하여 UI 업데이트 getNotificationStatus(); } else { // 실패 메시지 표시 $('#modal-alert .microModal__content p').text(response.message || (action === 'apply' ? '알림 신청에 실패했습니다.' : '알림 취소에 실패했습니다.')); MicroModal.show('modal-alert'); } }, error: function(xhr, status, error) { console.error(action === 'apply' ? '알림 신청 실패:' : '알림 취소 실패:', error); $('#modal-alert .microModal__content p').text(action === 'apply' ? '알림 신청 중 오류가 발생했습니다.' : '알림 취소 중 오류가 발생했습니다.'); MicroModal.show('modal-alert'); } }); } /** * 문서 로드 완료 이벤트 핸들러 * 페이지 로드 시 초기화 진행 (로그인 확인 로직 제거됨) */ document.addEventListener("DOMContentLoaded", function() { // 로그인 확인 로직 없이 바로 초기화 함수 호출 init(); // getNotificationStatus(); // init() 내부에서 호출됨 // getLatestAttendanceInfo(); // init() 내부에서 호출되도록 수정했으므로 제거 }); /** * 달력 업데이트 함수 (모듈 내부로 이동) */ function updateCalendarWithAttendanceDays(attendanceDaysArray) { console.log('Updating calendar with attendance days:', attendanceDaysArray); // 전역 attendanceDays 업데이트 attendanceDays = attendanceDaysArray; // 기존 success 클래스 제거 document.querySelectorAll('.days div').forEach(element => { element.classList.remove('success'); }); // 새로운 출석 날짜에 success 클래스 추가 attendanceDaysArray.forEach(dateStr => { const day = new Date(dateStr).getDate(); const dayElements = Array.from(daysContainer.children); // 해당 날짜 요소 찾기 (빈 셀 제외) const dayElement = dayElements.find(child => { return child.textContent == day && child.textContent !== ''; }); if (dayElement) { dayElement.classList.add('success'); console.log(`Added success class to day ${day}`); } }); } // 공개 메서드 return { init: init, // 초기화 함수 getLatestAttendanceInfo: getLatestAttendanceInfo, // 최신 출석 정보 조회 getNotificationStatus: getNotificationStatus, // 알림 상태 조회 handleNotification: handleNotification, // 알림 신청/취소 처리 updateCalendarWithAttendanceDays: updateCalendarWithAttendanceDays // 외부 접근용 }; })(); /** * 브라우저 뒤로가기 이벤트 핸들러 * 뒤로가기 시 열려있는 모달 닫기 */ window.addEventListener('popstate', function(event) { var modalId = window.location.hash.replace('#', ''); if (modalId) { // 해시에 모달 ID가 있으면 해당 모달 닫기 closeModal(modalId); } else { // 해시가 없으면 모든 모달 닫기 document.querySelectorAll('.microModal').forEach(modal => { MicroModal.close(modal.id); }); } // 본문 스크롤 복원 document.body.style.overflow = ''; }); /** * 페이지 로드 시 해시 체크 * URL에 해시가 있으면 해당 모달 열기 */ if (window.location.hash) { var modalId = window.location.hash.replace('#', ''); openModal(modalId); }