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,200 @@
/**
* 섭취챌린지 관련 스크립트
* 최근 1년 이내 구매한 제품과 위시리스트 저장 제품 중에서
* 섭취챌린지에 추가할 제품을 선택하는 기능을 제공합니다.
*/
$(document).ready(function() {
// 변수 선언
const $purchasedTab = $('#purchased-tab');
const $wishlistTab = $('#wishlist-tab');
const $purchasedList = $('.challenge_checklist.purchased');
const $wishlistList = $('.challenge_checklist.wishlist');
const $addToChallengeBtn = $('#addToChallengeBtn');
const bnfInnb = $('#bnfInnbHidden').val();
/**
* 탭 전환 처리
* 구매한 제품 탭과 위시리스트 탭 간 전환 기능
*/
$purchasedTab.on('click', function(e) {
e.preventDefault();
// 탭 활성화 상태 변경
$purchasedTab.addClass('active');
$wishlistTab.removeClass('active');
// 리스트 표시 상태 변경
$purchasedList.show();
$wishlistList.hide();
});
$wishlistTab.on('click', function(e) {
e.preventDefault();
// 탭 활성화 상태 변경
$wishlistTab.addClass('active');
$purchasedTab.removeClass('active');
// 리스트 표시 상태 변경
$wishlistList.show();
$purchasedList.hide();
});
/**
* 제품 체크박스 상태 관리
* 체크박스 토글 시 시각적 구분 처리
*/
$('.challenge_checklist input[type="checkbox"]').on('change', function() {
const $checkboxItem = $(this).closest('li');
if ($(this).is(':checked')) {
$checkboxItem.addClass('selected');
} else {
$checkboxItem.removeClass('selected');
}
});
/**
* 섭취챌린지 제품 추가
* 선택한 제품들을 섭취챌린지에 추가하는 기능
*/
$addToChallengeBtn.on('click', function() {
// 선택된 제품 ID 수집
const selectedProducts = [];
// 구매 제품 탭에서 선택된 제품 처리
$('.challenge_checklist.purchased input[type="checkbox"]:checked').each(function() {
selectedProducts.push({
goodsInnb: $(this).val(),
originDvCd: '01' // 구매 제품 출처 코드
});
});
// 위시리스트 탭에서 선택된 제품 처리
$('.challenge_checklist.wishlist input[type="checkbox"]:checked').each(function() {
selectedProducts.push({
goodsInnb: $(this).val(),
originDvCd: '02' // 위시리스트 출처 코드
});
});
// 선택된 제품이 없는 경우 알림 표시
if (selectedProducts.length === 0) {
showCustomAlert('섭취챌린지에 추가할 제품을 선택해주세요.');
return;
}
// 확인 팝업 표시
showConfirmModal('선택한 제품을 섭취챌린지에 추가하시겠습니까?', function() {
// API 호출 데이터 준비
const requestData = {
bnfInnb: bnfInnb,
products: selectedProducts
};
// API 호출
$.ajax({
url: '/sopmal/bnft/addSelectedProducts',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify(requestData),
success: function(response) {
// 응답 구조 파싱
try {
// result 필드가 문자열 형태의 JSON이므로 파싱
const resultObj = JSON.parse(response.resBody.result);
if (resultObj.success) {
// 성공 시 처리
showCustomAlert(resultObj.message || '제품이 섭취챌린지에 추가되었습니다.');
// 추가된 제품을 목록에서 제거
removeAddedProducts(selectedProducts);
} else {
// 실패 시 처리
showCustomAlert(resultObj.message || '제품 추가 중 오류가 발생했습니다.');
}
} catch (e) {
// JSON 파싱 오류 처리
showCustomAlert('응답 처리 중 오류가 발생했습니다.');
console.error('응답 파싱 오류:', e);
}
},
error: function() {
showCustomAlert('서버 통신 중 오류가 발생했습니다.');
}
});
});
});
/**
* 추가된 제품을 목록에서 제거
* @param {Array} productIds - 제거할 제품 ID 배열
*/
function removeAddedProducts(productIds) {
productIds.forEach(function(productId) {
$(`.challenge_checklist input[value="${productId.goodsInnb}"]`).closest('li').remove();
});
}
/**
* 확인 모달 표시
* @param {string} message - 표시할 메시지
* @param {Function} confirmCallback - 확인 버튼 클릭 시 실행할 콜백 함수
*/
function showConfirmModal(message, confirmCallback) {
// 모달 내용 설정
$('#modal-1 .microModal__title').html(message);
// 모달 버튼 영역 수정
$('#modal-1 .microModal__btnContainer').html(`
<button class="microModal__btn" id="modal-confirm-cancel" aria-label="취소">취소</button>
<button class="microModal__btn microModal__btn-primary" id="modal-confirm-ok">확인</button>
`);
// 기존 이벤트 핸들러 제거 후 새로 등록
$('#modal-confirm-ok').off('click').on('click', function() {
MicroModal.close('modal-1');
if (typeof confirmCallback === 'function') {
confirmCallback();
}
});
// 취소 버튼 이벤트
$('#modal-confirm-cancel').off('click').on('click', function() {
MicroModal.close('modal-1');
});
// 모달 표시
MicroModal.show('modal-1');
}
/**
* 커스텀 알림 표시 함수
* @param {string} message - 표시할 메시지
*/
function showCustomAlert(message) {
// 모달 내용 설정
$('#modal-1 .microModal__title').html(message);
// 모달 버튼 영역 수정
$('#modal-1 .microModal__btnContainer').html(`
<button class="microModal__btn" id="continueAdding" aria-label="계속추가">계속추가</button>
<button class="microModal__btn microModal__btn-primary" id="startChallenge">챌린지 시작</button>
`);
// 기존 이벤트 핸들러 제거 후 새로 등록
$('#continueAdding').off('click').on('click', function() {
MicroModal.close('modal-1');
});
$('#startChallenge').off('click').on('click', function() {
MicroModal.close('modal-1');
// 챌린지 페이지로 이동 (bnfInnb 파라미터 추가)
window.location.href = '/sopmal/bnft/intakchlDailyRecordMain?bnfInnb=' + bnfInnb;
});
// 모달 표시
MicroModal.show('modal-1');
}
});

View File

