Files
madeu_petit_home/src/main/resources/templates/web/webevent/makeReservation.html
2025-12-22 22:48:48 +09:00

939 lines
38 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{/web/layout/layout}">
<th:block layout:fragment="layoutCss">
<link rel="stylesheet" th:href="@{/css/web/service/makeReservation.css}" />
</th:block>
<th:block layout:fragment="layout_top_script">
<script th:inline="javascript">
let category_div_cd = [[${CATEGORY_DIV_CD}]];
let category_no = [[${CATEGORY_NO}]];
let post_no = [[${POST_NO}]];
let procedure_id = [[${PROCEDURE_ID}]];
</script>
</th:block>
<th:block layout:fragment="layoutContent">
<div class="reservation-container">
<!-- 좌측: 시술 예약 -->
<div class="box" id="box-service">
<div class="step-title">STEP 01. 이벤트 예약</div>
<div class="service-count-info" id="service-count-info">선택된 시술: 1개</div>
<div class="service-list" id="service-list">
<div class="service-item">
<span>다이어트약처방</span>
<span>
<span style="text-decoration:line-through; color:#bbb; font-size:0.95em; margin-right:6px;">50,000원</span>
<span class="price">50,000원</span>
<span class="del disabled" title="최소 1개의 시술은 필요합니다" onclick="removeService(this)">×</span>
</span>
</div>
</div>
<div class="total">
<span>총 금액 <small>(부가세 별도)</small></span>
<span class="price" id="total-price">50,000원</span>
</div>
<div style="font-size:0.9em; color:#aaa; margin-top:8px;">결제는 내원시 이루어집니다.</div>
</div>
<!-- 가운데: 예약 시간 선택 -->
<div class="box" id="box-calendar">
<div class="step-title" id="step02-title">STEP 02. 예약 시간 선택</div>
<div class="calendar-box">
<div class="calendar-header">
<button id="prev-month" aria-label="이전 달">&lt;</button>
<span id="calendar-title" style="font-weight:600;"></span>
<button id="next-month" aria-label="다음 달">&gt;</button>
</div>
<table class="calendar-table" id="calendar-table">
<!-- JS로 렌더링 -->
</table>
</div>
<div class="time-slots" id="time-slots">
<!-- JS로 렌더링 -->
</div>
<div class="person-count" style="display:none;">예약인원 <span id="person-count">-</span></div>
</div>
<!-- 우측: 고객정보 입력 -->
<div class="box" id="box-info">
<div class="step-title" id="step03-title">STEP 03. 고객정보</div>
<form id="reserve-form" autocomplete="off">
<div class="form-group">
<label for="customer-name">고객명</label>
<input type="text" id="customer-name" name="customer-name" required>
</div>
<div class="form-group">
<label for="birthDate">생년월일 (8자리)</label>
<input type="text" id="birthDate" name="birthDate"
placeholder="예: 19900115 (8자리 숫자만)"
maxlength="8" required>
<!-- BirthDateValidator가 동적 생성하는 메시지 영역 -->
</div>
<div class="form-group">
<label for="customer-phone">연락처</label>
<input type="tel" id="customer-phone" name="customer-phone"
placeholder="고객님의 핸드폰 번호를 입력해 주세요."
required pattern="^01[0-9]{8,9}$" title="올바른 휴대폰 번호를 입력해주세요.">
<!-- 피드백 메시지는 common.js PhoneValidator가 동적 생성 -->
</div>
<div class="form-group">
<label for="customer-req">요청사항</label>
<textarea id="customer-req" name="customer-req"
placeholder="요청사항을 작성해 주세요."></textarea>
</div>
<div class="checkbox-group">
<input type="checkbox" id="agree" required>
<label for="agree">[필수] 서비스 이용 및 예약 신청을 위한 개인정보 제공에 동의</label>
</div>
<button type="submit" class="submit-btn" id="submit-btn" disabled>📅 예약 날짜를 선택해주세요</button>
</form>
</div>
</div>
</th:block>
<th:block layout:fragment="layoutContentScript">
<script>
// 휴일(완전 휴무) 설정
const disabledSpecificDates = [
'2025-12-25', // 크리스마스
'2026-01-01', // 신정
// 필요 시 추가
];
// 단축 진료일 (15:30까지) 설정
const shortWorkingDates = [
'2025-12-24', // 크리스마스 이브
'2025-12-31' // 연말
];
// 날짜가 휴무일인지 확인
function isDateDisabled(date) {
const dateStr = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
return disabledSpecificDates.includes(dateStr);
}
// 단축 근무일인지 확인
function isShortWorkingDate(date) {
const dateStr = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
return shortWorkingDates.includes(dateStr);
}
// 생년월일 검증 클래스 (기존 그대로 유지)
class BirthDateValidator {
constructor(inputId, options = {}) {
this.inputElement = document.getElementById(inputId);
this.messageElement = null;
this.options = {
showMessage: true,
realTimeValidation: true,
minAge: 0,
maxAge: 150,
format: 'YYYYMMDD',
allowFuture: false,
onValidationChange: null,
...options
};
if (this.inputElement && this.options.showMessage) {
this.createMessageElement();
}
if (this.inputElement && this.options.realTimeValidation) {
this.bindEvents();
}
}
createMessageElement() {
this.messageElement = document.createElement('div');
this.messageElement.className = 'birth-date-message';
this.messageElement.style.display = 'none';
this.inputElement.parentNode.insertBefore(this.messageElement, this.inputElement.nextSibling);
}
bindEvents() {
this.inputElement.addEventListener('input', () => {
this.inputElement.value = this.inputElement.value.replace(/[^0-9]/g, '');
this.validateAndShowMessage();
});
this.inputElement.addEventListener('blur', () => {
this.validateAndShowMessage();
});
this.inputElement.addEventListener('keydown', (e) => {
if ([8, 9, 46, 37, 38, 39, 40].includes(e.keyCode)) {
return;
}
if ((e.keyCode < 48 || e.keyCode > 57) && (e.keyCode < 96 || e.keyCode > 105)) {
e.preventDefault();
}
});
}
validateBirthDate(dateStr) {
if (!dateStr) return { valid: false, message: '생년월일을 입력해주세요.' };
let cleanDate = dateStr.replace(/[^0-9]/g, '');
if (cleanDate.length !== 8) {
return { valid: false, message: '생년월일은 8자리 숫자로 입력해주세요. (예: 19900115)' };
}
const datePattern = /^\d{8}$/;
if (!datePattern.test(cleanDate)) {
return { valid: false, message: '올바른 생년월일 형식이 아닙니다.' };
}
const year = parseInt(cleanDate.slice(0, 4));
const month = parseInt(cleanDate.slice(4, 6));
const day = parseInt(cleanDate.slice(6, 8));
const currentYear = new Date().getFullYear();
const minYear = currentYear - this.options.maxAge;
const maxYear = currentYear - this.options.minAge;
if (year < minYear || year > maxYear) {
return {
valid: false,
message: `출생연도는 ${minYear}년부터 ${maxYear}년 사이여야 합니다.`
};
}
if (month < 1 || month > 12) {
return { valid: false, message: '월은 01부터 12까지 입력 가능합니다.' };
}
if (day < 1 || day > 31) {
return { valid: false, message: '일은 01부터 31까지 입력 가능합니다.' };
}
const date = new Date(year, month - 1, day);
const isValidDate = (
date.getFullYear() === year &&
date.getMonth() === month - 1 &&
date.getDate() === day
);
if (!isValidDate) {
return { valid: false, message: '존재하지 않는 날짜입니다.' };
}
if (!this.options.allowFuture && date > new Date()) {
return { valid: false, message: '미래 날짜는 입력할 수 없습니다.' };
}
if (month === 4 || month === 6 || month === 9 || month === 11) {
if (day === 31) {
return { valid: false, message: '해당 월은 31일이 없습니다.' };
}
}
if (month === 2) {
const isLeapYear = (year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0));
if (day > 29 || (day === 29 && !isLeapYear)) {
return { valid: false, message: isLeapYear ? '2월은 29일까지만 있습니다.' : '2월은 28일까지만 있습니다.' };
}
}
return { valid: true, message: '올바른 생년월일입니다.' };
}
validateAndShowMessage() {
const result = this.validateBirthDate(this.inputElement.value);
if (this.messageElement) {
this.showMessage(result.message, result.valid ? 'success' : 'error');
}
if (typeof this.options.onValidationChange === 'function') {
this.options.onValidationChange(result, this.inputElement.value);
}
return result.valid;
}
showMessage(message, type) {
if (!this.messageElement) return;
this.messageElement.textContent = message;
this.messageElement.className = `birth-date-message ${type}`;
this.messageElement.style.display = message ? 'block' : 'none';
}
isValid() {
return this.validateBirthDate(this.inputElement.value).valid;
}
getAge() {
const result = this.validateBirthDate(this.inputElement.value);
if (!result.valid) return null;
const cleanDate = this.inputElement.value.replace(/[^0-9]/g, '');
const birthYear = parseInt(cleanDate.slice(0, 4));
const birthMonth = parseInt(cleanDate.slice(4, 6));
const birthDay = parseInt(cleanDate.slice(6, 8));
const today = new Date();
let age = today.getFullYear() - birthYear;
if (today.getMonth() + 1 < birthMonth ||
(today.getMonth() + 1 === birthMonth && today.getDate() < birthDay)) {
age--;
}
return age;
}
static isValidBirthDate(dateStr) {
const validator = new BirthDateValidator('temp', { showMessage: false });
return validator.validateBirthDate(dateStr).valid;
}
}
// 전역 변수
let birthDateValidator;
let selectedTreatments = [];
// 초기화
fn_SelectReservation(category_div_cd, category_no, post_no, procedure_id);
// 시술 삭제 함수
function removeService(el) {
const serviceItems = document.querySelectorAll('.service-item');
const serviceCount = serviceItems.length;
if (serviceCount <= 1) {
alert('최소 1개의 시술은 선택되어 있어야 합니다.');
return false;
}
const serviceName = el.closest('.service-item').querySelector('span:first-child').textContent;
if (!confirm(`'${serviceName}' 시술을 삭제하시겠습니까?`)) {
return false;
}
const removedItem = el.closest('.service-item');
const priceText = removedItem.querySelector('.price').textContent;
const price = parseInt(priceText.replace(/[^0-9]/g, '')) || 0;
removedItem.remove();
updateTotalPrice();
updateServiceCount();
const removedIndex = Array.from(serviceItems).indexOf(removedItem);
if (removedIndex !== -1 && selectedTreatments[removedIndex]) {
selectedTreatments.splice(removedIndex, 1);
}
return true;
}
function updateTotalPrice() {
const serviceItems = document.querySelectorAll('.service-item');
let totalPrice = 0;
serviceItems.forEach(item => {
const priceText = item.querySelector('.price').textContent;
const price = parseInt(priceText.replace(/[^0-9]/g, '')) || 0;
totalPrice += price;
});
document.getElementById('total-price').textContent = totalPrice.toLocaleString() + '원';
}
function updateServiceCount() {
const serviceItems = document.querySelectorAll('.service-item');
const serviceCount = serviceItems.length;
const serviceCountInfo = document.getElementById('service-count-info');
serviceCountInfo.textContent = `선택된 시술: ${serviceCount}`;
if (serviceCount === 1) {
serviceCountInfo.className = 'service-count-info single';
serviceCountInfo.textContent = `선택된 시술: ${serviceCount}개 (최소 필수)`;
} else {
serviceCountInfo.className = 'service-count-info';
}
serviceItems.forEach(item => {
const delBtn = item.querySelector('.del');
if (serviceCount <= 1) {
delBtn.className = 'del disabled';
delBtn.title = '최소 1개의 시술은 필요합니다';
} else {
delBtn.className = 'del';
delBtn.title = '삭제';
}
});
}
// 캘린더 데이터
const today = new Date();
let selectedYear = today.getFullYear();
let selectedMonth = today.getMonth();
let selectedDay = today.getDate();
let selectedDate = null;
let selectedTime = null;
const calendarTitle = document.getElementById('calendar-title');
const calendarTable = document.getElementById('calendar-table');
const timeSlots = document.getElementById('time-slots');
const personCount = document.getElementById('person-count');
// 폼 요소들
const form = document.getElementById('reserve-form');
const agree = document.getElementById('agree');
const submitBtn = document.getElementById('submit-btn');
const step02Title = document.getElementById('step02-title');
const step03Title = document.getElementById('step03-title');
// 이벤트 리스너 등록
form.addEventListener('input', function(e) {
if (e.target.id !== 'customer-phone' && e.target.id !== 'birthDate') {
setTimeout(checkForm, 10);
}
});
agree.addEventListener('change', checkForm);
function checkForm() {
const name = document.getElementById('customer-name').value.trim();
let phoneValid = false;
try {
if (typeof PhoneValidator !== 'undefined' &&
PhoneValidator.isValid &&
typeof PhoneValidator.isValid === 'function') {
phoneValid = PhoneValidator.isValid('customer-phone');
} else {
const phone = document.getElementById('customer-phone').value.trim();
phoneValid = phone.match(/^01[0-9]{8,9}$/);
}
} catch (e) {
console.warn('PhoneValidator error:', e);
const phone = document.getElementById('customer-phone').value.trim();
phoneValid = phone.match(/^01[0-9]{8,9}$/);
}
let birthDateValid = false;
try {
if (birthDateValidator && typeof birthDateValidator.isValid === 'function') {
birthDateValid = birthDateValidator.isValid();
} else {
const birthDate = document.getElementById('birthDate').value.trim();
birthDateValid = birthDate.match(/^\d{8}$/);
}
} catch (e) {
console.warn('BirthDateValidator error:', e);
const birthDate = document.getElementById('birthDate').value.trim();
birthDateValid = birthDate.match(/^\d{8}$/);
}
const conditions = {
date: !!selectedDate,
time: !!selectedTime,
name: !!name,
phone: phoneValid,
birthDate: birthDateValid,
agree: agree.checked
};
const valid = Object.values(conditions).every(Boolean);
submitBtn.disabled = !valid;
const completedSteps = Object.values(conditions).filter(Boolean).length;
const totalSteps = Object.keys(conditions).length;
updateStepStatus(conditions);
updateButtonText(conditions, completedSteps, totalSteps);
if (valid) {
submitBtn.className = 'submit-btn ready';
} else {
submitBtn.className = 'submit-btn step-progress';
}
}
function updateStepStatus(conditions) {
if (conditions.date && conditions.time) {
step02Title.className = 'step-title completed';
step02Title.textContent = 'STEP 02. 예약 시간 선택 ✓';
} else {
step02Title.className = 'step-title';
step02Title.textContent = 'STEP 02. 예약 시간 선택';
}
if (conditions.name && conditions.phone && conditions.birthDate && conditions.agree) {
step03Title.className = 'step-title completed';
step03Title.textContent = 'STEP 03. 고객정보 ✓';
} else {
step03Title.className = 'step-title';
step03Title.textContent = 'STEP 03. 고객정보';
}
}
function updateButtonText(conditions, completedSteps, totalSteps) {
if (!conditions.date) {
submitBtn.textContent = '📅 예약 날짜를 선택해주세요';
} else if (!conditions.time) {
submitBtn.textContent = '⏰ 예약 시간을 선택해주세요';
} else if (!conditions.name) {
submitBtn.textContent = '👤 고객명을 입력해주세요';
} else if (!conditions.birthDate) {
submitBtn.textContent = '📅 생년월일을 올바르게 입력해주세요';
} else if (!conditions.phone) {
submitBtn.textContent = '📱 연락처를 올바르게 입력해주세요';
} else if (!conditions.agree) {
submitBtn.textContent = '✅ 개인정보 동의를 체크해주세요';
} else {
submitBtn.textContent = '🎉 시술 예약하기';
}
if (completedSteps < totalSteps) {
const progressText = ` (${completedSteps}/${totalSteps})`;
submitBtn.textContent += progressText;
}
}
// 새로운 진료시간 설정
const diettimes_mon_wed_fri = [
"10:00","10:30","11:00","11:30","12:00","12:30",
"13:00","13:30","14:00","14:30","15:00","15:30",
"16:00","16:30","17:00","17:30","18:00","18:30"
];
const diettimes_tue_thu = [
"10:00","10:30","11:00","11:30","12:00","12:30",
"13:00","13:30","14:00","14:30","15:00","15:30",
"16:00","16:30","17:00","17:30","18:00","18:30",
"19:00","19:30"
];
const diettimes_sat_short = [
"10:00","10:30","11:00","11:30","12:00","12:30",
"13:00","13:30","14:00","14:30","15:00","15:30"
];
// 캘린더 렌더링 (일요일만 휴무)
function renderCalendar(year, month) {
calendarTitle.textContent = `${year}.${(month+1).toString().padStart(2,'0')}`;
const firstDay = new Date(year, month, 1);
const lastDay = new Date(year, month+1, 0);
const now = new Date();
const todayDate = new Date(now.getFullYear(), now.getMonth(), now.getDate());
let html = '<thead><tr>';
['일','월','화','수','목','금','토'].forEach(d => html += `<th>${d}</th>`);
html += '</tr></thead><tbody><tr>';
for(let i = 0; i < firstDay.getDay(); i++) {
html += '<td class="disabled"></td>';
}
for(let d = 1; d <= lastDay.getDate(); d++) {
const dateObj = new Date(year, month, d);
let classes = [];
const isPastDate = dateObj < todayDate;
const isSunday = dateObj.getDay() === 0;
const isHoliday = isDateDisabled(dateObj);
if(dateObj.toDateString() === today.toDateString()) classes.push('today');
if(selectedDate && dateObj.toDateString() === selectedDate.toDateString()) classes.push('selected');
// ✅ 수요일 완전 제거! 일요일 + 공휴일만 비활성화
if(isPastDate || isSunday || isHoliday) {
classes.push('disabled');
}
const clickHandler = (isPastDate || isSunday || isHoliday) ?
'' : `onclick="selectDate(${year},${month},${d})"`;
html += `<td class="${classes.join(' ')}" ${clickHandler}>${d}</td>`;
if((firstDay.getDay()+d) % 7 === 0 && d !== lastDay.getDate()) html += '</tr><tr>';
}
for(let i = (lastDay.getDay()+1) % 7; i && i < 7; i++) {
html += '<td class="disabled"></td>';
}
html += '</tr></tbody>';
calendarTable.innerHTML = html;
checkForm();
}
function selectDate(y, m, d) {
const tempDate = new Date(y, m, d);
const now = new Date();
const todayDate = new Date(now.getFullYear(), now.getMonth(), now.getDate());
if (tempDate < todayDate) {
alert('과거 날짜는 선택할 수 없습니다.');
return;
}
if (tempDate.getDay() === 0) {
alert('일요일은 휴무일입니다.');
return;
}
if (isDateDisabled(tempDate)) {
alert('해당 날짜는 휴무일입니다.');
return;
}
// ✅ 수요일 체크 완전 제거!
selectedDate = tempDate;
renderCalendar(y, m);
renderTimeSlots();
checkForm();
}
// 월 이동 버튼
document.getElementById('prev-month').onclick = function() {
if(selectedMonth === 0) {
selectedYear--;
selectedMonth = 11;
} else {
selectedMonth--;
}
renderCalendar(selectedYear, selectedMonth);
renderTimeSlots();
checkForm();
};
document.getElementById('next-month').onclick = function() {
if(selectedMonth === 11) {
selectedYear++;
selectedMonth = 0;
} else {
selectedMonth++;
}
renderCalendar(selectedYear, selectedMonth);
renderTimeSlots();
checkForm();
};
// 시간 슬롯 렌더링 (새로운 진료시간 적용)
function renderTimeSlots() {
let html = '';
let dayOfWeek = selectedDate ? selectedDate.getDay() : null;
let slotArr = [];
if (!selectedDate || dayOfWeek === 0 || isDateDisabled(selectedDate)) {
timeSlots.innerHTML = '';
personCount.textContent = selectedDate ? 1 : '-';
return;
}
// 요일별 / 특수일별 시간대 설정
if (isShortWorkingDate(selectedDate)) {
// 12/24, 12/31 단축 근무: 10:00~15:30
slotArr = diettimes_sat_short;
} else if ([1,3,5].includes(dayOfWeek)) { // 월(1), 수(3), 금(5)
slotArr = diettimes_mon_wed_fri; // 10:00~18:30
} else if ([2,4].includes(dayOfWeek)) { // 화(2), 목(4)
slotArr = diettimes_tue_thu; // 10:00~19:30
} else if (dayOfWeek === 6) { // 토(6)
slotArr = diettimes_sat_short; // 10:00~15:30
}
const now = new Date();
const todayDate = new Date(now.getFullYear(), now.getMonth(), now.getDate());
const isToday = selectedDate && selectedDate.toDateString() === todayDate.toDateString();
slotArr.forEach(t => {
let isDisabled = false;
let disabledClass = '';
let clickHandler = `onclick="selectTimeAndCall('${t}', this)"`;
if (isToday) {
const currentTime = now.getHours() * 100 + now.getMinutes();
const [hour, minute] = t.split(':').map(Number);
const slotTime = hour * 100 + minute;
if (slotTime <= currentTime) {
isDisabled = true;
disabledClass = ' disabled';
clickHandler = '';
}
}
const selectedClass = selectedTime === t && !isDisabled ? ' selected' : '';
html += `<button type="button" class="time-btn${selectedClass}${disabledClass}" ${clickHandler} ${isDisabled ? 'disabled' : ''}>${t}</button>`;
});
timeSlots.innerHTML = html;
personCount.textContent = selectedDate ? 1 : '-';
}
function selectTimeAndCall(t, el) {
const now = new Date();
const todayDate = new Date(now.getFullYear(), now.getMonth(), now.getDate());
const isToday = selectedDate && selectedDate.toDateString() === todayDate.toDateString();
if (isToday) {
const currentTime = now.getHours() * 100 + now.getMinutes();
const [hour, minute] = t.split(':').map(Number);
const slotTime = hour * 100 + minute;
if (slotTime <= currentTime) {
alert('지난 시간은 선택할 수 없습니다.');
return;
}
}
selectedTime = t;
renderTimeSlots();
if (el) {
document.querySelectorAll('.time-btn').forEach(btn => btn.classList.remove('active'));
el.classList.add('active');
}
onClickTime(getSelectedDateStr(), t, el);
checkForm();
}
function getSelectedDateStr() {
if (!selectedDate) return '';
const yyyy = selectedDate.getFullYear();
const mm = String(selectedDate.getMonth() + 1).padStart(2, '0');
const dd = String(selectedDate.getDate()).padStart(2, '0');
return `${yyyy}${mm}${dd}`;
}
window.selectTime = function(t) {
selectedTime = t;
renderTimeSlots();
}
// 초기 날짜 설정 (일요일/공휴일 제외)
let initDate = new Date(today.getFullYear(), today.getMonth(), today.getDate());
while (initDate.getDay() === 0 || isDateDisabled(initDate)) {
initDate.setDate(initDate.getDate() + 1);
}
selectDate(initDate.getFullYear(), initDate.getMonth(), initDate.getDate());
form.onsubmit = function(e) {
e.preventDefault();
if(!selectedDate || !selectedTime) {
alert('예약 날짜와 시간을 선택해 주세요.');
return;
}
if (!birthDateValidator || !birthDateValidator.isValid()) {
alert('올바른 생년월일을 입력해 주세요.');
return;
}
fn_reservation();
form.reset();
submitBtn.disabled = true;
checkForm();
};
function fn_reservation(){
let formData = new FormData();
if (selectedDate) {
const yyyy = selectedDate.getFullYear();
const mm = String(selectedDate.getMonth() + 1).padStart(2, '0');
const dd = String(selectedDate.getDate()).padStart(2, '0');
formData.append('SELECTED_DATE', `${yyyy}-${mm}-${dd}`);
}
if (selectedTime) {
formData.append('TIME', selectedTime);
}
formData.append('CATEGORY_DIV_CD', typeof category_div_cd !== 'undefined' ? category_div_cd : '');
formData.append('CATEGORY_NO', typeof category_no !== 'undefined' ? category_no : '');
formData.append('POST_NO', typeof post_no !== 'undefined' ? post_no : '');
formData.append('NAME', document.getElementById('customer-name').value);
formData.append('BIRTH_DATE', document.getElementById('birthDate').value);
formData.append('PHONE_NUMBER', document.getElementById('customer-phone').value);
formData.append('ETC', document.getElementById('customer-req').value);
formData.append('TREATMENT_INFOS', JSON.stringify(selectedTreatments));
$.ajax({
url: encodeURI('/webservice/insertReservation.do'),
data: formData,
dataType: 'json',
processData: false,
contentType: false,
type: 'POST',
async: true,
success: function(data){
if(data.msgCode=='0'){
alert('예약이 완료되었습니다.');
location.href="/webevent/selectListWebEventIntro.do";
}else{
modalEvent.danger("조회 오류", data.msgDesc);
}
},
error : function(xhr, status, error) {
modalEvent.danger("조회 오류", "조회 중 오류가 발생하였습니다. 잠시후 다시시도하십시오.");
},
beforeSend:function(){
$(".loading-image-layer").show();
},
complete:function(){
$(".loading-image-layer").hide();
}
});
}
function fn_SelectReservation(category_div_cd, category_no, post_no, procedure_id) {
let formData = new FormData();
formData.append('CATEGORY_DIV_CD', category_div_cd);
formData.append('CATEGORY_NO', category_no);
formData.append('POST_NO', post_no);
formData.append('PROCEDURE_ID', procedure_id);
$.ajax({
url: encodeURI('/webservice/selectReservation.do'),
data: formData,
dataType: 'json',
processData: false,
contentType: false,
type: 'POST',
async: true,
success: function(data) {
if(data.msgCode=='0') {
const serviceList = document.getElementById('service-list');
serviceList.innerHTML = '';
let totalprice = 0;
selectedTreatments = [];
if (data.reservation && data.reservation.length > 0) {
data.reservation.forEach(function(item, i) {
let price = item.DISCOUNT_PRICE != null ? item.DISCOUNT_PRICE : item.PRICE || 0;
totalprice += Number(price);
selectedTreatments.push({
MU_TREATMENT_ID: item.MU_TREATMENT_ID,
TREATMENT_NAME: item.TREATMENT_NAME,
TREATMENT_PROCEDURE_NAME: item.TREATMENT_PROCEDURE_NAME,
MU_TREATMENT_PROCEDURE_ID: item.MU_TREATMENT_PROCEDURE_ID
});
const li = document.createElement('div');
li.className = 'service-item';
li.innerHTML = `
<span>${item.TREATMENT_PROCEDURE_NAME}</span>
<span>
<span style="text-decoration:line-through; color:#bbb; font-size:0.95em; margin-right:6px;">${item.DISCOUNT_PRICE != null ? (item.PRICE || 0).toLocaleString() : ''}원</span>
<span class="price">${Number(price).toLocaleString()}원</span>
<span class="del" title="삭제" onclick="removeService(this)">×</span>
</span>
`;
serviceList.appendChild(li);
});
}
document.getElementById('total-price').textContent = totalprice.toLocaleString() + '원';
updateServiceCount();
} else {
modalEvent.danger("조회 오류", data.msgDesc);
}
},
error : function(xhr, status, error) {
modalEvent.danger("조회 오류", "조회 중 오류가 발생하였습니다. 잠시후 다시시도하십시오.");
},
beforeSend:function() {
$(".loading-image-layer").show();
},
complete:function() {
$(".loading-image-layer").hide();
}
});
}
function onClickTime(selectedDate, time, el) {
document.querySelectorAll('.time-btn').forEach(btn => btn.classList.remove('active'));
if (el) el.classList.add('active');
let formData = new FormData();
formData.append('SELECTED_DATE', selectedDate);
formData.append('TIME', time);
res_date = selectedDate;
res_time = time;
$.ajax({
url: encodeURI('/webservice/selectReservationCnt.do'),
data: formData,
dataType: 'json',
processData: false,
contentType: false,
type: 'POST',
async: true,
success: function(data){
if(data.msgCode=='0'){
if (data.rows && data.rows.RES_CNT !== undefined) {
personCount.textContent = data.rows.RES_CNT;
} else {
personCount.textContent = '-';
}
}else{
modalEvent.danger("조회 오류", data.msgDesc);
}
},
error : function(xhr, status, error) {
modalEvent.danger("조회 오류", "조회 중 오류가 발생하였습니다. 잠시후 다시시도하십시오.");
},
beforeSend:function(){
$(".loading-image-layer").show();
},
complete:function(){
$(".loading-image-layer").hide();
}
});
}
$(document).ready(function() {
// PhoneValidator 초기화
try {
if (typeof PhoneValidator !== 'undefined' && PhoneValidator.init) {
PhoneValidator.init('customer-phone', {
showMessage: true,
realTimeValidation: true,
maxLength: 11,
allowedPrefixes: ['010', '011', '016', '017', '018', '019'],
onValidationChange: function(result, phoneNumber) {
setTimeout(checkForm, 10);
}
});
}
} catch (e) {
console.error('PhoneValidator initialization error:', e);
}
// BirthDateValidator 초기화
try {
birthDateValidator = new BirthDateValidator('birthDate', {
showMessage: true,
realTimeValidation: true,
minAge: 0,
maxAge: 150,
format: 'YYYYMMDD',
allowFuture: false,
onValidationChange: function(result, birthDate) {
setTimeout(checkForm, 10);
}
});
} catch (e) {
console.error('BirthDateValidator initialization error:', e);
}
$('#customer-phone, #birthDate').on('input keyup blur paste', function() {
setTimeout(checkForm, 50);
});
setTimeout(() => {
checkForm();
updateServiceCount();
}, 300);
});
</script>
</th:block>
</html>