Files
sake/build/resources/main/static/mobile/js/bnft/purstmp/PurstmpStamp.js

559 lines
19 KiB
JavaScript

/**
* PurstmpStamp.js
*
* 구매 스탬프 기능을 구현한 모듈
* 사용자가 스탬프를 적립하고 교환권을 발급받는 기능 제공
*
* 주요 기능:
* 1. 스탬프 카드 슬라이더 UI 구현
* 2. 스탬프 상태 확인 및 업데이트
* 3. 모달 팝업 관리
* 4. 스탬프 새로고침 기능
* 5. 이달의 선물 정보 표시
*/
// 전역 변수 제거 - window.screenInfo 사용
// let screenInfo = null; // 이 줄 삭제
// 문서가 로드되면 초기화 함수 실행
document.addEventListener('DOMContentLoaded', function() {
// window.screenInfo가 JSP에서 설정되었는지 확인
if (window.screenInfo) {
init(); // JSP에서 데이터가 이미 설정된 경우 바로 초기화
} else {
loadScreenData(); // Ajax로 데이터 로드
}
});
/**
* 화면 데이터 로드 함수
* Ajax로 서버에서 스탬프 정보를 가져옴
*/
function loadScreenData() {
// 로딩 표시
showLoading();
$.ajax({
url: '/sopmal/bnft/getStampData',
method: 'POST',
contentType: 'application/json',
data: JSON.stringify({}), // 빈 객체 전송 (인증은 세션으로 처리)
dataType: 'json',
success: function(response) {
try {
if (response.success) {
window.screenInfo = response.data;
// stampList 데이터 추출 (BO 서버 응답 구조에 맞춰)
if (window.screenInfo && window.screenInfo.stampList) {
window.screenInfo = window.screenInfo.stampList;
// couponList를 coupons로 매핑 (기존 코드와의 호환성 유지)
if (window.screenInfo.couponList) {
window.screenInfo.coupons = window.screenInfo.couponList;
}
}
init(); // 데이터 로드 완료 후 초기화
} else {
showError(response.message || '데이터를 불러오는데 실패했습니다.');
}
} catch (error) {
console.error('데이터 처리 중 오류:', error);
showError('데이터 처리 중 오류가 발생했습니다.');
}
},
error: function(xhr, status, error) {
console.error('Ajax 오류:', error);
showError('서버와의 통신 중 오류가 발생했습니다.');
},
complete: function() {
hideLoading();
}
});
}
/**
* 로딩 표시 함수
*/
function showLoading() {
$('.stampCardWrap').html('<div class="loading-spinner">로딩중...</div>');
}
/**
* 로딩 숨김 함수
*/
function hideLoading() {
$('.loading-spinner').remove();
}
/**
* 에러 표시 함수
*/
function showError(message) {
$('.stampCardWrap').html(`<div class="error-message">${message}</div>`);
}
/**
* 모듈 초기화 함수
* 필요한 모든 컴포넌트와 이벤트 리스너 초기화
*/
function init() {
if (!window.screenInfo) {
console.error('screenInfo가 없습니다.');
return;
}
initStampCardSlider(); // 스탬프 카드 슬라이더 초기화
initModal(); // 모달 팝업 초기화
updateTotalStamps(); // 전체 스탬프 수 업데이트
checkStampStatus(); // 스탬프 상태 확인
updateRewardInfo(); // 이달의 선물 정보 업데이트
// 새로고침 버튼 클릭 이벤트 처리
// init() 함수가 여러 번 호출될 수 있으므로, 이벤트 중복 등록을 방지하기 위해 .off()를 추가합니다.
$('.stampCardWrap').off('click.reply').on('click.reply', '.btn_reply', function(e) {
e.preventDefault();
e.stopPropagation(); // 슬라이드 이벤트와 충돌 방지
var couponIndex = $(this).closest('.stampCard').data('coupon-index');
refreshStamps(couponIndex);
});
}
/**
* 스탬프 상태 확인 함수
* 스탬프가 없거나 교환권이 모두 사용된 경우 UI 변경
*/
function checkStampStatus() {
if (!window.screenInfo) return;
// 데이터가 없는 경우 또는 교환권이 모두 사용된 경우
if (!window.screenInfo.coupons || window.screenInfo.coupons.length === 0) {
// 예시에 맞는 UI로 변경: 스탬프 카드 형태 유지하되 빈 상태 표시
$('.stampCardWrap').html(`
<div class="stampCard">
<div class="stampCardTop">
<p class="shopName_none">
<span>오프라인 매장 구매시<br>자동으로 스탬프가 적립됩니다.</span>
</p>
</div>
<ul class="stampArea">
<li><span>1</span></li>
<li><span>2</span></li>
<li><span>3</span></li>
<li><span>4</span></li>
<li><span>5</span></li>
<li><span>6</span></li>
<li><span>7</span></li>
<li><span>8</span></li>
<li><span>9</span></li>
<li><span>10</span></li>
</ul>
<div class="stampCardbm">
<p class="giftInfo">스탬프 <b>10개 적립</b>시 이달의 <b>선물 교환권</b>을 드려요.</p>
</div>
</div>
`);
}
}
/**
* 스탬프 카드 슬라이더 초기화 함수
* 스탬프 카드 UI 생성 및 슬라이드 기능 구현 (중복 생성 방지)
*/
function initStampCardSlider() {
if (!window.screenInfo || !window.screenInfo.coupons) {
console.warn('쿠폰 데이터가 없습니다.');
return;
}
const $stampCardWrap = $('.stampCardWrap');
const couponCount = window.screenInfo.coupons.length;
if (couponCount === 0) {
return;
}
// ✨ 가장 중요한 부분: 이미 카드가 생성되었다면, 함수를 즉시 종료하여 다시 그리지 않습니다.
if ($stampCardWrap.find('.stampCard').length > 0) {
console.log('카드가 이미 존재하므로 재생성하지 않습니다.');
return;
}
let currentCouponIndex = 0;
let startX, moveX;
/**
* 모든 쿠폰 카드를 미리 생성하는 함수
*/
function createAllCouponCards() {
// 이 함수는 최초 1회만 호출되므로, 이전처럼 .empty()를 호출할 필요가 없습니다.
window.screenInfo.coupons.forEach((coupon, index) => {
const $card = createCouponCard(coupon, index);
$stampCardWrap.append($card);
});
// 페이지네이션 생성
if (couponCount > 1) {
const $pagination = $('<div class="pagination"></div>');
for (let i = 0; i < couponCount; i++) {
const $button = $(`<button data-index="${i}"></button>`);
$pagination.append($button);
}
$stampCardWrap.append($pagination);
}
}
/**
* 쿠폰 카드 생성 함수
*/
function createCouponCard(coupon, index) {
// 교환권 완료 상태에 따른 메시지 결정
let giftMessage = '';
if (coupon.extiketStmpCnt === 10 && coupon.exchngAt === 'Y') {
giftMessage = '<span class="exchange-completed">✅ 교환권 발급 완료! 쿠폰함에서 확인하세요</span>';
} else if (coupon.extiketStmpCnt === 10) {
giftMessage = '<span class="exchange-ready">🎁 교환권 발급 준비완료!</span>';
} else {
giftMessage = `스탬프 <b>10개 적립</b>시 이달의 <b>선물 교환권</b>을 드려요.`;
}
return $(`
<div class="stampCard" data-coupon-index="${index}" style="display: none;">
<div class="stampCardTop">
<p class="shopName">
<span>${coupon.branchName || coupon.storeName || '매장명 미확인'}</span>
<a href="javascript:void(0);" class="btn_reply">새로고침</a>
</p>
<p class="cardCnt"><b>${coupon.extiketStmpCnt || 0}</b> / 10</p>
</div>
<ul class="stampArea">
${Array(10).fill().map((_, i) => `<li class="${i < (coupon.extiketStmpCnt || 0) ? 'on' : ''}"><span>${i + 1}</span></li>`).join('')}
</ul>
<div class="stampCardbm">
<p class="useDate">유효기간 ${formatDate(coupon.stmpValidDt)} 까지</p>
<p class="giftInfo">${giftMessage}</p>
</div>
</div>
`);
}
/**
* 스탬프 카드 표시 함수 (참조 코드 방식 적용)
*/
function showSlide(index) {
$('.stampCard').hide();
$('.stampCard').eq(index).fadeIn();
$('.pagination button').removeClass('active');
$('.pagination button').eq(index).addClass('active');
}
/**
* 페이지네이션 생성 함수
*/
function createPagination() {
if (couponCount <= 1) return;
const $pagination = $('.pagination');
if ($pagination.length === 0) {
const $newPagination = $('<div class="pagination"></div>');
for (let i = 0; i < couponCount; i++) {
$newPagination.append(`<button data-index="${i}"></button>`);
}
$stampCardWrap.append($newPagination);
}
}
/**
* 다음 슬라이드 표시
*/
function nextSlide() {
currentCouponIndex = (currentCouponIndex + 1) % couponCount;
showSlide(currentCouponIndex);
}
/**
* 이전 슬라이드 표시
*/
function previousSlide() {
currentCouponIndex = (currentCouponIndex - 1 + couponCount) % couponCount;
showSlide(currentCouponIndex);
}
// 페이지네이션 버튼 클릭 이벤트
$stampCardWrap.on('click', '.pagination button', function(e) {
e.stopPropagation();
currentCouponIndex = $(this).data('index');
showSlide(currentCouponIndex);
});
// 카드 클릭 이벤트 (참조 코드 방식)
$stampCardWrap.on('click', function(e) {
if (couponCount <= 1) return;
// 새로고침 버튼 클릭인 경우 슬라이드 이벤트 무시
if ($(e.target).hasClass('btn_reply') || $(e.target).closest('.btn_reply').length > 0) {
return;
}
const clickX = e.clientX;
const wrapWidth = $stampCardWrap.width();
const halfWidth = wrapWidth / 2;
if (clickX < halfWidth) {
previousSlide();
} else {
nextSlide();
}
});
// 터치 이벤트
$stampCardWrap.on('touchstart', function(e) {
startX = e.touches[0].clientX;
});
$stampCardWrap.on('touchmove', function(e) {
moveX = e.touches[0].clientX;
});
$stampCardWrap.on('touchend', function(e) {
if (couponCount <= 1) return;
if (startX - moveX > 50) {
nextSlide();
} else if (moveX - startX > 50) {
previousSlide();
}
});
// 슬라이더 초기화
if (couponCount > 0) {
createAllCouponCards();
createPagination();
showSlide(currentCouponIndex);
}
}
/**
* 날짜 포맷 변환 함수
* 날짜 문자열을 YYYY.MM.DD 형식으로 변환
*
* @param {string} dateString - 변환할 날짜 문자열
* @returns {string} 포맷된 날짜 문자열
*/
function formatDate(dateString) {
const date = new Date(dateString);
return `${date.getFullYear()}.${(date.getMonth() + 1).toString().padStart(2, '0')}.${date.getDate().toString().padStart(2, '0')}`;
}
/**
* 모달 초기화 함수
* MicroModal 라이브러리를 사용한 모달 팝업 초기화
*/
function initModal() {
MicroModal.init({
disableScroll: true,
awaitCloseAnimation: true,
});
// 모달 트리거 버튼 클릭 이벤트
$('.fullscreen-trigger').on('click', function(e) {
e.preventDefault();
var modalId = $(this).attr('data-micromodal-trigger');
// 전역 openModal 함수 사용
if (typeof window.openModal === 'function') {
window.openModal(modalId);
} else {
MicroModal.show(modalId);
}
});
// 모달 닫기 버튼 클릭 이벤트
$('#modal-close-btn').on('click', function(e) {
e.preventDefault();
e.stopPropagation();
console.log('모달 닫기 버튼 클릭됨'); // 디버깅용
window.location.href = '/sopmal/bnft/bnftMain';
});
// 브라우저 뒤로가기 버튼 처리
window.addEventListener('popstate', function(event) {
var modalId = window.location.hash.replace('#', '');
if (modalId) {
// 전역 closeModal 함수 사용
if (typeof window.closeModal === 'function') {
window.closeModal(modalId);
} else {
MicroModal.close(modalId);
}
} else {
$('.microModal').each(function() {
MicroModal.close($(this).attr('id'));
});
}
document.body.style.overflow = '';
});
// URL 해시가 있는 경우 해당 모달 열기
if (window.location.hash) {
var modalId = window.location.hash.replace('#', '');
// 전역 openModal 함수 사용
if (typeof window.openModal === 'function') {
window.openModal(modalId);
} else {
MicroModal.show(modalId);
}
}
}
/**
* 토스트 메시지 표시 함수
*
* @param {string} message - 표시할 메시지
*/
function showToast(message) {
const $toastPop = $('#modal_exchange_ticket_toast');
$toastPop.text(message);
$toastPop.addClass('show');
setTimeout(() => {
$toastPop.removeClass('show');
}, 2000);
}
/**
* 스탬프 새로고침 함수 (토스트 제거)
*/
function refreshStamps(couponIndex) {
if (!window.screenInfo || !window.screenInfo.coupons || !window.screenInfo.coupons[couponIndex]) {
showToast('쿠폰 정보를 찾을 수 없습니다.');
return;
}
const coupon = window.screenInfo.coupons[couponIndex];
$.ajax({
url: '/sopmal/bnft/refreshStamps',
method: 'POST',
data: JSON.stringify({
bnfInnb: window.screenInfo.bnfInnb || '',
strInnb: coupon.strInnb || '',
bnfExtiketInnb: coupon.bnfExtiketInnb || '',
mbrInnb: window.screenInfo.mbrInnb || '',
mbrDvCd: window.screenInfo.mbrDvCd || '',
sopmalCd: window.screenInfo.sopmalCd || ''
}),
contentType: 'application/json',
success: function(response) {
try {
if (response && response.resBody && response.resBody.result) {
const responseData = JSON.parse(response.resBody.result);
if (responseData.success) {
// ⚠️ 매장명이 null인 경우 기존 매장명 유지
if (!responseData.cardInfo.branchName) {
responseData.cardInfo.branchName = coupon.storeName || coupon.branchName || '매장명 미확인';
}
updateStampCard(couponIndex, responseData);
// ❌ 토스트 제거: showToast('스탬프 정보가 새로고침되었습니다.');
} else {
showToast(responseData.message || '스탬프 정보를 새로고침하는데 실패했습니다.');
}
} else {
showToast('응답 형식이 올바르지 않습니다.');
}
} catch (error) {
console.error('응답 처리 중 오류:', error);
showToast('응답을 처리하는 동안 오류가 발생했습니다.');
}
},
error: function(xhr, status, error) {
console.error('새로고침 Ajax 오류:', error);
showToast('스탬프 정보를 새로고침하는데 실패했습니다.');
}
});
}
/**
* 스탬프 카드 업데이트 함수 - 카드 틀은 유지하고 내용만 업데이트
*/
function updateStampCard(couponIndex, data) {
// 데이터 업데이트
Object.assign(window.screenInfo.coupons[couponIndex], data.cardInfo);
const coupon = window.screenInfo.coupons[couponIndex];
const $card = $(`.stampCard[data-coupon-index="${couponIndex}"]`);
// 1. 스탬프 개수 및 시각적 표시 업데이트
const newStampCount = coupon.extiketStmpCnt || 0;
$card.find('.cardCnt b').text(newStampCount);
$card.find('.stampArea li').each((index, el) => {
$(el).toggleClass('on', index < newStampCount);
});
// 2. 지점명 업데이트
const branchName = coupon.branchName || coupon.storeName || '매장명 미확인';
$card.find('.shopName span').text(branchName);
// 3. 유효기간 업데이트
if (coupon.stmpValidDt) {
$card.find('.useDate').text(`유효기간 ${formatDate(coupon.stmpValidDt)} 까지`);
}
// 4. 교환권 상태 메시지 업데이트
let giftMessage = '';
if (coupon.extiketStmpCnt === 10 && coupon.exchngAt === 'Y') {
giftMessage = '<span class="exchange-completed">✅ 교환권 발급 완료! 쿠폰함에서 확인하세요</span>';
} else if (coupon.extiketStmpCnt === 10) {
giftMessage = '<span class="exchange-ready">🎁 교환권 발급 준비완료!</span>';
} else {
giftMessage = `스탬프 <b>10개 적립</b>시 이달의 <b>선물 교환권</b>을 드려요.`;
}
$card.find('.giftInfo').html(giftMessage);
// 5. 부드러운 업데이트 효과 (CSS 애니메이션 필요)
$card.find('.stampArea').addClass('updated');
setTimeout(() => {
$card.find('.stampArea').removeClass('updated');
}, 300);
updateTotalStamps();
}
/**
* 전체 스탬프 수 업데이트 함수
* 화면에 표시된 전체 쿠폰 수 업데이트
*/
function updateTotalStamps() {
const totalCoupons = window.screenInfo.coupons.length;
$('.card_title b').text(totalCoupons);
window.screenInfo.totalStamps = totalCoupons;
}
/**
* 이달의 선물 정보 업데이트 함수
* 서버에서 받은 purstmpScreenInfo 데이터로 UI 업데이트
*/
function updateRewardInfo() {
if (!window.screenInfo || !window.screenInfo.purstmpScreenInfo || !window.screenInfo.purstmpScreenInfo[0]) {
console.warn('이달의 선물 정보가 없습니다.');
return;
}
const rewardInfo = window.screenInfo.purstmpScreenInfo[0];
// 이달의 선물 이름 설정 (cnSj)
if (rewardInfo.cnSj) {
$('#rewardName').text(rewardInfo.cnSj);
}
// 이달의 선물 이미지 설정 (imgFilCours)
if (rewardInfo.imgFilCours) {
const imageSrc = window.cdnUrl + rewardInfo.imgFilCours;
$('#rewardImage').attr('src', imageSrc);
}
// 이벤트 설명 설정 (wordsCn) - HTML 형태로 삽입
if (rewardInfo.wordsCn) {
$('#eventDescription').html(rewardInfo.wordsCn);
}
}