@@ -0,0 +1,473 @@
/**
* 섭취챌린지 알림설정 관련 JavaScript
*
* 이 파일은 섭취챌린지 알림설정 기능을 구현한 JavaScript 파일입니다.
* 주요 기능:
* 1. 알림 시간 설정 - 타임피커를 통해 시간, 분, 오전/오후 설정
* 2. 알림 활성화/비활성화 - 토글 스위치를 통한 알림 설정 관리
* 3. 미완료 알림 설정 - 섭취 미완료 시 알림 설정 기능
* 4. 서버 통신 - AJAX를 통한 알림 설정 정보 저장 및 조회
*
* 사용 방법:
* - 페이지 로드 시 자동으로 초기화되어 기존 알림 설정을 불러옴
* - 사용자가 알림 시간을 변경하면 서버에 자동으로 저장됨
* - 알림 토글 변경 시 즉시 서버에 반영됨
*
*/
const Intakchl = (() => {
// 전역 변수
let currentNotificationId = null;
let contextPath = '';
// 초기화
function init() {
setupTimepicker();
setupHeaderTitle();
initializeAlerts();
initializeIncompleteNotification();
setupMainAlarmToggle();
renderTimeNotifications();
// MicroModal 초기화 추가
if(typeof MicroModal !== 'undefined') {
MicroModal.init({
disableScroll: true,
awaitCloseAnimation: true
});
} else {
console.error('MicroModal 라이브러리가 로드되지 않았습니다');
}
}
// 헤더 타이틀 설정
function setupHeaderTitle() {
$(".header__title").text("알림설정");
}
// 알림 메시지 초기화
function initializeAlerts() {
if (notificationData.alertMessage) {
showCustomAlert(notificationData.alertMessage);
if (notificationData.goBack === 'Y') {
$('#modal-alert .microModal__btn-primary').on('click', function() {
window.history.back();
});
}
}
}
// 미완료 알림 설정 초기화
function initializeIncompleteNotification() {
if (notificationData.incompleteNotification) {
const useAt = notificationData.incompleteNotification.useAt === "Y";
$("#mainAlarmToggle").prop("checked", useAt);
}
}
// 메인 알림 토글 설정
function setupMainAlarmToggle() {
$("#mainAlarmToggle").on("change", function() {
updateIncompleteNotification($(this).is(":checked") ? "Y" : "N");
});
}
// 타임피커 설정
function setupTimepicker() {
// 시간 옵션 생성
const hourSelect = document.getElementById('hour-select');
hourSelect.innerHTML = '';
for (let i = 1; i <= 12; i++) {
const option = document.createElement('option');
option.value = i.toString().padStart(2, '0');
option.textContent = i.toString().padStart(2, '0');
hourSelect.appendChild(option);
}
// 분 옵션 생성
const minuteSelect = document.getElementById('minute-select');
minuteSelect.innerHTML = '';
for (let i = 0; i < 60; i += 10) {
const option = document.createElement('option');
option.value = i.toString().padStart(2, '0');
option.textContent = i.toString().padStart(2, '0');
minuteSelect.appendChild(option);
}
// 셀렉트 박스가 포커스를 잃었을 때 원래 값으로 돌아가기
['ampm', 'hour', 'minute'].forEach(type => {
const select = document.getElementById(`${type}-select`);
select.addEventListener('blur', () => updateValue(type));
});
}
// 타임피커 값 조정
function adjustValue(type, direction) {
const elem = document.getElementById(type);
const select = document.getElementById(`${type}-select`);
if (type === 'ampm') {
// 오전/오후 전환
elem.textContent = elem.textContent === '오전' ? '오후' : '오전';
} else if (type === 'hour') {
// 1~12 사이에서 순환
let currentValue = parseInt(elem.textContent, 10);
currentValue = (currentValue + direction + 12 - 1) % 12 + 1;
elem.textContent = currentValue.toString().padStart(2, '0');
} else if (type === 'minute') {
// 0~50 사이에서 10분 단위로 순환
let currentValue = parseInt(elem.textContent, 10);
currentValue = (currentValue + direction * 10 + 60) % 60;
elem.textContent = currentValue.toString().padStart(2, '0');
}
}
// 셀렉트 박스 표시
function showSelect(type) {
const elem = document.getElementById(type);
const select = document.getElementById(`${type}-select`);
elem.style.display = 'none';
select.style.display = 'inline-block';
select.value = elem.textContent;
select.focus();
}
// 선택된 값으로 업데이트
function updateValue(type) {
const elem = document.getElementById(type);
const select = document.getElementById(`${type}-select`);
elem.textContent = select.value;
elem.style.display = 'inline-block';
select.style.display = 'none';
}
// 미완료 알림 설정 업데이트
function updateIncompleteNotification(useAt) {
if (notificationData.incompleteNotification) {
$.ajax({
url: `${contextPath}/sopmal/bnft/updateIntakeChallengeIncompleteNotification`,
type: "POST",
data: JSON.stringify({
bnfPushEvtSetupInnb: notificationData.incompleteNotification.bnfPushEvtSetupInnb,
useAt: useAt
}),
contentType: "application/json",
success: function(response) {
try {
const result = JSON.parse(response.resBody.result);
if (result.success) {
showCustomAlert("알림 설정이 변경되었습니다.");
} else {
showCustomAlert("알림 설정 변경에 실패했습니다.");
$("#mainAlarmToggle").prop("checked", useAt == "Y" ? false : true);
}
} catch (e) {
console.error("응답 처리 중 오류 발생:", e);
showCustomAlert("알림 설정 변경에 실패했습니다.");
$("#mainAlarmToggle").prop("checked", useAt == "Y" ? false : true);
}
},
error: function() {
showCustomAlert("알림 설정 변경 중 오류가 발생했습니다.");
$("#mainAlarmToggle").prop("checked", useAt == "Y" ? false : true);
}
});
}
}
// 시간 알림 목록 렌더링
function renderTimeNotifications() {
const alarmList = $(".alarmList");
alarmList.empty();
if (notificationData.timeNotifications && notificationData.timeNotifications.length > 0) {
notificationData.timeNotifications.forEach(notification => {
const li = $("<li>");
const time = notification.ntcnTme;
const useAt = notification.useAt === "Y";
const id = notification.bnfNtkNtcnInnb;
li.append('<span><a data-micromodal-trigger="modal-1" data-id="' + id + '" data-time="' + time + '">' + formatTime(time) + '</a></span>');
li.append(
'<fieldset class="toggle">' +
'<label>' +
'<input role="switch" type="checkbox" data-id="' + id + '" class="time-toggle" ' + (useAt ? 'checked' : '') + '>' +
'</label>' +
'</fieldset>'
);
alarmList.append(li);
});
}
// 시간 토글 변경 이벤트 바인딩
$(".time-toggle").on("change", function() {
const id = $(this).data("id");
const useAt = $(this).is(":checked") ? "Y" : "N";
const time = $('a[data-id="' + id + '"]').data("time");
updateTimeNotification(id, useAt, time);
});
// 시간 설정 모달 설정 - 이벤트 버블링 방지 추가
$("[data-micromodal-trigger='modal-1']").on("click", function(e) {
e.preventDefault(); // 기본 동작 방지
e.stopPropagation(); // 이벤트 버블링 방지
const id = $(this).data("id");
const time = $(this).data("time");
setupTimeModal(id, time);
MicroModal.show('modal-1');
});
}
// 시간 표시 포맷 (HH:MM -> 오전/오후 HH:MM)
function formatTime(timeStr) {
if (!timeStr) return "";
const [hours, minutes] = timeStr.split(":");
const hour = parseInt(hours, 10);
if (hour < 12) {
return '오전 ' + (hour == 0 ? '12' : hour) + ':' + minutes;
} else {
return '오후 ' + (hour == 12 ? '12' : hour - 12) + ':' + minutes;
}
}
// 시간 알림 업데이트
function updateTimeNotification(id, useAt, ntcnTme) {
// bnfInnb 파라미터 추출
const urlParams = new URLSearchParams(window.location.search);
const bnfInnb = urlParams.get('bnfInnb');
// 로그 출력
console.log('[알림 설정] bnfInnb 파라미터:', bnfInnb);
// 매개변수 검증 추가
if (!id) {
console.error("알림 ID가 누락되었습니다");
return;
}
// useAt이 없으면 현재 토글 상태에서 가져옴
if (useAt === undefined) {
useAt = $('input[data-id="' + id + '"]').is(":checked") ? "Y" : "N";
}
// ntcnTme이 없으면 현재 시간 데이터에서 가져옴
if (!ntcnTme) {
ntcnTme = $('a[data-id="' + id + '"]').data("time");
}
$.ajax({
url: `${contextPath}/sopmal/bnft/updateIntakeChallengeNotification`,
type: "POST",
data: JSON.stringify({
bnfNtkNtcnInnb: id,
useAt: useAt,
ntcnTme: ntcnTme,
bnfInnb: bnfInnb
}),
contentType: "application/json",
success: function(response) {
try {
// resBody.result가 문자열로 된 JSON이므로 파싱 필요
const result = JSON.parse(response.resBody.result);
if (result.success) {
// 성공적으로 업데이트 됨
} else {
showCustomAlert("알림 설정 변경에 실패했습니다.");
$('input[data-id="' + id + '"]').prop("checked", useAt == "Y" ? false : true);
}
} catch (e) {
console.error("응답 처리 중 오류 발생:", e);
showCustomAlert("알림 설정 변경에 실패했습니다.");
$('input[data-id="' + id + '"]').prop("checked", useAt == "Y" ? false : true);
}
},
error: function() {
showCustomAlert("알림 설정 변경 중 오류가 발생했습니다.");
$('input[data-id="' + id + '"]').prop("checked", useAt == "Y" ? false : true);
}
});
}
// 시간 설정 모달 초기화
function setupTimeModal(id, timeStr) {
currentNotificationId = id;
if (!timeStr) return;
const [hours, minutes] = timeStr.split(":");
const hour = parseInt(hours, 10);
// 오전/오후 설정
const ampm = hour < 12 ? "오전" : "오후";
$("#ampm").text(ampm);
// 시간 설정 (12시간제)
const hour12 = hour == 0 ? 12 : (hour > 12 ? hour - 12 : hour);
$("#hour").text(hour12.toString().padStart(2, '0'));
// 분 설정
$("#minute").text(minutes);
// 모달 저장 버튼 이벤트 바인딩 수정
$("#modal-1 .btn.lg.full.theme").off("click").on("click", function(e) {
e.preventDefault(); // 기본 동작 방지
e.stopPropagation(); // 이벤트 버블링 방지
saveTimeNotification();
// 여기서 모달 닫기는 saveTimeNotification 성공 시 내부에서 처리됨
});
}
// 시간 설정 저장
function saveTimeNotification() {
if (!currentNotificationId) return;
// bnfInnb 파라미터 추출
const urlParams = new URLSearchParams(window.location.search);
const bnfInnb = urlParams.get('bnfInnb');
const ampm = $("#ampm").text();
let hour = parseInt($("#hour").text(), 10);
const minute = $("#minute").text();
// 24시간제로 변환
if (ampm === "오후" && hour !== 12) {
hour += 12;
} else if (ampm === "오전" && hour === 12) {
hour = 0;
}
const timeStr = hour.toString().padStart(2, '0') + ':' + minute;
// 현재 토글 상태 가져오기
const useAt = $('input[data-id="' + currentNotificationId + '"]').is(":checked") ? "Y" : "N";
$.ajax({
url: `${contextPath}/sopmal/bnft/updateIntakeChallengeNotification`,
type: "POST",
data: JSON.stringify({
bnfNtkNtcnInnb: currentNotificationId,
ntcnTme: timeStr,
useAt: useAt, // 토글 상태 추가
bnfInnb: bnfInnb
}),
contentType: "application/json",
success: function(response) {
try {
// resBody.result가 문자열로 된 JSON이므로 파싱 필요
const result = JSON.parse(response.resBody.result);
if (result.success) {
// 시간 표시 업데이트
$('a[data-id="' + currentNotificationId + '"]').text(formatTime(timeStr));
$('a[data-id="' + currentNotificationId + '"]').data("time", timeStr);
MicroModal.close('modal-1'); // 성공 시 모달 닫기
// 디버깅용 알림 추가
console.log("알림 시간이 변경되었습니다: " + formatTime(timeStr));
} else {
showCustomAlert("알림 시간 변경에 실패했습니다.");
}
} catch (e) {
console.error("응답 처리 중 오류 발생:", e);
showCustomAlert("알림 시간 변경에 실패했습니다.");
}
},
error: function() {
showCustomAlert("알림 시간 변경 중 오류가 발생했습니다.");
}
});
}
// 기본 알림시간 초기화
function resetDefaultTimes() {
if (confirm('알림 시간을 기본값으로 초기화하시겠습니까?')) {
$.ajax({
url: `${contextPath}/sopmal/bnft/resetDefaultNotificationTimes`,
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({}),
success: function(response) {
try {
// resBody.result가 문자열로 된 JSON이므로 파싱 필요
const result = JSON.parse(response.resBody.result);
if (result.success) {
showCustomAlert('알림 시간이 기본값으로 초기화되었습니다.');
window.location.reload();
} else {
showCustomAlert('알림 시간 초기화에 실패했습니다.');
}
} catch (e) {
console.error("응답 처리 중 오류 발생:", e);
showCustomAlert('알림 시간 초기화에 실패했습니다.');
}
},
error: function() {
showCustomAlert('알림 시간 초기화 중 오류가 발생했습니다.');
}
});
}
}
// 개별 알림 삭제
function deleteNotification(id) {
if (confirm('이 알림을 삭제하시겠습니까?')) {
$.ajax({
url: `${contextPath}/sopmal/bnft/deleteNotification`,
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({
bnfNtkNtcnInnb: id
}),
success: function(response) {
try {
// resBody.result가 문자열로 된 JSON이므로 파싱 필요
const result = JSON.parse(response.resBody.result);
if (result.success) {
showCustomAlert('알림이 삭제되었습니다.');
window.location.reload();
} else {
showCustomAlert('알림 삭제에 실패했습니다.');
}
} catch (e) {
console.error("응답 처리 중 오류 발생:", e);
showCustomAlert('알림 삭제에 실패했습니다.');
}
},
error: function() {
showCustomAlert('알림 삭제 중 오류가 발생했습니다.');
}
});
}
}
// 알림용 모달 표시 함수
function showCustomAlert(message) {
// 기존 모달 스타일을 활용한 새로운 알림 모달
$('#modal-alert .microModal__content p').text(message);
MicroModal.show('modal-alert');
}
// 초기 데이터 설정
function setInitialData(data, path) {
notificationData = data;
contextPath = path;
}
// 공개 API
return {
init,
adjustValue,
showSelect,
updateValue,
resetDefaultTimes,
deleteNotification,
setInitialData,
showCustomAlert
};
})();
// 페이지 로드 시 초기화
$(document).ready(function() {
// 초기화는 JSP에서 데이터 설정 후 호출됨
});

