merge master into develop - CscListRForm.jsp FAQ 아코디언 버그 수정

This commit is contained in:
jilee4unity
2026-05-28 23:34:06 +09:00
7250 changed files with 570509 additions and 25 deletions

View File

@@ -0,0 +1,432 @@
/**
* OXQuiz.js
*
* OX 퀴즈 기능을 구현한 모듈
* 사용자가 OX 퀴즈에 참여하고 결과를 확인하는 기능 제공
*
* 주요 기능:
* 1. OX 퀴즈 참여 및 결과 처리
* 2. 퀴즈 UI 업데이트 및 상태 관리
* 3. 알림 설정 관리
* 4. 모달 팝업을 통한 결과 안내
*/
const OXQuiz = (function() {
/**
* 모듈 초기화 함수
* 이벤트 리스너 등록 및 알림 상태 조회
*/
function init() {
addEventListeners();
getNotificationStatus();
}
/**
* 이벤트 리스너 등록 함수
* 퀴즈 결과 확인, 답변 선택, 알림 설정 버튼에 이벤트 리스너 등록
*/
function addEventListeners() {
$('#quizResult').off('click').on('click', handleQuizResult);
$('.qzSelect a').off('click').on('click', handleAnswerSelection);
$('#notificationBtn').off('click').on('click', handleNotification);
// 힌트 버튼에 이벤트 리스너 추가
$('#hintButton').off('click').on('click', function() {
const hintUrl = screenInfo.quizInfoList[screenInfo.currentQuiz - 1].hintUrl;
openExternalUrl(hintUrl);
});
}
/**
* 답변 선택 처리 함수
* 사용자가 OX 중 하나를 선택했을 때 UI 업데이트
* @param {Event} e - 클릭 이벤트 객체
*/
function handleAnswerSelection(e) {
e.preventDefault();
// 현재 퀴즈가 이미 완료된 상태인지 확인
const currentQuiz = screenInfo.quizInfoList[screenInfo.currentQuiz - 1];
if (currentQuiz.userAnswer !== null) {
showCustomAlert('이미 완료된 퀴즈입니다.');
return;
}
$('.qzSelect a').removeClass('on');
$(this).addClass('on');
}
/**
* 퀴즈 결과 확인 처리 함수
* 사용자가 선택한 답변을 서버에 전송하고 결과 처리
*/
function handleQuizResult() {
const $resultButton = $(this);
// 버튼이 비활성화 상태면 함수 실행 중지
if ($resultButton.prop('disabled')) return;
// 선택된 답변 확인
const selectedAnswer = $('.qzSelect a.on').data('answer');
if (!selectedAnswer) {
showCustomAlert('OX 중 한 가지를 선택해 주세요.');
return;
}
// 중복 클릭 방지를 위한 버튼 비활성화
$resultButton.prop('disabled', true);
// 서버에 퀴즈 참여 요청 전송
$.ajax({
url: '/sopmal/bnft/participate',
method: 'POST',
contentType: 'application/json',
data: JSON.stringify({
bnfInnb: screenInfo.basicInfo.bnfInnb, // 혜택 고유 번호
bnfQuizInnb: screenInfo.quizInfoList[screenInfo.currentQuiz - 1].bnfQuizInnb, // 퀴즈 고유 번호
answCn: selectedAnswer // 선택한 답변(O/X)
}),
success: handleQuizResponse,
error: function(xhr, status, error) {
$resultButton.prop('disabled', false);
handleQuizError(xhr, status, error);
}
});
}
/**
* 퀴즈 응답 처리 함수
* 서버로부터 받은 퀴즈 결과를 처리하고 UI 업데이트
* @param {Object} response - 서버 응답 객체
*/
function handleQuizResponse(response) {
if (response.success) {
updateQuizState(response);
showResultModal(response);
} else {
showCustomAlert(response.message || '퀴즈 참여에 실패했습니다.');
}
}
/**
* 퀴즈 오류 처리 함수
* 서버 요청 중 발생한 오류를 처리하고 사용자에게 안내
* @param {Object} xhr - XMLHttpRequest 객체
* @param {string} status - 요청 상태
* @param {string} error - 오류 메시지
*/
function handleQuizError(xhr, status, error) {
console.error('퀴즈 참여 실패:', error);
let errorMessage = '퀴즈 참여 중 오류가 발생했습니다. ';
if (xhr.status === 404) {
errorMessage += '퀴즈를 찾을 수 없습니다. 페이지를 새로고침 해주세요.';
} else if (xhr.status === 403) {
errorMessage += '권한이 없습니다. 로그인 상태를 확인해주세요.';
} else {
errorMessage += '잠시 후 다시 시도해주세요.';
}
showCustomAlert(errorMessage);
}
/**
* 결과 모달 표시 함수
* 퀴즈 결과에 따라 적절한 모달 팝업을 표시
* @param {Object} response - 서버 응답 객체
*/
function showResultModal(response) {
let modalId;
// 보상 코드에 따른 모달 선택 (수정)
if (response.rewardCodes && response.rewardCodes.includes('13030')) {
modalId = 'modal-3'; // 모든 퀴즈 정답
} else if (response.isCorrect) {
modalId = 'modal-1'; // 정답
} else {
modalId = 'modal-2'; // 오답
}
// 모달 내용 업데이트
const $modal = $(`#${modalId}`);
const $content = $modal.find('.microModal__content');
// 퀴즈 번호와 정답 업데이트
$content.find('.sub_tit:first').html(
`${screenInfo.currentQuiz}번째 퀴즈, 정답은 <b class="c_blue">${response.correctAnswer}</b>입니다.`
);
// 마일리지 금액 업데이트 (수정)
if (response.rewardInfos && response.rewardInfos.length > 0) {
// 모든 보상의 마일리지 합계 계산
const totalMileage = response.rewardInfos.reduce((total, reward) => {
return total + (reward.mlgRwdAmt || 0);
}, 0);
if (totalMileage > 0) {
$content.find('.sub_tit:eq(1)').html(
`마일리지 ${totalMileage.toLocaleString()}원 획득`
);
}
}
// 다음 퀴즈 안내 메시지 업데이트
const lastMessage = screenInfo.currentQuiz < screenInfo.quizCount
? `${screenInfo.currentQuiz + 1}번째 퀴즈도 내일 꼭! 도전하세요.`
: '모든 퀴즈에 도전해 주셔서 감사합니다!';
$content.find('p:last').text(lastMessage);
// 모달 표시 및 닫힐 때 UI 업데이트
MicroModal.show(modalId, {
onClose: function() {
updateQuizUI();
}
});
}
/**
* 알림 설정 토글 함수
* 퀴즈 알림 신청/취소 처리
*/
function handleNotification() {
const $btn = $(this);
const currentStatus = $btn.data('status') === 'on';
const newStatus = !currentStatus;
// 알림 상태 변경 API 호출
$.ajax({
url: '/sopmal/bnft/updateoxquizNotificationStatus',
method: 'POST',
contentType: 'application/json',
data: JSON.stringify({
bnfInnb: screenInfo.basicInfo.bnfInnb, // 혜택 고유 번호
agreAt: newStatus ? 'Y' : 'N' // 알림 동의 여부(Y/N)
}),
success: function(response) {
if (response.success) {
updateNotificationButton(response.notificationStatus);
showCustomAlert(response.notificationStatus ?
"알림 신청이 완료되었습니다." :
"알림 신청이 해제되었습니다.");
} else {
showCustomAlert("알림 상태 변경에 실패했습니다.\n잠시 후 다시 시도해주세요.");
}
},
error: function(xhr, status, error) {
console.error('알림 상태 변경 실패:', error);
showCustomAlert('알림 상태 변경 중 오류가 발생했습니다.');
}
});
}
/**
* 알림 상태 조회 함수
* 서버에서 현재 알림 설정 상태를 조회하여 UI 업데이트
*/
function getNotificationStatus() {
$.ajax({
url: '/sopmal/bnft/getoxquizNotificationStatus',
method: 'POST',
contentType: 'application/json',
data: JSON.stringify({ bnfInnb: screenInfo.basicInfo.bnfInnb }),
success: function(response) {
updateNotificationButton(response.notificationStatus);
},
error: function(xhr, status, error) {
console.error('알림 상태 조회 실패:', error);
}
});
}
/**
* 알림 버튼 UI 업데이트 함수
* 알림 상태에 따라 버튼 스타일과 텍스트 변경
* @param {boolean} isNotificationOn - 알림 활성화 여부
*/
function updateNotificationButton(isNotificationOn) {
const $notificationBtn = $('#notificationBtn');
const $notificationText = $notificationBtn.find('#notificationText');
// 아이콘을 항상 업데이트
const iconClass = isNotificationOn ? 'ico_alarm2' : 'ico_alarm2';
$notificationBtn.find('i').remove(); // 기존 아이콘 제거
$notificationBtn.prepend('<i class="' + iconClass + '"></i>'); // 새로운 아이콘 추가
// 버튼 텍스트와 스타일 업데이트
if (isNotificationOn) {
$notificationText.text('알림 신청 완료');
$notificationBtn
.data('status', 'on')
.removeClass('outline');
} else {
$notificationText.text('알림 신청');
$notificationBtn
.data('status', 'off')
.addClass('outline');
}
}
/**
* 퀴즈 상태 업데이트 함수
* 퀴즈 결과에 따라 내부 상태 데이터 업데이트
* @param {Object} quizResult - 퀴즈 결과 객체
*/
function updateQuizState(quizResult) {
screenInfo.quizInfoList[screenInfo.currentQuiz - 1].userAnswer = quizResult.userAnswer;
screenInfo.quizInfoList[screenInfo.currentQuiz - 1].isCorrect = quizResult.isCorrect ? 'O' : 'X';
updateQuizUI();
}
/**
* 퀴즈 UI 업데이트 함수
* 현재 퀴즈 상태에 맞게 화면 요소 업데이트
*/
function updateQuizUI() {
// 퀴즈 스탬프 업데이트
$('.quizStamp li').each(function(index) {
const quizInfo = screenInfo.quizInfoList[index];
$(this).removeClass('on fail location');
if (quizInfo.isCorrect === 'O') {
$(this).addClass('on');
} else if (quizInfo.isCorrect === 'X') {
$(this).addClass('on fail');
}
if (index + 1 === screenInfo.currentQuiz) {
$(this).addClass('location');
}
});
// 현재 퀴즈 내용 업데이트
const currentQuiz = screenInfo.quizInfoList[screenInfo.currentQuiz - 1];
$('.quizBox .txt').text(currentQuiz.quizCn);
// 선택 초기화
resetQuizSelection();
// OX 버튼 상태 업데이트 (추가)
const $oxButtons = $('.qzSelect a');
if (currentQuiz.userAnswer !== null) {
// 이미 완료된 퀴즈: OX 버튼 비활성화 및 스타일 변경
$oxButtons.addClass('disabled')
.css({
'opacity': '0.5',
'pointer-events': 'none',
'cursor': 'not-allowed'
});
// 사용자가 선택한 답변 표시
$oxButtons.filter(`[data-answer="${currentQuiz.userAnswer}"]`).addClass('on');
} else {
// 미완료 퀴즈: OX 버튼 활성화
$oxButtons.removeClass('disabled')
.css({
'opacity': '1',
'pointer-events': 'auto',
'cursor': 'pointer'
});
}
// 결과확인 버튼 상태 업데이트
const $resultButton = $('.bottomBtnControls button:last-child');
if (currentQuiz.userAnswer !== null) {
$resultButton.text('도전완료')
.prop('disabled', true)
.removeClass('theme')
.addClass('gray');
} else {
$resultButton.text('결과확인')
.prop('disabled', false)
.removeClass('gray')
.addClass('theme');
}
// 힌트 버튼 이벤트 리스너 재설정
$('#hintButton').off('click').on('click', function() {
const hintUrl = screenInfo.quizInfoList[screenInfo.currentQuiz - 1].hintUrl;
openExternalUrl(hintUrl);
});
}
/**
* 다음 퀴즈로 이동 함수
* 현재 퀴즈에서 다음 퀴즈로 이동
*/
function moveToNextQuiz() {
if (screenInfo.currentQuiz < screenInfo.quizCount) {
screenInfo.currentQuiz++;
updateQuizUI();
} else {
showCustomAlert('모든 퀴즈를 완료하셨습니다!');
}
}
/**
* 퀴즈 선택 초기화 함수
* OX 선택 상태를 초기화
*/
function resetQuizSelection() {
$('.qzSelect a').removeClass('on');
}
/**
* 힌트 모달 표시 함수
* 퀴즈 힌트를 모달 팝업으로 표시
* @param {string} hintUrl - 힌트 내용을 가져올 URL
*/
function showHintModal(hintUrl) {
window.location.href = hintUrl;
}
/**
* 힌트 페이지로 이동하는 함수
* 힌트 URL이 상대경로인지 절대경로인지 확인하고 적절히 처리
* @param {string} hintUrl - 힌트 URL
*/
function goToHintPage(hintUrl) {
// URL이 http:// 또는 https://로 시작하지 않으면 절대 경로로 변환
if (hintUrl && !hintUrl.match(/^https?:\/\//i)) {
// URL이 /로 시작하지 않으면 /를 추가
if (!hintUrl.startsWith('/')) {
hintUrl = '/' + hintUrl;
}
}
// 새 창으로 URL 열기 (웹뷰에서도 작동)
window.open(hintUrl, '_blank');
}
/**
* 외부 URL을 여는 함수
* a 태그를 동적으로 생성하여 새 창에서 URL 열기
* @param {string} url - 열고자 하는 URL
*/
function openExternalUrl(url) {
// URL이 http나 https로 시작하지 않으면 https 프로토콜 추가
if (url && !url.match(/^https?:\/\//i)) {
url = 'https://' + url;
}
console.log('Opening URL:', url);
// a 태그 동적 생성
const link = document.createElement('a');
link.href = url;
link.target = '_blank';
link.rel = 'noopener noreferrer';
// DOM에 추가하고 클릭 이벤트 발생
document.body.appendChild(link);
link.click();
// DOM에서 제거
setTimeout(() => {
document.body.removeChild(link);
}, 100);
}
// 공개 API 반환
return {
init: init,
openExternalUrl: openExternalUrl
};
})();
// 문서 로드 완료 시 OXQuiz 모듈 초기화
$(document).ready(function() {
OXQuiz.init();
});