1421 lines
55 KiB
HTML
1421 lines
55 KiB
HTML
<!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">
|
||
<style>
|
||
* {
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
body {
|
||
font-family: 'Noto Sans KR', sans-serif;
|
||
margin: 0;
|
||
background: #fafafa;
|
||
color: #222;
|
||
height: 100vh;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
/* Main container */
|
||
.reservation-container {
|
||
display: flex;
|
||
max-width: 1280px !important;
|
||
margin: 0 auto;
|
||
gap: 20px;
|
||
height: 100%;
|
||
flex: 1;
|
||
padding: 20px 0;
|
||
margin-top:65px;
|
||
}
|
||
|
||
.box {
|
||
background: #fff;
|
||
border-radius: 16px;
|
||
box-shadow: 0 2px 8px rgba(0,0,0,0.06);
|
||
padding: 24px 16px;
|
||
flex: 1 1 0;
|
||
min-width: 260px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
height: fit-content;
|
||
min-height: 600px;
|
||
}
|
||
|
||
.step-title {
|
||
color: #b23c3c;
|
||
font-weight: 700;
|
||
font-size: 1.1em;
|
||
margin-bottom: 12px;
|
||
letter-spacing: 0.02em;
|
||
transition: color 0.3s ease;
|
||
}
|
||
|
||
.step-title.completed {
|
||
color: #008000 !important;
|
||
}
|
||
|
||
/* Service section */
|
||
.service-list {
|
||
flex: 1;
|
||
margin-bottom: 24px;
|
||
}
|
||
|
||
.service-item {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
font-size: 1.1em;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.service-item .del {
|
||
cursor: pointer;
|
||
color: #b23c3c;
|
||
margin-left: 8px;
|
||
font-size: 1.2em;
|
||
transition: color 0.3s ease, opacity 0.3s ease;
|
||
}
|
||
|
||
.service-item .del:hover {
|
||
color: #ff0000;
|
||
}
|
||
|
||
.service-item .del.disabled {
|
||
color: #ccc;
|
||
cursor: not-allowed;
|
||
opacity: 0.5;
|
||
}
|
||
|
||
.service-item .del.disabled:hover {
|
||
color: #ccc;
|
||
}
|
||
|
||
.service-item .price {
|
||
font-weight: bold;
|
||
color: #b23c3c;
|
||
}
|
||
|
||
/* Service count indicator */
|
||
.service-count-info {
|
||
font-size: 0.85em;
|
||
color: #888;
|
||
margin-bottom: 8px;
|
||
text-align: center;
|
||
padding: 4px 8px;
|
||
background: #f8f9fa;
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.service-count-info.single {
|
||
color: #ff8c00;
|
||
background: #fff3e0;
|
||
border: 1px solid #ffcc80;
|
||
}
|
||
|
||
.total {
|
||
border-top: 1px solid #eee;
|
||
margin-top: auto;
|
||
padding-top: 16px;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
font-size: 1.1em;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.total .price {
|
||
color: #b23c3c;
|
||
}
|
||
|
||
.total small {
|
||
font-weight: normal;
|
||
color: #888;
|
||
font-size: 0.9em;
|
||
margin-right: 8px;
|
||
}
|
||
|
||
/* Calendar section */
|
||
.calendar-box {
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.calendar-header {
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
gap: 8px;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.calendar-header button {
|
||
background: none;
|
||
border: none;
|
||
font-size: 1.2em;
|
||
cursor: pointer;
|
||
color: #b23c3c;
|
||
padding: 0 6px;
|
||
}
|
||
|
||
.calendar-table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
text-align: center;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.calendar-table th,
|
||
.calendar-table td {
|
||
width: 2em;
|
||
height: 2em;
|
||
padding: 2px;
|
||
font-size: 1em;
|
||
border-radius: 50%;
|
||
cursor: pointer;
|
||
transition: background 0.15s;
|
||
}
|
||
|
||
.calendar-table th {
|
||
color: #b23c3c;
|
||
font-weight: 500;
|
||
background: none;
|
||
}
|
||
|
||
.calendar-table td.selected {
|
||
background: #b23c3c;
|
||
color: #fff;
|
||
}
|
||
|
||
.calendar-table td.today {
|
||
border: 1.5px solid #b23c3c;
|
||
}
|
||
|
||
.calendar-table td:not(.selected):hover {
|
||
background: #f5eaea;
|
||
}
|
||
|
||
.calendar-table td.disabled {
|
||
color: #ccc;
|
||
pointer-events: none;
|
||
background: none;
|
||
}
|
||
|
||
/* Time slots */
|
||
.time-slots {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 8px;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.time-btn {
|
||
flex: 1 0 30%;
|
||
min-width: 80px;
|
||
padding: 8px 0;
|
||
border: 1px solid #b23c3c;
|
||
border-radius: 20px;
|
||
background: #fff;
|
||
color: #b23c3c;
|
||
font-size: 1em;
|
||
cursor: pointer;
|
||
transition: background 0.15s, color 0.15s;
|
||
}
|
||
|
||
.time-btn.selected,
|
||
.time-btn:active {
|
||
background: #b23c3c;
|
||
color: #fff;
|
||
}
|
||
|
||
.time-btn:disabled {
|
||
color: #ccc;
|
||
border-color: #eee;
|
||
background: #f5f5f5;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
.person-count {
|
||
margin-top: 12px;
|
||
font-size: 0.98em;
|
||
color: #888;
|
||
}
|
||
|
||
/* Form section */
|
||
.form-group {
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.form-group label {
|
||
display: block;
|
||
margin-bottom: 4px;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.form-group input,
|
||
.form-group textarea {
|
||
width: 100%;
|
||
padding: 8px 10px;
|
||
border: 1px solid #ddd;
|
||
border-radius: 6px;
|
||
font-size: 1em;
|
||
resize: none;
|
||
transition: border-color 0.3s ease;
|
||
}
|
||
|
||
.form-group textarea {
|
||
min-height: 60px;
|
||
}
|
||
|
||
.checkbox-group {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 18px;
|
||
font-size: 0.98em;
|
||
}
|
||
|
||
.checkbox-group input[type="checkbox"] {
|
||
margin-right: 8px;
|
||
accent-color: #b23c3c;
|
||
}
|
||
|
||
.submit-btn {
|
||
width: 100%;
|
||
padding: 14px 0;
|
||
background: #ddd;
|
||
color: #888;
|
||
border: none;
|
||
border-radius: 8px;
|
||
font-size: 1.1em;
|
||
font-weight: bold;
|
||
cursor: not-allowed;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.submit-btn.step-progress {
|
||
background: linear-gradient(45deg, #ddd, #bbb);
|
||
color: #666;
|
||
font-size: 0.95em;
|
||
}
|
||
|
||
.submit-btn.ready {
|
||
background: #b23c3c;
|
||
color: #fff;
|
||
cursor: pointer;
|
||
animation: pulse 2s infinite;
|
||
}
|
||
|
||
@keyframes pulse {
|
||
0% { transform: scale(1); }
|
||
50% { transform: scale(1.02); }
|
||
100% { transform: scale(1); }
|
||
}
|
||
|
||
/* Phone message styles (common.js PhoneValidator용) */
|
||
.phone-message {
|
||
font-size: 12px;
|
||
margin-top: 5px;
|
||
padding: 4px 8px;
|
||
border-radius: 4px;
|
||
font-weight: 500;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.phone-message.error {
|
||
color: #ff0000;
|
||
background-color: #ffebee;
|
||
border: 1px solid #ffcdd2;
|
||
}
|
||
|
||
.phone-message.warning {
|
||
color: #ff8c00;
|
||
background-color: #fff3e0;
|
||
border: 1px solid #ffcc80;
|
||
}
|
||
|
||
.phone-message.success {
|
||
color: #008000;
|
||
background-color: #f1f8e9;
|
||
border: 1px solid #c8e6c9;
|
||
}
|
||
|
||
.phone-message.info {
|
||
color: #2196f3;
|
||
background-color: #e3f2fd;
|
||
border: 1px solid #90caf9;
|
||
}
|
||
|
||
/* Birth date message styles */
|
||
.birth-date-message {
|
||
font-size: 12px;
|
||
margin-top: 5px;
|
||
padding: 4px 8px;
|
||
border-radius: 4px;
|
||
font-weight: 500;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.birth-date-message.error {
|
||
color: #ff0000;
|
||
background-color: #ffebee;
|
||
border: 1px solid #ffcdd2;
|
||
}
|
||
|
||
.birth-date-message.warning {
|
||
color: #ff8c00;
|
||
background-color: #fff3e0;
|
||
border: 1px solid #ffcc80;
|
||
}
|
||
|
||
.birth-date-message.success {
|
||
color: #008000;
|
||
background-color: #f1f8e9;
|
||
border: 1px solid #c8e6c9;
|
||
}
|
||
|
||
.birth-date-message.info {
|
||
color: #2196f3;
|
||
background-color: #e3f2fd;
|
||
border: 1px solid #90caf9;
|
||
}
|
||
|
||
/* Responsive Design */
|
||
@media (max-width: 1199.98px) {
|
||
.reservation-container {
|
||
max-width: 960px !important;
|
||
gap: 16px;
|
||
padding: 20px 15px;
|
||
}
|
||
.box {
|
||
padding: 20px 14px;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 991.98px) {
|
||
.reservation-container {
|
||
max-width: 720px !important;
|
||
flex-direction: column;
|
||
gap: 24px;
|
||
height: auto;
|
||
padding: 20px 15px;
|
||
}
|
||
.box {
|
||
min-height: unset;
|
||
min-width: unset;
|
||
}
|
||
|
||
}
|
||
|
||
@media (max-width: 767.98px) {
|
||
.reservation-container {
|
||
max-width: 540px !important;
|
||
padding: 15px;
|
||
gap: 20px;
|
||
}
|
||
.box {
|
||
padding: 16px 12px;
|
||
}
|
||
.calendar-table th,
|
||
.calendar-table td {
|
||
width: 1.8em;
|
||
height: 1.8em;
|
||
}
|
||
.time-btn {
|
||
min-width: 70px;
|
||
font-size: 0.95em;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 575.98px) {
|
||
.reservation-container {
|
||
max-width: 100% !important;
|
||
padding: 10px;
|
||
gap: 16px;
|
||
}
|
||
.box {
|
||
padding: 16px 8px;
|
||
}
|
||
.calendar-table th,
|
||
.calendar-table td {
|
||
width: 1.5em;
|
||
height: 1.5em;
|
||
font-size: 0.9em;
|
||
}
|
||
.time-btn {
|
||
min-width: 60px;
|
||
font-size: 0.9em;
|
||
padding: 6px 0;
|
||
}
|
||
.step-title {
|
||
font-size: 1em;
|
||
}
|
||
.service-item {
|
||
font-size: 1em;
|
||
}
|
||
}
|
||
</style>
|
||
</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="이전 달"><</button>
|
||
<span id="calendar-title" style="font-weight:600;"></span>
|
||
<button id="next-month" aria-label="다음 달">></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>
|
||
// 생년월일 검증 클래스
|
||
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', // YYYYMMDD, YYYY-MM-DD
|
||
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까지 입력 가능합니다.' };
|
||
}
|
||
|
||
// Date 객체로 실제 날짜 존재 여부 확인
|
||
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일이 없습니다.' };
|
||
}
|
||
}
|
||
|
||
// 2월 윤년 체크
|
||
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;
|
||
|
||
// 초기화
|
||
fn_SelectReservation(category_div_cd, category_no, post_no, procedure_id);
|
||
|
||
// 개선된 시술 삭제 함수 (1개일 때 삭제 방지)
|
||
function removeService(el) {
|
||
const serviceItems = document.querySelectorAll('.service-item');
|
||
const serviceCount = serviceItems.length;
|
||
|
||
// 시술이 1개만 남았을 때 삭제 방지
|
||
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();
|
||
|
||
// selectedTreatments 배열에서도 제거 (인덱스 기반)
|
||
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}개`;
|
||
|
||
// 1개일 때 스타일 변경
|
||
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); // Validator 처리 완료 대기
|
||
}
|
||
});
|
||
agree.addEventListener('change', checkForm);
|
||
|
||
// 개선된 checkForm 함수 (BirthDateValidator 연계)
|
||
function checkForm() {
|
||
const name = document.getElementById('customer-name').value.trim();
|
||
|
||
// common.js의 PhoneValidator 메서드 사용
|
||
let phoneValid = false;
|
||
try {
|
||
if (typeof PhoneValidator !== 'undefined' &&
|
||
PhoneValidator.isValid &&
|
||
typeof PhoneValidator.isValid === 'function') {
|
||
phoneValid = PhoneValidator.isValid('customer-phone');
|
||
} else {
|
||
// fallback: 기본 정규식 검증
|
||
const phone = document.getElementById('customer-phone').value.trim();
|
||
phoneValid = phone.match(/^01[0-9]{8,9}$/);
|
||
}
|
||
} catch (e) {
|
||
console.warn('PhoneValidator error:', e);
|
||
// fallback 검증
|
||
const phone = document.getElementById('customer-phone').value.trim();
|
||
phoneValid = phone.match(/^01[0-9]{8,9}$/);
|
||
}
|
||
|
||
// BirthDateValidator 사용
|
||
let birthDateValid = false;
|
||
try {
|
||
if (birthDateValidator && typeof birthDateValidator.isValid === 'function') {
|
||
birthDateValid = birthDateValidator.isValid();
|
||
} else {
|
||
// fallback: 기본 검증
|
||
const birthDate = document.getElementById('birthDate').value.trim();
|
||
birthDateValid = birthDate.match(/^\d{8}$/);
|
||
}
|
||
} catch (e) {
|
||
console.warn('BirthDateValidator error:', e);
|
||
// fallback 검증
|
||
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);
|
||
|
||
// CSS 클래스 업데이트
|
||
if (valid) {
|
||
submitBtn.className = 'submit-btn ready';
|
||
} else {
|
||
submitBtn.className = 'submit-btn step-progress';
|
||
}
|
||
}
|
||
|
||
function updateStepStatus(conditions) {
|
||
// STEP 02 상태 업데이트
|
||
if (conditions.date && conditions.time) {
|
||
step02Title.className = 'step-title completed';
|
||
step02Title.textContent = 'STEP 02. 예약 시간 선택 ✓';
|
||
} else {
|
||
step02Title.className = 'step-title';
|
||
step02Title.textContent = 'STEP 02. 예약 시간 선택';
|
||
}
|
||
|
||
// STEP 03 상태 업데이트
|
||
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;
|
||
}
|
||
}
|
||
|
||
// 캘린더 렌더링
|
||
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;
|
||
|
||
if(dateObj.toDateString() === today.toDateString()) classes.push('today');
|
||
if(selectedDate && dateObj.toDateString() === selectedDate.toDateString()) classes.push('selected');
|
||
if(isPastDate || dateObj.getDay() === 0) classes.push('disabled');
|
||
|
||
const clickHandler = (isPastDate || dateObj.getDay() === 0) ? '' : `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('일요일은 선택할 수 없습니다.');
|
||
const nextMonday = new Date(now);
|
||
if (now.getDay() === 0) {
|
||
nextMonday.setDate(now.getDate() + 1);
|
||
} else {
|
||
nextMonday.setDate(now.getDate() + (8 - now.getDay()));
|
||
}
|
||
selectedDate = new Date(nextMonday.getFullYear(), nextMonday.getMonth(), nextMonday.getDate());
|
||
renderCalendar(selectedDate.getFullYear(), selectedDate.getMonth());
|
||
renderTimeSlots();
|
||
checkForm();
|
||
return;
|
||
}
|
||
|
||
selectedDate = tempDate;
|
||
renderCalendar(y, m);
|
||
renderTimeSlots();
|
||
checkForm();
|
||
}
|
||
|
||
// 월 이동 버튼 이벤트 (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(); // 월 변경 시 폼 검증
|
||
};
|
||
|
||
// 시간대 정의
|
||
const diettimes_mon_fri = [
|
||
"10:00","10:30", "11:00","11:30", "12:00","12:30",
|
||
"13:00","13:30", "15:00","15:30", "16:00","16:30", "17:00","17:30",
|
||
"18:00","18:30"
|
||
];
|
||
|
||
const diettimes_tue_ths = [
|
||
"10:00","10:30", "11:00","11:30", "12:00","12:30",
|
||
"13:00","13:30", "15:00","15:30", "16:00","16:30", "17:00","17:30",
|
||
"18:00","18:30", "19:00","19:30"
|
||
];
|
||
|
||
const diettimes_wes_sat = [
|
||
"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 renderTimeSlots() {
|
||
let html = '';
|
||
let dayOfWeek = selectedDate ? selectedDate.getDay() : null;
|
||
let slotArr = [];
|
||
|
||
if (dayOfWeek === 0) {
|
||
timeSlots.innerHTML = '';
|
||
personCount.textContent = selectedDate ? 1 : '-';
|
||
return;
|
||
}
|
||
|
||
if (dayOfWeek !== null) {
|
||
if (dayOfWeek === 1 || dayOfWeek === 5) { // 월(1), 금(5)
|
||
slotArr = diettimes_mon_fri;
|
||
} else if (dayOfWeek === 2 || dayOfWeek === 4) { // 화(2), 목(4)
|
||
slotArr = diettimes_tue_ths;
|
||
} else if (dayOfWeek === 3 || dayOfWeek === 6) { // 수(3), 토(6)
|
||
slotArr = diettimes_wes_sat;
|
||
}
|
||
}
|
||
|
||
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 : '-';
|
||
}
|
||
|
||
// 시간 선택 시 selectedTime 설정 및 onClickTime 호출 (checkForm 추가)
|
||
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();
|
||
}
|
||
|
||
// 선택된 날짜를 yyyymmdd 문자열로 반환
|
||
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());
|
||
if (initDate.getDay() === 0) {
|
||
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(); // 폼 리셋 후 재검증
|
||
};
|
||
|
||
let selectedTreatments = [];
|
||
|
||
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);
|
||
}
|
||
|
||
var date = new Date();
|
||
if(String(new Date()).split(" ") == "Sun") {
|
||
date = new Date();
|
||
date.setDate(date.getDate() + 1);
|
||
}
|
||
},
|
||
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.toString().substr(0, 4) + '-' + selectedDate.toString().substr(4, 2) + '-' + selectedDate.toString().substr(6, 2));
|
||
formData.append('TIME', time);
|
||
|
||
res_date = selectedDate.toString().substr(0, 4) + '-' + selectedDate.toString().substr(4, 2) + '-' + selectedDate.toString().substr(6, 2);
|
||
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() {
|
||
// common.js의 PhoneValidator 초기화
|
||
try {
|
||
if (typeof PhoneValidator !== 'undefined' && PhoneValidator.init) {
|
||
console.log('Initializing PhoneValidator from common.js');
|
||
PhoneValidator.init('customer-phone', {
|
||
showMessage: true,
|
||
realTimeValidation: true,
|
||
maxLength: 11,
|
||
allowedPrefixes: ['010', '011', '016', '017', '018', '019'],
|
||
onValidationChange: function(result, phoneNumber) {
|
||
// PhoneValidator 상태 변경 시 checkForm 호출
|
||
setTimeout(checkForm, 10);
|
||
}
|
||
});
|
||
} else {
|
||
console.warn('PhoneValidator not found in common.js');
|
||
}
|
||
} catch (e) {
|
||
console.error('PhoneValidator initialization error:', e);
|
||
}
|
||
|
||
// BirthDateValidator 초기화
|
||
try {
|
||
console.log('Initializing BirthDateValidator');
|
||
birthDateValidator = new BirthDateValidator('birthDate', {
|
||
showMessage: true,
|
||
realTimeValidation: true,
|
||
minAge: 0,
|
||
maxAge: 150,
|
||
format: 'YYYYMMDD',
|
||
allowFuture: false,
|
||
onValidationChange: function(result, birthDate) {
|
||
// BirthDateValidator 상태 변경 시 checkForm 호출
|
||
setTimeout(checkForm, 10);
|
||
}
|
||
});
|
||
console.log('BirthDateValidator initialized successfully');
|
||
} catch (e) {
|
||
console.error('BirthDateValidator initialization error:', e);
|
||
}
|
||
|
||
// PhoneValidator와 BirthDateValidator 이벤트 감지 (fallback 및 보완)
|
||
$('#customer-phone').on('input keyup blur paste', function() {
|
||
setTimeout(checkForm, 50);
|
||
});
|
||
|
||
$('#birthDate').on('input keyup blur paste', function() {
|
||
setTimeout(checkForm, 50);
|
||
});
|
||
|
||
// 초기 폼 상태 체크
|
||
setTimeout(() => {
|
||
checkForm();
|
||
updateServiceCount(); // 초기 시술 개수 상태 설정
|
||
}, 300); // Validator들 초기화 완료 대기
|
||
});
|
||
</script>
|
||
</th:block>
|
||
</html>
|