View File

@@ -0,0 +1,108 @@
/**
* IntakchlChallengeSettings.js
*
* 섭취 챌린지 설정 화면을 관리하는 모듈
* 사용자가 섭취 제품 관리와 알림 설정 페이지로 이동할 수 있는 기능 제공
*
* 주요 기능:
* 1. 섭취 제품 관리 페이지 이동 처리
* 2. 알림 설정 페이지 이동 처리
* 3. 이벤트 리스너 등록 및 관리
*
* 연관 파일:
* - IntakchlAlarmSettings.js: 알림 설정 관련 기능
* - IntakchlProductDetail.js: 제품 상세 관련 기능
*/
$(function() {
/**
* 문서 로딩 완료 후 초기화 함수
* 각 버튼에 이벤트 리스너 등록
*/
function init() {
// 이벤트 리스너 등록
registerEventListeners();
}
/**
* URL에서 특정 파라미터 값을 추출하는 함수
* @param {string} paramName - 추출할 파라미터명
* @return {string|null} 파라미터 값 또는 null
*/
function getUrlParameter(paramName) {
const urlParams = new URLSearchParams(window.location.search);
return urlParams.get(paramName);
}
/**
* 각 버튼 및 요소에 이벤트 리스너 등록
*/
function registerEventListeners() {
// 섭취제품 관리 버튼 클릭 이벤트
$("#manageProducts").on("click", function(e) {
e.preventDefault(); // 기본 이벤트 방지
navigateToProductManagement();
});
// 알림설정 버튼 클릭 이벤트
$("#setAlarms").on("click", function(e) {
e.preventDefault(); // 기본 이벤트 방지
navigateToAlarmSettings();
});
}
/**
* 섭취제품 관리 페이지로 이동
* 해당 컨트롤러의 intakchlChallengeProductManagement 메소드 호출
*/
function navigateToProductManagement() {
const bnfInnb = getUrlParameter('bnfInnb');
const source = getUrlParameter('source'); // source 파라미터도 추출
let url = '/sopmal/bnft/intakchlChallengeProductManagement';
const params = [];
// bnfInnb 파라미터가 있는 경우 추가
if (bnfInnb) {
params.push('bnfInnb=' + encodeURIComponent(bnfInnb));
}
// source 파라미터가 있는 경우 추가
if (source) {
params.push('source=' + encodeURIComponent(source));
}
// 파라미터가 있으면 URL에 추가
if (params.length > 0) {
url += '?' + params.join('&');
}
// 페이지 이동 전 로딩 표시 등 추가 필요시 여기에 작성
window.location.href = url;
}
/**
* 알림설정 페이지로 이동
* 해당 컨트롤러의 intakchllAlarmSettings 메소드 호출
*/
function navigateToAlarmSettings() {
const bnfInnb = getUrlParameter('bnfInnb');
let url = '/sopmal/bnft/intakchllAlarmSettings';
// bnfInnb 파라미터가 있는 경우 URL에 추가
if (bnfInnb) {
url += '?bnfInnb=' + encodeURIComponent(bnfInnb);
}
// 페이지 이동 전 로딩 표시 등 추가 필요시 여기에 작성
window.location.href = url;
}
// 문서 로딩 완료 시 초기화 함수 호출
init();
// 에러 처리를 위한 전역 에러 핸들러 추가
$(window).on("error", function(e) {
console.error("JavaScript 오류 발생:", e.originalEvent.message);
});
});

View File

