// 상품 수량 조절 기능 초기화
const plusBtns = document.querySelectorAll('.btn-plus');
const minusBtns = document.querySelectorAll('.btn-minus');
const quantityEls = document.querySelectorAll('#quantity');
const quantityInputs = document.querySelectorAll('.quantity-input');
const priceTotals = document.querySelectorAll('.price_total');
const unitPrices = Array.from(priceTotals).map((el, i) => {
const priceText = el.textContent.replace(/[^0-9]/g, '');
const initialCount = parseInt(quantityEls[i].textContent);
return parseInt(priceText) / initialCount;
});
// (+) 버튼 클릭 시 수량 증가 및 금액 재계산
plusBtns.forEach((btn, index) => {
btn.addEventListener('click', () => {
let count = parseInt(quantityEls[index].textContent);
count++;
quantityEls[index].textContent = count;
quantityInputs[index].value = count;
const totalPrice = unitPrices[index] * count;
priceTotals[index].textContent = totalPrice.toLocaleString() + '원';
});
});
// (–) 버튼 클릭 시 수량 감소(1 미만 방지) 및 금액 재계산
minusBtns.forEach((btn, index) => {
btn.addEventListener('click', () => {
let count = parseInt(quantityEls[index].textContent);
if (count > 1) {
count--;
quantityEls[index].textContent = count;
quantityInputs[index].value = count;
const totalPrice = unitPrices[index] * count;
priceTotals[index].textContent = totalPrice.toLocaleString() + '원';
}
});
});
// input 필드 직접 입력 시 span과 총금액 동기화
quantityInputs.forEach((input, index) => {
input.addEventListener('input', () => {
let count = parseInt(input.value);
if (count > 0) {
quantityEls[index].textContent = count;
const totalPrice = unitPrices[index] * count;
priceTotals[index].textContent = totalPrice.toLocaleString() + '원';
}
});
});
기능 설명
unitPrices배열에 초기 한 개당 단가를 저장- “+” 버튼 클릭 시 해당 인덱스의 수량(
span#quantity)을 1 증가시키고, 숨겨진input.quantity-input값도 동기화- “–” 버튼 클릭 시 1보다 클 때만 수량 감소 및 값 업데이트
- 수량 변경 후에는
totalPrice = 단가 × 수량계산 후span.price_total에 콤마(천 단위) 포함하여 표시- 사용자가 직접
input.quantity-input에 입력하면, 입력값으로 수량을 갱신하고 금액을 재계산하여 반영
// 탭 메뉴 요소 선택
const tabs = document.querySelectorAll('.tab-menu li');
const panes = document.querySelectorAll('.tab-pane');
// 탭 클릭 시 active 클래스 토글
tabs.forEach(tab => {
tab.addEventListener('click', () => {
tabs.forEach(t => t.classList.remove('active'));
panes.forEach(p => p.classList.remove('active'));
tab.classList.add('active');
const target = tab.getAttribute('data-tab');
const targetPane = document.getElementById(target);
targetPane.classList.add('active');
targetPane.scrollIntoView({ behavior: 'smooth', block: 'start' });
});
});
// 스크롤 시 탭 메뉴가 상단에 도달하면 fixed 클래스 추가
const tabMenu = document.querySelector('.tab-menu');
const offsetTop = tabMenu.offsetTop;
window.addEventListener('scroll', () => {
if (window.scrollY >= offsetTop) {
tabMenu.classList.add('fixed');
} else {
tabMenu.classList.remove('fixed');
}
});
기능 설명
.tab-menu li클릭 시 모든 탭과 패널(.tab-pane)의active클래스를 제거하고, 클릭된 탭과 해당 패널(data-tab속성 매핑)에만active를 부여- 패널 전환 후
scrollIntoView로 해당 패널이 보이도록 스무스 스크롤- 스크롤이 일정 높이(offsetTop) 이상 내려가면 탭 메뉴에
fixed클래스를 추가하여 화면 상단에 고정
// 배송 도착 예정일을 오늘 기준 +3일로 계산하여 표시
const arrivalSpan = document.getElementById("arrivalDate");
const today = new Date();
today.setDate(today.getDate() + 3);
const weekdays = ['일', '월', '화', '수', '목', '금', '토'];
const formatted = `${today.getMonth() + 1}/${today.getDate()}(${weekdays[today.getDay()]})`;
arrivalSpan.textContent = formatted;
기능 설명
- 현재 날짜(
new Date())에서 3일을 더한 뒤, 월/일/요일(weekdays배열) 형식으로 문자열을 생성span#arrivalDate에"MM/DD(요일)"형태로 표시
const couponButton = document.querySelector('.coupon');
const couponModal = document.getElementById('couponModal');
const couponListDiv = document.getElementById('couponList');
const companySpan = document.querySelector('.department');
const selectedCoupons = new Map();
const applyCouponButton = couponModal ? couponModal.querySelector('.modal-buttons button') : null;
if (couponButton && couponModal && couponListDiv && companySpan && applyCouponButton) {
couponButton.addEventListener('click', function () {
const companyName = companySpan.textContent.trim();
const couponUrl = `/product/coupon?company=${companyName}`;
fetch(couponUrl, { method: 'GET', credentials: 'include' })
.then(response => {
if (response.redirected) {
const currentUrl = window.location.href;
const prod = currentUrl.split("=")[1];
window.location.href = "/product/ViewLoginCheck?prodNo=" + prod;
return;
}
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
return response.json();
})
.then(coupons => {
if (!coupons) return;
couponListDiv.innerHTML = '';
const currentDate = new Date();
const oneDay = 24 * 60 * 60 * 1000;
// 쿠폰 클릭 시 선택/해제
const handleCouponClick = function (couponData, element) {
const couponId = couponData.cno;
const validTo = couponData.validTo;
if (selectedCoupons.has(couponId)) {
element.style.border = '';
selectedCoupons.delete(couponId);
} else {
element.style.border = '2px solid red';
selectedCoupons.set(couponId, element);
}
};
// 개별 쿠폰 요소 렌더링
const renderCouponItem = (coupon) => {
const couponItem = document.createElement('div');
couponItem.classList.add('coupon-item');
couponItem.innerHTML = `
<p>${coupon.benefit}</p>
<p>${coupon.couponName}</p>
<p>쿠폰 종류: ${coupon.couponType} (${coupon.minPrice} 이상 구매 시 사용 가능)</p>
<input type="hidden" class="coupon-id" value="${coupon.cno}">
`;
couponItem.dataset.cno = coupon.cno;
couponItem.dataset.validto = coupon.validTo;
const validToDate = new Date(coupon.validTo);
const timeDiff = validToDate.getTime() - currentDate.getTime();
const daysLeft = Math.ceil(timeDiff / oneDay);
if (daysLeft < 7 && daysLeft >= 0) {
const tooltip = document.createElement('span');
tooltip.classList.add('coupon-expiry');
tooltip.textContent = `${daysLeft}일 남음`;
couponItem.appendChild(tooltip);
}
couponItem.addEventListener('click', function () {
handleCouponClick(coupon, this);
});
couponListDiv.appendChild(couponItem);
};
// 응답 형태에 따라 리스트 또는 단일 쿠폰 렌더
if (!coupons || (Array.isArray(coupons) && coupons.length === 0) ||
(typeof coupons === 'object' && !coupons.cno && !Array.isArray(coupons))) {
couponListDiv.innerHTML = '<p>현재 적용 가능한 쿠폰이 없습니다.</p>';
} else if (Array.isArray(coupons)) {
coupons.forEach(renderCouponItem);
} else if (coupons && typeof coupons === 'object' && coupons.cno) {
renderCouponItem(coupons);
}
couponModal.style.display = 'block';
selectedCoupons.clear();
})
.catch(error => {
console.error('Error fetching or processing coupons:', error);
couponListDiv.innerHTML = '<p>쿠폰 정보를 불러오는 중 오류가 발생했습니다.</p>';
couponModal.style.display = 'block';
});
});
// 모달 닫기 (× 버튼 및 바깥 클릭)
const couponCloseButton = couponModal.querySelector('.close-button');
if (couponCloseButton) {
couponCloseButton.addEventListener('click', () => {
couponModal.style.display = 'none';
});
}
window.addEventListener('click', (event) => {
if (event.target === couponModal) {
couponModal.style.display = 'none';
}
});
// 선택된 쿠폰 일괄 발급
applyCouponButton.addEventListener('click', function () {
if (selectedCoupons.size > 0) {
applyCouponButton.disabled = true;
applyCouponButton.textContent = '요청 중...';
const requests = [];
selectedCoupons.forEach((couponElement) => {
const couponCno = couponElement.dataset.cno;
const formData = new URLSearchParams();
formData.append("cno", couponCno);
const request = fetch('/product/couponIssue', {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: formData.toString()
})
.then(response => response.text())
.then(message => message)
.catch(() => `쿠폰 ${couponCno} 발급 실패`);
requests.push(request);
});
Promise.all(requests).then(results => {
const successCount = results.filter(msg => msg.includes('발급되었습니다')).length;
if (successCount > 0) {
alert(`쿠폰 ${successCount}개가 발급되었습니다.`);
}
applyCouponButton.disabled = false;
applyCouponButton.textContent = '할인 혜택 받기';
couponModal.style.display = 'none';
selectedCoupons.clear();
couponListDiv.querySelectorAll('.coupon-item').forEach(item => item.style.border = '');
});
} else {
alert('적용할 쿠폰을 선택해주세요.');
}
});
} else {
console.warn('Coupon modal elements missing.');
}
기능 설명
- 쿠폰 조회
.coupon버튼 클릭 시/product/coupon?company=회사명GET 요청- 로그인 필요 시
response.redirected체크하여 로그인 페이지로 리다이렉트- 응답 데이터를 JSON으로 파싱하여
coupons변수에 저장- 쿠폰 렌더링
- 배열 형태면
forEach, 단일 객체면 바로renderCouponItem호출renderCouponItem내부에서 각 쿠폰에 대한div.coupon-item생성
benefit,couponName,couponType과minPrice정보 표시- 만료일(
validTo) 기준 남은 일수(daysLeft) 계산 → 7일 미만이면<span class="coupon-expiry">n일 남음</span>추가- 클릭하면
selectedCouponsMap에cno를 키로 추가/제거, 선택 시 테두리를 빨간색으로 표시- 모달 열기/닫기
- 조회 후
couponModal.style.display = 'block'- × 버튼 클릭 및 모달 바깥 클릭 시
couponModal.style.display = 'none'- 쿠폰 발급
- “할인 혜택 받기” 버튼 클릭 시
selectedCouponsMap 순회/product/couponIssuePOST 요청(폼 URL인코딩) → 쿠폰 발급 처리Promise.all로 모든 요청 완료 후 성공 메시지(“발급되었습니다”)를 검사하여 개수만큼 alert 표시- 모달 닫기, 테두리 초기화, 버튼 활성화 복원
const cartButton = document.querySelector('form table button.cart');
if (cartButton) {
cartButton.addEventListener('click', (event) => {
event.preventDefault();
const productNoInput = document.getElementById('productNo');
const productNo = productNoInput ? productNoInput.value : null;
const quantityElement = document.getElementById('quantity');
const quantity = quantityElement ? parseInt(quantityElement.textContent, 10) : 1;
// 옵션 데이터를 객체에 수집
const optionData = {};
const optionRows = document.querySelectorAll('form table tr.choice');
optionRows.forEach((row, index) => {
const optionTypeElement = row.querySelector('td:first-child');
const selectElement = row.querySelector('td:nth-child(2) select');
if (optionTypeElement && selectElement && selectElement.value) {
const optKey = `opt${index + 1}`;
const optContKey = `opt${index + 1}cont`;
optionData[optKey] = optionTypeElement.textContent.trim();
optionData[optContKey] = selectElement.value;
}
});
const cartItemData = {
prodNo: productNo,
quantity: quantity,
options: optionData
};
fetch('/product/addCart', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify(cartItemData)
})
.then(response => {
if (response.redirected) {
let loginRedirectUrl = '/product/ViewLoginCheck';
if (productNo) {
loginRedirectUrl += '?prodNo=' + productNo;
}
window.location.href = loginRedirectUrl;
return null;
}
if (!response.ok) {
return response.json().catch(() => {
throw new Error(`서버 응답 오류: ${response.status}`);
}).then(errorBody => {
const message = errorBody?.message || `서버 응답 오류: ${response.status}`;
throw new Error(message);
});
}
return response.json();
})
.then(result => {
if (result === null) return;
if (result === 1) {
if (confirm('장바구니로 이동하시겠습니까?')) {
window.location.href = '/product/cart';
}
} else {
alert('장바구니 요청이 처리되었으나, 추가되지 않았을 수 있습니다. (결과: ' + result + ')');
}
})
.catch(error => {
console.error('장바구니 추가 실패:', error);
alert(`오류가 발생했습니다: ${error.message}`);
});
});
} else {
console.error('장바구니 버튼(form table button.cart)을 찾을 수 없습니다.');
}