상품 상세 스크립트

상품 수량 조절 기능

// 상품 수량 조절 기능 초기화
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() + '원';
        }
    });
});

기능 설명

탭 메뉴 고정 및 전환 기능

// 탭 메뉴 요소 선택
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');
    }
});

기능 설명

도착 예정일 표시 기능

// 배송 도착 예정일을 오늘 기준 +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;

기능 설명

쿠폰 모달 조회·선택·발급 기능

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.');
}

기능 설명

장바구니 추가 기능

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)을 찾을 수 없습니다.');
}