@@ -0,0 +1,660 @@
/**
* 섭취챌린지 일일 기록 관련 스크립트
* 달력을 통한 날짜별 섭취 기록 관리 및 섭취 상태 변경 기능 제공
*/
$(document).ready(function() {
// 변수 선언
let currentDate = new Date();
let selectedDate = new Date();
const dayNames = ['일', '월', '화', '수', '목', '금', '토'];
// 요소 캐싱
const $calendarDays = $('#calendarDays');
const $currentMonth = $('#currentMonth');
const $prevWeek = $('#prevWeek');
const $nextWeek = $('#nextWeek');
const $challengeProductContainer = $('.sub-box:nth-child(3)');
const $addChallengeBtn = $('.btn_add_challenge');
// 초기화
init();
/**
* 초기화 함수
*/
function init() {
// 달력 초기화
updateCalendar();
// 이벤트 리스너 등록
registerEventListeners();
// 현재 날짜 데이터 로드
loadDailyRecords(formatDate(selectedDate));
}
/**
* 이벤트 리스너 등록
*/
function registerEventListeners() {
// 이전 주 버튼 클릭
$prevWeek.on('click', function() {
currentDate.setDate(currentDate.getDate() - 7);
selectedDate = new Date(currentDate); // 선택된 날짜도 함께 업데이트
updateCalendar();
loadDailyRecords(formatDate(selectedDate)); // 데이터 로드
});
// 다음 주 버튼 클릭
$nextWeek.on('click', function() {
currentDate.setDate(currentDate.getDate() + 7);
selectedDate = new Date(currentDate); // 선택된 날짜도 함께 업데이트
updateCalendar();
loadDailyRecords(formatDate(selectedDate)); // 데이터 로드
});
// 제품 추가 버튼 클릭
$addChallengeBtn.on('click', function() {
window.location.href = '/sopmal/bnft/intakchlMain';
});
// 섭취 상태 변경 이벤트 위임
$(document).on('click', '.drugCheck span.drug_off', function(e) {
e.preventDefault();
e.stopPropagation();
const $this = $(this);
const bnfNtkInnb = $this.closest('.challengeProduct').data('bnfntkinnb');
const intakeSequence = $this.data('sequence');
updateIntakeStatus(bnfNtkInnb, intakeSequence, $this);
});
}
/**
* 달력 업데이트
*/
function updateCalendar() {
$calendarDays.empty();
$currentMonth.text(`${currentDate.getFullYear()}년 ${currentDate.getMonth() + 1}월`);
// 현재 주의 날짜 표시 (-3일 ~ +3일)
for (let i = -3; i <= 3; i++) {
const date = new Date(currentDate);
date.setDate(currentDate.getDate() + i);
const dayElement = $('<div>').addClass('day');
if (i === 0) dayElement.addClass('center');
// 오늘 날짜 표시
if (isSameDate(date, new Date())) {
dayElement.addClass('today');
}
// 선택된 날짜 표시
if (isSameDate(date, selectedDate)) {
dayElement.addClass('focused');
}
// 요일 및 날짜 추가
dayElement.append(
$('<div>').addClass('day-name').text(dayNames[date.getDay()]),
$('<div>').addClass('day-number').text(date.getDate())
);
// 날짜 클릭 이벤트
dayElement.on('click', function() {
selectedDate = new Date(date);
updateCalendar();
loadDailyRecords(formatDate(selectedDate));
});
$calendarDays.append(dayElement);
}
// 스와이프 이벤트 추가
setupSwipeEvents();
}
/**
* 스와이프 이벤트 설정
*/
function setupSwipeEvents() {
let touchStartX = 0;
let touchStartY = 0;
let touchStartTime = 0;
let isSwipeInProgress = false; // 스와이프 진행 중 플래그
const $calendar = $('.calendar-days');
// 기존 이벤트 리스너 제거 (중복 방지)
$calendar.off('touchstart touchend touchmove');
$calendar.on('touchstart', function(e) {
if (isSwipeInProgress) return; // 이미 스와이프 진행 중이면 무시
const touch = e.originalEvent.touches[0];
touchStartX = touch.clientX;
touchStartY = touch.clientY;
touchStartTime = new Date().getTime();
});
$calendar.on('touchmove', function(e) {
// 스크롤 방지 (세로 스크롤은 허용하되 가로 스와이프 시에만)
const touch = e.originalEvent.touches[0];
const diffX = Math.abs(touch.clientX - touchStartX);
const diffY = Math.abs(touch.clientY - touchStartY);
// 가로 움직임이 세로 움직임보다 클 경우에만 스크롤 방지
if (diffX > diffY && diffX > 10) {
e.preventDefault();
}
});
$calendar.on('touchend', function(e) {
if (isSwipeInProgress) return; // 이미 스와이프 진행 중이면 무시
const touch = e.originalEvent.changedTouches[0];
const touchEndX = touch.clientX;
const touchEndY = touch.clientY;
const touchEndTime = new Date().getTime();
const diffX = touchEndX - touchStartX;
const diffY = touchEndY - touchStartY;
const timeDiff = touchEndTime - touchStartTime;
// 스와이프 조건 검증
const minDistance = 80; // 최소 스와이프 거리 (50 -> 80으로 증가)
const maxTime = 500; // 최대 스와이프 시간 (0.5초)
const maxVerticalDistance = 100; // 세로 방향 최대 허용 거리
// 가로 방향 움직임이 세로 방향보다 크고, 조건을 만족하는 경우에만 스와이프 인식
if (Math.abs(diffX) > minDistance &&
Math.abs(diffX) > Math.abs(diffY) &&
Math.abs(diffY) < maxVerticalDistance &&
timeDiff < maxTime) {
isSwipeInProgress = true; // 스와이프 진행 플래그 설정
if (diffX > 0) {
// 오른쪽 스와이프 - 이전 주 (7일 이전)
currentDate.setDate(currentDate.getDate() - 7);
} else {
// 왼쪽 스와이프 - 다음 주 (7일 다음)
currentDate.setDate(currentDate.getDate() + 7);
}
// 스와이프 후 중앙 날짜를 선택된 날짜로 설정
selectedDate = new Date(currentDate);
// 달력 업데이트
updateCalendar();
// 선택된 날짜의 데이터 로드
loadDailyRecords(formatDate(selectedDate));
// 스와이프 완료 후 일정 시간 후 플래그 해제 (연속 스와이프 방지)
setTimeout(function() {
isSwipeInProgress = false;
}, 300); // 0.3초 후 다시 스와이프 가능
}
});
}
/**
* 일일 섭취 기록 로드
* @param {string} date - 날짜 (YYYY-MM-DD 형식)
*/
function loadDailyRecords(date) {
// URL에서 bnfInnb 파라미터 추출
const urlParams = new URLSearchParams(window.location.search);
const bnfInnb = urlParams.get('bnfInnb');
// 먼저 일일 섭취율 정보 조회
$.ajax({
url: '/sopmal/bnft/selectDailyTotalIntakeRecord',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({
selectedDate: date,
bnfInnb: bnfInnb // BNF_INNB 추가
}),
success: function(response) {
if (response && response.resBody && response.resBody.result) {
try {
const result = JSON.parse(response.resBody.result);
if (result.success) {
// 일일 총 섭취율 업데이트
const intakeRatio = result.dailyTotalIntakeRecord ?
result.dailyTotalIntakeRecord.overallIntakeRatePercentage : 0;
updateTotalIntakeRate(intakeRatio);
// 초기 로드 시 마일리지 적립 상태 체크
const mileageAlreadyAwarded = result.mileageAlreadyAwarded || false;
const mileageAmount = result.rewardInfo || 10;
// 초기 로드 시에도 마일리지 상태 표시
checkMileageStatusOnLoad(intakeRatio, mileageAmount, mileageAlreadyAwarded);
// 현재 챌린지 중인 제품 목록 조회
loadChallengeProducts(date);
} else {
console.error('일일 섭취율 정보 조회 실패:', result.message);
showCustomAlert('데이터 조회 중 오류가 발생했습니다.');
}
} catch (e) {
console.error('응답 파싱 오류:', e, response.resBody.result);
showCustomAlert('데이터 처리 중 오류가 발생했습니다.');
}
}
},
error: function() {
showCustomAlert('서버 통신 중 오류가 발생했습니다.');
}
});
}
/**
* 초기 로드 시 마일리지 적립 상태 체크
* @param {number} rate - 현재 섭취율
* @param {number} amount - 마일리지 금액
* @param {boolean} alreadyAwarded - 이미 적립된 상태 여부
*/
function checkMileageStatusOnLoad(rate, amount, alreadyAwarded) {
const $topupPoint = $('.topupPoint');
if (rate >= 100 && alreadyAwarded) {
// 100% 달성하고 이미 적립된 경우
$topupPoint.html(`<span><i class="ico_mile"></i><b>마일리지 ${amount}원</b> 적립완료!</span>`);
} else if (rate >= 100) {
// 100% 달성했지만 아직 적립되지 않은 경우 (이론적으로는 발생하지 않아야 함)
$topupPoint.html(`<span><i class="ico_mile"></i><b>마일리지 ${amount}원</b> 적립가능!</span>`);
} else {
// 100% 미달성인 경우
$topupPoint.html(`<span>오늘의 섭취기록 100% 도달시<i class="ico_mile"></i><b>마일리지 ${amount}원</b>적립</span>`);
}
}
// 챌린지 중인 제품 목록 조회 함수 추가
function loadChallengeProducts(date) {
// URL에서 bnfInnb 파라미터 추출
const urlParams = new URLSearchParams(window.location.search);
const bnfInnb = urlParams.get('bnfInnb');
$.ajax({
url: '/sopmal/bnft/selectCurrentChallengeProducts',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({
selectedDate: date,
bnfInnb: bnfInnb // BNF_INNB 추가
}),
success: function(response) {
if (response && response.resBody && response.resBody.result) {
try {
const result = JSON.parse(response.resBody.result);
if (result.success) {
console.log("제품 목록:", result.currentChallengeProducts);
renderProductList(result.currentChallengeProducts || []);
} else {
console.log("제품 목록 조회 실패:", result.message);
renderProductList([]);
}
} catch (e) {
console.error('제품 데이터 파싱 오류:', e, response.resBody.result);
renderProductList([]);
}
} else {
renderProductList([]);
}
},
error: function() {
console.error('제품 목록 조회 중 오류 발생');
renderProductList([]);
}
});
}
/**
* 총 섭취율 업데이트
* @param {number} rate - 섭취율 (0-100)
*/
function updateTotalIntakeRate(rate) {
const intakeRate = rate || 0;
$('.challengeBar-container .per').css('width', `${intakeRate}%`);
$('.challengeBar-container .per-txt').text(`${intakeRate}%`);
}
/**
* 이미지 URL에 CDN 적용
* @param {string} imageUrl - 원본 이미지 URL
* @returns {string} - CDN이 적용된 이미지 URL
*/
function applyCdnUrl(imageUrl) {
if (!imageUrl) return '';
// 이미 완전한 URL인 경우 (http:// 또는 https://로 시작)
if (imageUrl.startsWith('http://') || imageUrl.startsWith('https://')) {
return imageUrl;
}
// CDN URL이 있고 상대 경로인 경우
if (window.CDN_URL && imageUrl.startsWith('/')) {
return window.CDN_URL + imageUrl;
}
return imageUrl;
}
/**
* 오늘 날짜인지 확인
* @param {Date} date - 확인할 날짜
* @returns {boolean} - 오늘 날짜면 true, 아니면 false
*/
function isToday(date) {
const today = new Date();
return isSameDate(date, today);
}
/**
* 토스트 메시지 표시
* @param {string} message - 표시할 메시지
*/
function showToast(message) {
// 토스트 요소가 없으면 생성
if ($("#toastPop").length === 0) {
$("body").append('<div id="toastPop" class="toastPop"><p></p></div>');
}
$('#toastPop p').text(message);
$('#toastPop').addClass('show');
// 2초 후 토스트 메시지 숨김
setTimeout(function() {
$('#toastPop').removeClass('show');
}, 2000);
}
/**
* 제품 목록 렌더링
* @param {Array} products - 제품 목록
*/
function renderProductList(products) {
$challengeProductContainer.empty();
const urlParams = new URLSearchParams(window.location.search);
const bnfInnb = urlParams.get('bnfInnb');
$challengeProductContainer.empty();
if (!products || products.length === 0) {
$challengeProductContainer.append(
$('<div>').addClass('no-products').text('섭취 챌린지 중인 제품이 없습니다.')
);
return;
}
// 오늘 날짜인지 확인
const isTodaySelected = isToday(selectedDate);
// 제품 목록 렌더링
products.forEach(product => {
const $productElement = $('<div>')
.addClass('challengeProduct')
.attr('data-bnfntkinnb', product.bnfNtkInnb);
// 제품 컨테이너 생성 (링크 또는 div)
let $productContainer;
if (isTodaySelected) {
// 오늘 날짜인 경우 링크 생성
$productContainer = $('<a>').attr('href',
`/sopmal/bnft/intakchlProductDetail?bnfNtkInnb=${product.bnfNtkInnb}&bnfInnb=${bnfInnb || ''}&returnPage=dailyRecord`
);
} else {
// 오늘 날짜가 아닌 경우 div 생성하되 기존 a 태그와 동일한 CSS 클래스/스타일 적용
$productContainer = $('<div>').addClass('product-link-disabled');
}
// 썸네일 - CDN URL 적용
$productContainer.append(
$('<div>').addClass('thum').append(
$('<img>').attr('src', applyCdnUrl(product.imageUrl)).attr('alt', product.productName)
)
);
// 제품명과 섭취 정보
$productContainer.append(
$('<div>').addClass('cpTit').append(
$('<p>').addClass('tit').text(product.productName),
$('<p>').addClass('dayCheck').html(`<span class="ico_tablet"></span>${product.dailyIntakeCount}회`)
)
);
// 섭취 상태 표시
const $drugCheck = $('<div>').addClass('drugCheck');
// 섭취 횟수가 0이거나 정보가 없는 경우 알약 그림 표시하지 않음
if (product.dailyIntakeCount && product.dailyIntakeCount > 0) {
if (!product.intakeStatuses || product.intakeStatuses.length === 0) {
// 섭취 정보가 없지만 섭취 횟수가 있는 경우 기본 1개 표시
const $statusSpan = $('<span>')
.addClass('drug_off')
.text('섭취미완료')
.attr('data-sequence', 1);
// 오늘 날짜가 아닌 경우 비활성화
if (!isTodaySelected) {
$statusSpan.addClass('disabled').css({
'opacity': '0.5',
'pointer-events': 'none'
});
}
$drugCheck.append($statusSpan);
} else {
// 섭취 상태 표시
product.intakeStatuses.forEach((status, index) => {
const $statusSpan = $('<span>')
.addClass(status ? 'drug_on' : 'drug_off')
.text(status ? '섭취완료' : '섭취미완료')
.attr('data-sequence', index + 1);
// 이미 섭취 완료된 경우 또는 오늘 날짜가 아닌 경우 비활성화
if (status || !isTodaySelected) {
$statusSpan.css('pointer-events', 'none');
if (!isTodaySelected && !status) {
$statusSpan.addClass('disabled').css('opacity', '0.5');
}
}
$drugCheck.append($statusSpan);
});
}
}
$productContainer.append($drugCheck);
// 오늘 날짜가 아닌 경우 전체 제품 요소에 비활성화 클래스 추가
if (!isTodaySelected) {
$productElement.addClass('product-disabled');
}
$productElement.append($productContainer);
$challengeProductContainer.append($productElement);
});
// 오늘 날짜가 아닌 경우 안내 메시지 추가
if (!isTodaySelected) {
const $noticeElement = $('<div>')
.addClass('date-notice')
.css({
'text-align': 'center',
'padding': '15px',
'margin': '10px 0',
'background-color': '#f8f9fa',
'border-radius': '8px',
'color': '#6c757d',
'font-size': '14px'
})
.text('섭취 기록은 오늘 날짜만 추가할 수 있습니다.');
$challengeProductContainer.append($noticeElement);
}
}
/**
* 섭취 상태 업데이트
* @param {string} productId - 제품 ID
* @param {number} sequence - 섭취 순서
* @param {jQuery} $element - 업데이트할 요소
*/
function updateIntakeStatus(productId, sequence, $element) {
// 오늘 날짜가 아닌 경우 업데이트 차단
if (!isToday(selectedDate)) {
showToast('섭취 기록은 오늘 날짜만 추가할 수 있습니다.');
return;
}
// URL에서 bnfInnb 파라미터 추출
const urlParams = new URLSearchParams(window.location.search);
const bnfInnb = urlParams.get('bnfInnb');
$.ajax({
url: '/sopmal/bnft/updateIntakeStatus',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({
bnfNtkInnb: productId,
intakeSequence: sequence,
selectedDate: formatDate(selectedDate),
bnfInnb: bnfInnb // BNF_INNB 추가
}),
success: function(response) {
if (response && response.resBody && response.resBody.result) {
try {
const result = JSON.parse(response.resBody.result);
if (result.success) {
// 성공적으로 업데이트됐을 때 UI 변경
$element.removeClass('drug_off').addClass('drug_on');
$element.text('섭취완료');
$element.css('pointer-events', 'none');
// 일일 총 섭취율 업데이트
updateTotalIntakeRate(result.newTotalIntakeRate);
// 마일리지 적립 상태 체크 (mileageAlreadyAwarded 추가)
checkMileageStatus(
result.mileageAwarded,
result.newTotalIntakeRate,
result.amount,
result.mileageAlreadyAwarded // 추가
);
} else {
showToast(result.message || '상태 업데이트 중 오류가 발생했습니다.');
}
} catch (e) {
console.error('응답 파싱 오류:', e);
showToast('데이터 처리 중 오류가 발생했습니다.');
}
}
},
error: function() {
showToast('서버 통신 중 오류가 발생했습니다.');
}
});
}
/**
* 마일리지 적립 상태 체크
* @param {boolean} awarded - 적립 완료 여부
* @param {number} rate - 현재 섭취율
* @param {number} amount - 마일리지 금액
* @param {boolean} alreadyAwarded - 이미 적립된 상태 여부
*/
function checkMileageStatus(awarded, rate, amount, alreadyAwarded) {
const $topupPoint = $('.topupPoint');
if (awarded && rate >= 100) {
if (alreadyAwarded) {
// 이미 적립된 경우
$topupPoint.html(`<span><i class="ico_mile"></i><b>마일리지 ${amount || 10}원</b> 이미 적립완료!</span>`);
} else {
// 새로 적립된 경우
$topupPoint.html(`<span><i class="ico_mile"></i><b>마일리지 ${amount || 10}원</b> 적립완료!</span>`);
}
} else if (rate >= 100) {
// 100% 달성했지만 마일리지가 아직 처리되지 않은 경우
awardMileage();
}
}
/**
* 마일리지 적립 API 호출
*/
function awardMileage() {
// URL에서 bnfInnb 파라미터 추출
const urlParams = new URLSearchParams(window.location.search);
const bnfInnb = urlParams.get('bnfInnb');
$.ajax({
url: '/sopmal/bnft/awardIntakeChallengesMileage',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({
intakeDate: formatDate(selectedDate),
bnfInnb: bnfInnb // BNF_INNB 추가
}),
success: function(response) {
if (response && response.resBody && response.resBody.result) {
try {
const result = JSON.parse(response.resBody.result);
if (result.success) {
$('.topupPoint').html('<span><i class="ico_mile"></i><b>마일리지 10원</b> 적립완료!</span>');
}
} catch (e) {
console.error('응답 파싱 오류:', e);
}
}
},
error: function() {
console.error('마일리지 적립 중 오류 발생');
}
});
}
/**
* 날짜가 같은지 비교
* @param {Date} date1 - 비교할 첫 번째 날짜
* @param {Date} date2 - 비교할 두 번째 날짜
* @returns {boolean} - 날짜가 같으면 true, 다르면 false
*/
function isSameDate(date1, date2) {
return date1.getFullYear() === date2.getFullYear() &&
date1.getMonth() === date2.getMonth() &&
date1.getDate() === date2.getDate();
}
/**
* 날짜 포맷팅 (YYYY-MM-DD)
* @param {Date} date - 포맷팅할 날짜
* @returns {string} - 포맷팅된 날짜 문자열
*/
function formatDate(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
}
/**
* 커스텀 알림 표시 (기존 showCustomAlert 함수를 토스트로 변경)
* @param {string} message - 표시할 메시지
*/
function showCustomAlert(message) {
showToast(message);
}
});

