merge master into develop - CscListRForm.jsp FAQ 아코디언 버그 수정
This commit is contained in:
200
build/resources/main/static/mobile/js/bnft/intakchl/Intakchl.js
Normal file
200
build/resources/main/static/mobile/js/bnft/intakchl/Intakchl.js
Normal 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');
|
||||
}
|
||||
});
|
||||
@@ -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에서 데이터 설정 후 호출됨
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
@@ -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('[마일리지 요약 페이지] 초기화 완료');
|
||||
});
|
||||
@@ -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');
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user