/**
* 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);
}