View File

@@ -0,0 +1,280 @@
/**
* 섭취 챌린지 마일리지 요약 페이지 JavaScript
*
* 주요 기능:
* 1. 달력 렌더링 (월별 섭취 현황 표시)
* 2. 날짜별 상태 조회
*/
$(document).ready(function() {
console.log('[마일리지 요약 페이지] 초기화 시작');
// 월 이름 배열
const monthNames = ["1월", "2월", "3월", "4월", "5월", "6월",
"7월", "8월", "9월", "10월", "11월", "12월"];
console.log('[마일리지 요약 페이지] 월 이름 배열 설정 완료');
// 현재 날짜 및 상태 변수 초기화
let currentDate = new Date();
let isLoading = false;
const $calendarContainer = $("#stats-dates");
console.log('[마일리지 요약 페이지] 현재 날짜:', currentDate);
// 날짜별 상태 클래스 데이터 - 서버에서 받은 데이터 사용
let dateClasses = {};
console.log('[마일리지 요약 페이지] dateClasses 객체 초기화');
// 서버에서 받은 데이터를 객체 형태로 변환
if (window.initialDateClasses && Array.isArray(window.initialDateClasses)) {
console.log('[마일리지 요약 페이지] 서버에서 받은 initialDateClasses 데이터:', window.initialDateClasses);
window.initialDateClasses.forEach(item => {
if (item.date) {
// 상태 클래스들을 조합하여 저장
let classes = [];
if (item.statsDone) classes.push(item.statsDone);
if (item.statsIng && item.statsIng !== item.statsDone) classes.push(item.statsIng);
if (item.stats30done) classes.push(item.stats30done);
if (classes.length > 0) {
dateClasses[item.date] = classes.join(' ');
console.log(`[마일리지 요약 페이지] 날짜 클래스 매핑: ${item.date} => ${dateClasses[item.date]}`);
}
}
});
} else {
console.log('[마일리지 요약 페이지] 초기 날짜 클래스 데이터가 없거나 유효하지 않음');
}
console.log('[마일리지 요약 페이지] 최종 dateClasses 객체:', dateClasses);
/**
* 달력을 렌더링하는 함수
* @param {Date} date - 렌더링할 달의 날짜
* @param {String} position - 추가 위치 ('top' 또는 'bottom')
*/
function renderCalendar(date, position = 'bottom') {
console.log(`[마일리지 요약 페이지] 달력 렌더링 시작: ${date.getFullYear()}년 ${date.getMonth() + 1}월, 위치: ${position}`);
const $statsDatesElement = $("#stats-dates");
const currentYear = date.getFullYear();
const currentMonth = date.getMonth();
const monthYearText = `${monthNames[currentMonth]} ${currentYear}`;
console.log(`[마일리지 요약 페이지] 렌더링할 월/년: ${monthYearText}`);
// 이미 해당 월이 렌더링 되어 있는지 확인
if (!$statsDatesElement.html().includes(monthYearText)) {
console.log(`[마일리지 요약 페이지] ${monthYearText} 달력 새로 렌더링 시작`);
const firstDay = new Date(currentYear, currentMonth, 1).getDay();
const lastDate = new Date(currentYear, currentMonth + 1, 0).getDate();
console.log(`[마일리지 요약 페이지] 첫 요일: ${firstDay}, 마지막 날짜: ${lastDate}`);
const today = new Date();
const isCurrentMonth = today.getFullYear() === currentYear && today.getMonth() === currentMonth;
const todayDate = today.getDate();
console.log(`[마일리지 요약 페이지] 현재 월 여부: ${isCurrentMonth}, 오늘 날짜: ${todayDate}`);
const $monthContainer = $("<div>").addClass("month-container");
// 월 헤더 추가
const $monthHeader = $("<div>").addClass("month-header").text(monthYearText);
$monthContainer.append($monthHeader);
console.log('[마일리지 요약 페이지] 월 헤더 추가 완료');
// 요일 행 추가
const $daysRow = $("<div>").addClass("stats-days");
const days = ["일", "월", "화", "수", "목", "금", "토"];
$.each(days, function(i, day) {
$daysRow.append($("<div>").text(day));
});
$monthContainer.append($daysRow);
console.log('[마일리지 요약 페이지] 요일 행 추가 완료');
// 날짜 그리드 추가
const $datesGrid = $("<div>").addClass("dates-grid");
// 월 시작 전 빈 셀 추가
for (let i = 0; i < firstDay; i++) {
$datesGrid.append($("<div>"));
}
console.log(`[마일리지 요약 페이지] 빈 셀 ${firstDay}개 추가 완료`);
// 날짜 셀 추가
for (let date = 1; date <= lastDate; date++) {
const $dateElement = $("<div>");
const $dateSpan = $("<span>").text(date);
$dateElement.append($dateSpan);
// 날짜 형식화: YYYY-MM-DD
const fullDate = `${currentYear}-${String(currentMonth + 1).padStart(2, '0')}-${String(date).padStart(2, '0')}`;
// 해당 날짜의 상태 클래스가 있으면 추가
if (dateClasses[fullDate]) {
$dateElement.addClass(dateClasses[fullDate]);
console.log(`[마일리지 요약 페이지] 날짜 ${fullDate}에 클래스 ${dateClasses[fullDate]} 추가`);
}
// 오늘 날짜 표시
if (isCurrentMonth && date === todayDate) {
$dateElement.addClass("today");
console.log(`[마일리지 요약 페이지] 오늘 날짜 ${fullDate}에 today 클래스 추가`);
}
$datesGrid.append($dateElement);
}
console.log(`[마일리지 요약 페이지] ${lastDate}일까지 날짜 셀 추가 완료`);
$monthContainer.append($datesGrid);
// 위치에 따라 추가
if (position === 'top') {
$statsDatesElement.prepend($monthContainer);
console.log('[마일리지 요약 페이지] 달력을 상단에 추가');
} else {
$statsDatesElement.append($monthContainer);
console.log('[마일리지 요약 페이지] 달력을 하단에 추가');
}
// 섭취 완료 후 미완료 날짜 스타일 처리
hideAfterStatsDone($datesGrid);
console.log('[마일리지 요약 페이지] hideAfterStatsDone 처리 완료');
} else {
console.log(`[마일리지 요약 페이지] ${monthYearText} 달력이 이미 렌더링되어 있음`);
}
}
/**
* 이전 달을 로드하는 함수
*/
function loadPreviousMonth() {
console.log('[마일리지 요약 페이지] 이전 달 로드 시작');
const newDate = new Date(currentDate.getFullYear(), currentDate.getMonth() - 1, 1);
console.log(`[마일리지 요약 페이지] 이전 달: ${newDate.getFullYear()}년 ${newDate.getMonth() + 1}월`);
renderCalendar(newDate, 'top');
currentDate = newDate;
}
/**
* 다음 달을 로드하는 함수
*/
function loadNextMonth() {
console.log('[마일리지 요약 페이지] 다음 달 로드 시작');
const newDate = new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 1);
console.log(`[마일리지 요약 페이지] 다음 달: ${newDate.getFullYear()}년 ${newDate.getMonth() + 1}월`);
renderCalendar(newDate, 'bottom');
currentDate = newDate;
}
/**
* 스크롤 이벤트 핸들러
* 스크롤 끝에 도달하면 이전/다음 달 로드
*/
function handleScroll() {
if (isLoading) {
console.log('[마일리지 요약 페이지] 로딩 중이므로 스크롤 이벤트 무시');
return;
}
const $container = $(this);
const scrollTop = $container.scrollTop();
const innerHeight = $container.innerHeight();
const scrollHeight = $container[0].scrollHeight;
console.log(`[마일리지 요약 페이지] 스크롤 위치: ${scrollTop}, 컨테이너 높이: ${innerHeight}, 스크롤 전체 높이: ${scrollHeight}`);
if (scrollTop === 0) {
// 맨 위에 도달하면 이전 달 로드
console.log('[마일리지 요약 페이지] 스크롤 상단 도달, 이전 달 로드');
isLoading = true;
const previousScrollHeight = $container[0].scrollHeight;
loadPreviousMonth();
$container.scrollTop($container[0].scrollHeight - previousScrollHeight);
console.log(`[마일리지 요약 페이지] 스크롤 위치 조정: ${$container[0].scrollHeight - previousScrollHeight}`);
isLoading = false;
} else if (scrollTop + innerHeight >= scrollHeight - 10) {
// 맨 아래에 도달하면 다음 달 로드
console.log('[마일리지 요약 페이지] 스크롤 하단 도달, 다음 달 로드');
isLoading = true;
loadNextMonth();
isLoading = false;
}
}
/**
* 섭취 완료 후 미완료 날짜 스타일 처리 함수
* @param {jQuery} $datesGrid - 날짜 그리드 요소
*/
function hideAfterStatsDone($datesGrid) {
console.log('[마일리지 요약 페이지] hideAfterStatsDone 함수 실행');
const statsDoneElements = $datesGrid.find(".stats_done");
console.log(`[마일리지 요약 페이지] stats_done 클래스 요소 수: ${statsDoneElements.length}`);
statsDoneElements.each(function(index) {
const $nextElement = $(this).next();
console.log(`[마일리지 요약 페이지] stats_done 요소 ${index + 1}, 다음 요소 존재: ${$nextElement.length > 0}`);
if ($nextElement.length &&
!$nextElement.hasClass("stats_done") &&
!$nextElement.hasClass("stats_30done")) {
$(this).addClass("hide-after");
console.log(`[마일리지 요약 페이지] stats_done 요소 ${index + 1}에 hide-after 클래스 추가`);
}
});
}
// 이벤트 리스너 등록
$calendarContainer.on("scroll", handleScroll);
console.log('[마일리지 요약 페이지] 스크롤 이벤트 리스너 등록 완료');
// 섭취 완료 버튼 클릭 이벤트 (동적으로 추가된 요소에 대한 이벤트 바인딩)
$(document).on("click", ".drug_off", function(e) {
e.preventDefault();
const $this = $(this);
console.log('[마일리지 요약 페이지] drug_off 클릭 이벤트 발생');
$this.removeClass("drug_off").addClass("drug_on");
$this.text("섭취완료");
console.log('[마일리지 요약 페이지] 클래스 drug_off에서 drug_on으로 변경, 텍스트 "섭취완료"로 변경');
// 섭취 완료 후 클릭 이벤트 제거
$this.css("pointer-events", "none");
console.log('[마일리지 요약 페이지] 클릭 이벤트 제거 (pointer-events: none)');
// 여기에 섭취 완료 API 호출 로직 추가 가능
console.log('[마일리지 요약 페이지] 섭취 완료 API 호출 위치 (아직 구현되지 않음)');
});
// 달력 초기화: 이전 달과 현재 달 모두 렌더링
function initializeCalendar() {
console.log('[마일리지 요약 페이지] 달력 초기화 시작');
// 이전 달 렌더링
const prevMonth = new Date(currentDate.getFullYear(), currentDate.getMonth() - 1, 1);
console.log(`[마일리지 요약 페이지] 이전 달 렌더링: ${prevMonth.getFullYear()}년 ${prevMonth.getMonth() + 1}월`);
renderCalendar(prevMonth, 'top');
// 현재 달 렌더링
console.log(`[마일리지 요약 페이지] 현재 달 렌더링: ${currentDate.getFullYear()}년 ${currentDate.getMonth() + 1}월`);
renderCalendar(currentDate, 'bottom');
// 현재 달로 스크롤 포지션 설정 (약간의 지연을 두어 렌더링 완료 후 스크롤)
console.log('[마일리지 요약 페이지] 스크롤 위치 설정 지연 시작 (100ms)');
setTimeout(function() {
// 이전 달 높이를 찾아서 그 위치로 스크롤
const $monthContainers = $(".month-container");
console.log(`[마일리지 요약 페이지] 월 컨테이너 수: ${$monthContainers.length}`);
if ($monthContainers.length > 1) {
const firstMonthHeight = $($monthContainers[0]).outerHeight();
console.log(`[마일리지 요약 페이지] 첫 번째 월 높이: ${firstMonthHeight}px`);
$calendarContainer.scrollTop(firstMonthHeight);
console.log(`[마일리지 요약 페이지] 스크롤 위치 설정: ${firstMonthHeight}px`);
} else {
console.log('[마일리지 요약 페이지] 월 컨테이너가 충분하지 않아 스크롤 위치 설정 생략');
}
}, 100);
}
// 달력 초기화 함수 호출
initializeCalendar();
console.log('[마일리지 요약 페이지] 초기화 완료');
});

View File

@@ -0,0 +1,296 @@
/**
* 섭취챌린지 제품 상세 페이지 관련 자바스크립트 (jQuery 버전)
*
* 제품 섭취 상태, 섭취 횟수, 메모 정보 관리 및 저장 기능 구현
*/
$(document).ready(function() {
// 초기 설정 및 이벤트 바인딩
initializeEvents();
// 페이지 로드 시 초기 상태 확인 및 UI 업데이트
updateIntakeCounterState();
checkInitialState(); // 초기 상태 확인 추가
// 삭제 버튼(data-micromodal-trigger 속성이 있는 버튼)만 선택
$(".header__toBack__btn[data-micromodal-trigger='modal-1']").on('click', function() {
// 모달 표시
MicroModal.show('modal-1');
});
// 뒤로가기 버튼(data-micromodal-trigger 속성이 없는 버튼) 이벤트
$(".header__toBack__btn:not([data-micromodal-trigger])").on('click', function() {
goBackToPreviousPage();
});
// 모달 확인 버튼 클릭 이벤트
$("#modal-1 .microModal__btn-primary").on('click', function() {
deleteProduct();
});
});
/**
* 초기 이벤트 설정 함수
*/
function initializeEvents() {
// 카운터 버튼 이벤트 설정
$('#decrease').on('click', function() {
let count = parseInt($('#count').text());
if (count > 1) {
$('#count').text(--count);
}
});
// 증가 버튼 클릭 이벤트
$('#increase').on('click', function() {
let count = parseInt($('#count').text());
$('#count').text(++count);
});
// 저장 버튼 클릭 이벤트
$('#save_toast').on('click', saveProductDetails);
// 섭취 상태 변경 이벤트
$('input[name="intakeStatus"]').on('change', function() {
updateIntakeCounterState();
});
}
/**
* 섭취 상태에 따른 섭취설정 영역 활성화/비활성화 처리
*/
function updateIntakeCounterState() {
const intakeStatus = $('input[name="intakeStatus"]:checked').val();
const isDisabled = intakeStatus === '030'; // 종료 상태인 경우
// 섭취 횟수 영역 UI 요소들
const $counterArea = $('.ci_counter');
const $decreaseBtn = $('#decrease');
const $increaseBtn = $('#increase');
if (isDisabled) {
// 종료 상태일 때 비활성화
$counterArea.addClass('disabled');
$decreaseBtn.prop('disabled', true);
$increaseBtn.prop('disabled', true);
$counterArea.css('opacity', '0.5');
$counterArea.css('pointer-events', 'none');
} else {
// 다른 상태일 때 활성화
$counterArea.removeClass('disabled');
$decreaseBtn.prop('disabled', false);
$increaseBtn.prop('disabled', false);
$counterArea.css('opacity', '1');
$counterArea.css('pointer-events', 'auto');
}
}
/**
* 제품 상세 정보 저장 함수
*/
function saveProductDetails() {
const urlParams = new URLSearchParams(window.location.search);
const bnfInnb = urlParams.get('bnfInnb'); // 파라미터에서 값 가져오기
// 데이터 수집
// const intakeStatus = $('input[name="intakeStatus"]:checked').val();
const intakeStatus = '010'; // 무조건 섭취중 코드로 설정
const intakeCount = parseInt($('#count').text());
const memo = $('.white_textarea').val();
const bnfNtkInnb = $('#bnfNtkInnb').val(); // 히든 필드에서 값 가져오기
// 데이터 객체 생성
const requestData = {
bnfNtkSttusDvCd: intakeStatus,
ntkCnt: intakeCount,
memo: memo,
bnfNtkInnb: bnfNtkInnb,
bnfInnb: bnfInnb
};
// API 호출
sendDataToServer(requestData);
}
/**
* URL에서 bnfNtkInnb 파라미터 추출
* @returns {number} bnfNtkInnb 값 (없으면 null)
*/
function getBnfNtkInnbFromUrl() {
const urlParams = new URLSearchParams(window.location.search);
const bnfNtkInnb = urlParams.get('bnfNtkInnb');
return bnfNtkInnb ? Number(bnfNtkInnb) : null;
}
/**
* 서버에 데이터 전송
* @param {Object} data - 전송할 데이터 객체
*/
function sendDataToServer(data) {
$.ajax({
url: '/sopmal/bnft/updateDailyIntakeAndMemo',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify(data),
success: function(response) {
try {
// resBody.result가 문자열로 되어 있으므로 파싱
const resultData = JSON.parse(response.resBody.result);
if (resultData.resultCode === '0000') {
showToast(resultData.message || '저장되었습니다.');
// 종료 상태인 경우 전체 폼 비활성화
if (data.bnfNtkSttusDvCd === '030') {
disableEntireForm();
}
} else {
showToast('저장 중 오류가 발생했습니다.');
}
} catch (e) {
console.error('Error parsing result:', e);
showToast('저장 중 오류가 발생했습니다.');
}
},
error: function(xhr, status, error) {
console.error('Error:', error);
showToast('저장 중 오류가 발생했습니다.');
}
});
}
/**
* 토스트 메시지 표시
* @param {string} message - 표시할 메시지
*/
function showToast(message) {
$('#toastPop p').text(message);
$('#toastPop').addClass('show');
// 2초 후 토스트 메시지 숨김
setTimeout(function() {
$('#toastPop').removeClass('show');
}, 2000);
}
// 페이지 로드 시 종료 상태인지 확인하여 폼 비활성화
function checkInitialState() {
const currentStatus = $('input[name="intakeStatus"]:checked').val();
if (currentStatus === '030') {
disableEntireForm();
}
}
// 전체 폼 비활성화 함수
function disableEntireForm() {
// 라디오 버튼, 카운터, 메모, 저장 버튼 모두 비활성화
$('input[name="intakeStatus"]').prop('disabled', true);
$('.ci_counter').css({'opacity': '0.5', 'pointer-events': 'none'});
$('.white_textarea').prop('readonly', true);
$('#save_toast').prop('disabled', true).css('opacity', '0.5');
// 종료 알림 표시
$('.challengeSet').append('<div class="notice">종료된 챌린지는 수정할 수 없습니다.</div>');
}
/**
* 이전 페이지로 돌아가기 함수
*/
function goBackToPreviousPage() {
const urlParams = new URLSearchParams(window.location.search);
const bnfInnb = urlParams.get('bnfInnb');
const returnPage = urlParams.get('returnPage');
console.log('이전 페이지로 돌아가기:', returnPage);
switch (returnPage) {
case 'dailyRecord':
// 일일 기록 페이지에서 왔을 경우
if (bnfInnb) {
window.location.href = `/sopmal/bnft/intakchlDailyRecordMain?bnfInnb=${bnfInnb}`;
} else {
window.location.href = '/sopmal/bnft/intakchlDailyRecordMain';
}
break;
case 'mileageSummary':
// 마일리지 요약 페이지에서 왔을 경우
if (bnfInnb) {
window.location.href = `/sopmal/bnft/intakchlMileageSummary?bnfInnb=${bnfInnb}`;
} else {
window.location.href = '/sopmal/bnft/intakchlMileageSummary';
}
break;
case 'productManagement':
// 제품 관리 페이지에서 왔을 경우
if (bnfInnb) {
window.location.href = `/sopmal/bnft/intakchlChallengeProductManagement?bnfInnb=${bnfInnb}`;
} else {
window.location.href = '/sopmal/bnft/intakchlChallengeProductManagement';
}
break;
case 'main':
// 메인 페이지에서 왔을 경우
window.location.href = '/sopmal/bnft/bnftMain';
break;
default:
// 기본값은 제품 관리 페이지
if (bnfInnb) {
window.location.href = `/sopmal/bnft/intakchlChallengeProductManagement?bnfInnb=${bnfInnb}`;
} else {
window.location.href = '/sopmal/bnft/intakchlChallengeProductManagement';
}
break;
}
}
/**
* 제품 삭제 함수
*/
function deleteProduct() {
// 삭제할 제품의 고유번호 가져오기
const bnfNtkInnb = $('#bnfNtkInnb').val();
// 삭제 요청 데이터 객체 생성
const requestData = {
bnfNtkInnb: bnfNtkInnb
};
// 삭제 API 호출
$.ajax({
url: '/sopmal/bnft/deleteIntakeChallengeProduct',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify(requestData),
success: function(response) {
// resBody.result가 문자열로 오므로 JSON으로 파싱해야 함
if (response && response.resBody && response.resBody.result) {
const result = JSON.parse(response.resBody.result);
if (result.success === true) {
// 삭제 성공
showToast(result.message || '제품이 삭제되었습니다.');
// 1.5초 후 이전 페이지로 이동
setTimeout(function() {
goBackToPreviousPage();
}, 1500);
} else {
// 삭제 실패
showToast(result.message || '삭제 중 오류가 발생했습니다.');
MicroModal.close('modal-1');
}
} else {
// 응답 구조가 예상과 다른 경우
showToast('삭제 중 오류가 발생했습니다.');
MicroModal.close('modal-1');
}
},
error: function(xhr, status, error) {
console.error('Error:', error);
showToast('삭제 중 오류가 발생했습니다.');
MicroModal.close('modal-1');
}
});
}