Files
dnssash/adm/order_manage/components/expert_visit_popup.php
T
2026-06-11 18:47:38 +09:00

727 lines
35 KiB
PHP
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.
<?php
/**
* 전문가 방문 예약 팝업 UI 및 데이터 처리
*/
// AJAX 요청 처리
if (isset($_POST['action'])) {
include_once('../_common_con.php');
include_once('../lib/notification_helper.php');
header('Content-Type: application/json');
$action = $_POST['action'] ?? '';
$response = ['success' => false, 'message' => '알 수 없는 요청입니다.'];
// 월별 예약 가능일 조회
if ($action === 'get_expert_visit_month_availability') {
$year = (int) ($_POST['year'] ?? 0);
$month = (int) ($_POST['month'] ?? 0);
$expert_id = $_POST['expert_id'] ?? ''; // 전문가 ID 추가
if ($year && $month) {
$start_date = date('Y-m-d', mktime(0, 0, 0, $month, 1, $year));
$end_date = date('Y-m-t', strtotime($start_date));
$max_advance_days = get_order_config('expert_visit_max_advance_days', 30);
$max_date = date('Y-m-d', strtotime("+" . $max_advance_days . " days"));
// 날짜별로 루프를 돌며 가용성을 체크합니다.
// 쿼리 하나로 처리하기 복잡하므로, 기간 내의 모든 스케줄을 가져와서 PHP에서 병합합니다.
// 1. 기간 내의 모든 '지정일' 스케줄 가져오기
$specific_schedules = [];
$sql_specific = "SELECT * FROM expert_visit_schedules
WHERE specific_date BETWEEN '{$start_date}' AND '{$end_date}'
AND (expert_id = '{$expert_id}' OR expert_id IS NULL)
ORDER BY expert_id DESC"; // 전문가 설정 우선
$res_specific = sql_query($sql_specific);
while ($row = sql_fetch_array($res_specific)) {
$date = $row['specific_date'];
if (!isset($specific_schedules[$date])) { // 이미 전문가 설정이 있으면 공통 설정은 무시
$specific_schedules[$date] = $row;
}
}
// 2. '요일별' 스케줄 가져오기
$weekly_schedules = [];
$sql_weekly = "SELECT * FROM expert_visit_schedules
WHERE day_of_week IS NOT NULL
AND (expert_id = '{$expert_id}' OR expert_id IS NULL)
ORDER BY expert_id DESC"; // 전문가 설정 우선
$res_weekly = sql_query($sql_weekly);
while ($row = sql_fetch_array($res_weekly)) {
$dow = $row['day_of_week'];
if (!isset($weekly_schedules[$dow])) {
$weekly_schedules[$dow] = $row;
}
}
$availability = [];
$current = strtotime($start_date);
$end = strtotime($end_date);
while ($current <= $end) {
$date_str = date('Y-m-d', $current);
$day_num = date('j', $current);
$dow = date('N', $current); // 1(월) ~ 7(일)
// 우선순위: 지정일 > 요일별
$schedule = $specific_schedules[$date_str] ?? ($weekly_schedules[$dow] ?? null);
$is_bookable = false;
$reason = '';
if (!$schedule) {
$reason = 'no_schedule'; // 스케줄 없음
} elseif ($schedule['is_available'] == 0) {
$reason = 'holiday'; // 휴무
} elseif ($date_str < date('Y-m-d')) {
$reason = 'past_date'; // 지난 날짜
} elseif ($date_str > $max_date) {
$reason = 'too_far'; // 예약 가능 기간 초과
} else {
// 예약 꽉 찼는지 확인
// 해당 날짜, 해당 전문가(또는 전체)의 예약 건수 확인
// 시간대별로 체크해야 정확하지만, 여기서는 '하루 전체 마감' 여부를 대략적으로 판단하거나
// 일단 '가능'으로 표시하고 시간 선택에서 막을 수 있습니다.
// 정확도를 위해 해당 날짜의 총 슬롯 수와 예약 수를 비교합니다.
$start_time = strtotime($date_str . ' ' . $schedule['start_time']);
$end_time = strtotime($date_str . ' ' . $schedule['end_time']);
$slot_duration = $schedule['time_slot'] * 60;
$total_slots = 0;
for ($t = $start_time; $t < $end_time; $t += $slot_duration) {
$total_slots++;
}
$max_capacity = $total_slots * $schedule['max_persons'];
// 예약된 건수 조회
$sql_reserved = "SELECT COUNT(*) as cnt FROM expert_visit_reservations
WHERE visit_date = '{$date_str}' AND status != 'cancelled'";
if ($expert_id) {
$sql_reserved .= " AND expert_id = '{$expert_id}'";
}
$row_reserved = sql_fetch($sql_reserved);
if ($row_reserved['cnt'] >= $max_capacity) {
$reason = 'full';
} else {
$is_bookable = true;
}
}
$availability[$day_num] = [
'available' => $is_bookable,
'reason' => $reason
];
$current = strtotime('+1 day', $current);
}
$response = ['success' => true, 'data' => $availability];
}
}
// 특정일의 예약 가능 시간 조회
if ($action === 'get_expert_visit_time_slots') {
$date = preg_replace('/[^0-9\-]/', '', $_POST['date'] ?? '');
$expert_id = $_POST['expert_id'] ?? '';
if ($date) {
$min_advance_hours = get_order_config('expert_visit_min_advance_hours', 24);
$min_datetime = date('Y-m-d H:i:s', strtotime("+" . $min_advance_hours . " hours"));
$dow = date('N', strtotime($date));
// 1. 해당 날짜의 스케줄 조회 (우선순위 적용)
$schedule = sql_fetch("SELECT * FROM expert_visit_schedules
WHERE (specific_date = '{$date}' OR (specific_date IS NULL AND day_of_week = '{$dow}'))
AND (expert_id = '{$expert_id}' OR expert_id IS NULL)
ORDER BY specific_date DESC, expert_id DESC LIMIT 1");
$slots = [];
if ($schedule && $schedule['is_available']) {
$start_time = strtotime($date . ' ' . $schedule['start_time']);
$end_time = strtotime($date . ' ' . $schedule['end_time']);
$slot_duration = $schedule['time_slot'] * 60;
$max_persons = $schedule['max_persons'];
for ($t = $start_time; $t < $end_time; $t += $slot_duration) {
$current_slot_time = date('H:i', $t);
$slot_datetime = $date . ' ' . $current_slot_time;
// 예약 건수 조회
$sql_reserved = "SELECT COUNT(*) as cnt FROM expert_visit_reservations
WHERE visit_date = '{$date}'
AND visit_time = '{$current_slot_time}:00'
AND status != 'cancelled'";
if ($expert_id) {
$sql_reserved .= " AND expert_id = '{$expert_id}'";
}
$reserved_count = (int)sql_fetch($sql_reserved)['cnt'];
$is_too_soon = ($slot_datetime < $min_datetime);
$is_full = ($reserved_count >= $max_persons);
$slots[] = [
'time' => $current_slot_time,
'available' => !$is_too_soon && !$is_full,
'reason' => $is_too_soon ? 'too_soon' : ($is_full ? 'full' : ''),
'reserved_count' => $reserved_count,
'max_persons' => $max_persons
];
}
}
$response = ['success' => true, 'data' => $slots];
}
}
echo json_encode($response);
exit;
}
if (!defined('_GNUBOARD_')) exit;
include_once(G5_ADMIN_PATH . '/order_manage/_common_con.php');
include_once(G5_ADMIN_PATH . '/order_manage/lib/notification_helper.php');
$current_year = date('Y');
$current_month_num = date('n');
$visit_fee = get_order_config('expert_visit_fee', 50000);
$account_info = get_order_config('expert_visit_account_info', '');
$max_advance_days = get_order_config('expert_visit_max_advance_days', 30);
$ajax_url = G5_ADMIN_URL . '/order_manage/components/expert_visit_popup.php';
$form_action_url = G5_ADMIN_URL . '/order_manage/components/expert_visit_submit.php';
$css_url = G5_ADMIN_URL . '/order_manage/components/expert_visit_popup.css';
$wr_id = isset($_GET['wr_id']) ? (int)$_GET['wr_id'] : 0;
// 💡 [추가] 로그인 회원 정보 가져오기
$customer_name = '';
$customer_phone = '';
$customer_email = '';
if (isset($member) && $member['mb_id']) {
$customer_name = $member['mb_name'];
$customer_phone = $member['mb_hp'];
$customer_email = $member['mb_email'];
}
?>
<link rel="stylesheet" href="<?php echo $css_url; ?>">
<div id="expert-visit-popup-overlay" class="expert-visit-modal-overlay">
<div id="expert-visit-popup" class="expert-visit-modal-content">
<div class="expert-visit-modal-header">
<h2>전문가 방문 예약</h2>
<button type="button" class="expert-visit-modal-close" aria-label="팝업 닫기">
<span>&times;</span>
</button>
</div>
<div class="expert-visit-modal-body">
<div class="loading-overlay" style="display: none;">
<div class="loading-spinner"></div>
</div>
<div class="expert-visit-steps">
<div class="step active" data-step="1">
<span class="step-number">1</span>
<span class="step-text">날짜 선택</span>
</div>
<div class="step" data-step="2">
<span class="step-number">2</span>
<span class="step-text">시간 선택</span>
</div>
<div class="step" data-step="3">
<span class="step-number">3</span>
<span class="step-text">정보 입력</span>
</div>
</div>
<form id="expert-visit-form" method="post" action="<?php echo $form_action_url; ?>">
<div class="expert-visit-step-content" data-step="1">
<div class="step-description">
<h4>📅 방문 날짜를 선택해주세요</h4>
<p>최대 <?php echo $max_advance_days; ?>일 후까지 예약 가능합니다.</p>
<!-- 전문가 선택을 1단계로 이동 -->
<div class="form-group" style="margin-top: 15px;">
<label for="expert-select-step1">👨‍⚕️ 전문가 선택</label>
<select id="expert-select-step1" name="expert_id_display" class="form-control">
<option value="">선택 안 함 (전체 일정 보기)</option>
<?php
/* 전문가 목록 조회 (실제 DB 연동 필요)
$experts = get_experts();
foreach ($experts as $expert):
?>
<option value="<?php echo $expert['id']; ?>"><?php echo htmlspecialchars($expert['name']); ?></option>
<?php endforeach;
*/
?>
</select>
</div>
</div>
<div class="calendar-container">
<div class="calendar-header">
<button type="button" class="calendar-nav prev-month" aria-label="이전 달"><span></span></button>
<h3 class="calendar-title">
<span id="calendar-year"><?php echo $current_year; ?></span>년
<span id="calendar-month"><?php echo $current_month_num; ?></span>월
</h3>
<button type="button" class="calendar-nav next-month" aria-label="다음 달"><span></span></button>
</div>
<div class="calendar-grid">
<div class="calendar-weekdays">
<div class="weekday">일</div><div class="weekday">월</div><div class="weekday">화</div><div class="weekday">수</div><div class="weekday">목</div><div class="weekday">금</div><div class="weekday">토</div>
</div>
<div class="calendar-days" id="calendar-days"></div>
</div>
<div class="calendar-legend">
<div class="legend-item"><span class="legend-color available"></span><span>예약 가능</span></div>
<div class="legend-item"><span class="legend-color holiday"></span><span>휴일</span></div>
<div class="legend-item"><span class="legend-color full"></span><span>예약 마감</span></div>
<div class="legend-item"><span class="legend-color unavailable"></span><span>불가</span></div>
<div class="legend-item"><span class="legend-color selected"></span><span>선택</span></div>
</div>
</div>
</div>
<div class="expert-visit-step-content" data-step="2" style="display: none;">
<div class="step-description">
<h4>🕐 방문 시간을 선택해주세요</h4>
<div class="selected-date-info"><strong>선택된 날짜: <span id="selected-date-display"></span></strong></div>
</div>
<div class="time-slots-container">
<div class="time-slots-grid" id="time-slots-grid"></div>
<div class="time-legend">
<div class="legend-item"><span class="legend-color time-available"></span><span>예약 가능</span></div>
<div class="legend-item"><span class="legend-color time-full"></span><span>예약 마감</span></div>
<div class="legend-item"><span class="legend-color time-too-soon"></span><span>예약 임박</span></div>
<div class="legend-item"><span class="legend-color time-selected"></span><span>선택</span></div>
</div>
</div>
</div>
<div class="expert-visit-step-content" data-step="3" style="display: none;">
<div class="step-description">
<h4>📝 고객 정보를 입력해주세요</h4>
</div>
<div class="expert-visit-summary">
<h5>📋 예약 정보 확인</h5>
<div class="summary-grid">
<div class="summary-item"><span class="label">📅 예약 날짜:</span><span id="summary-date">-</span></div>
<div class="summary-item"><span class="label">🕐 예약 시간:</span><span id="summary-time">-</span></div>
<div class="summary-item"><span class="label">💰 방문 비용:</span><span><?php echo number_format($visit_fee); ?>원</span></div>
</div>
</div>
<div class="customer-info-form">
<div class="form-row">
<div class="form-group">
<label for="customer-name">👤 이름 <span class="required">*</span></label>
<input type="text" id="customer-name" name="customer_name" required placeholder="홍길동" value="<?php echo htmlspecialchars($customer_name); ?>">
</div>
<div class="form-group">
<label for="customer-phone">📱 연락처 <span class="required">*</span></label>
<input type="tel" id="customer-phone" name="customer_phone" required placeholder="010-1234-5678" value="<?php echo htmlspecialchars($customer_phone); ?>">
</div>
</div>
<div class="form-group">
<label for="customer-email">📧 이메일 <span class="required">*</span></label>
<input type="email" id="customer-email" name="customer_email" required placeholder="example@email.com" value="<?php echo htmlspecialchars($customer_email); ?>">
</div>
<div class="form-group">
<label for="expert-visit-type">🏠 방문 유형</label>
<select id="expert-visit-type" name="visit_type">
<option value="onsite">현장 방문</option>
<option value="online">온라인 상담</option>
<option value="phone">전화 상담</option>
</select>
</div>
<!-- 3단계 전문가 선택은 hidden으로 처리하고 1단계 값과 동기화 -->
<input type="hidden" id="expert-visit-resource" name="expert_id">
<div class="form-group">
<label for="customer-request">📝 요청사항</label>
<textarea id="customer-request" name="request_memo" rows="4" placeholder="방문 관련 요청사항이나 문의사항을 입력해주세요."></textarea>
</div>
</div>
<input type="hidden" id="selected-date" name="visit_date">
<input type="hidden" id="selected-time" name="visit_time">
<input type="hidden" name="payment_amount" value="<?php echo $visit_fee; ?>">
<input type="hidden" name="status" value="payment_pending">
<input type="hidden" name="wr_id" value="<?php echo $wr_id; ?>">
</div>
<div class="expert-visit-nav-buttons">
<button type="button" class="btn-prev" style="display: none;">← 이전</button>
<button type="button" class="btn-next">다음 →</button>
<button type="submit" class="btn-submit" style="display: none;">예약 신청</button>
</div>
</form>
</div>
</div>
</div>
<script>
const ExpertVisitPopup = {
elements: {},
state: {
currentStep: 1,
selectedDate: null,
selectedTime: null,
currentYear: new Date().getFullYear(),
currentMonth: new Date().getMonth() + 1,
expertId: '', // 전문가 ID 상태 추가
},
config: {
ajaxUrl: '<?php echo $ajax_url; ?>',
maxAdvanceDays: <?php echo (int)$max_advance_days; ?>,
},
init() {
this.elements.overlay = document.getElementById('expert-visit-popup-overlay');
if (!this.elements.overlay) return;
this.elements.popup = this.elements.overlay.querySelector('.expert-visit-modal-content');
this.elements.loading = this.elements.popup.querySelector('.loading-overlay');
this.elements.form = this.elements.popup.querySelector('#expert-visit-form');
this.elements.calendar = {
year: this.elements.popup.querySelector('#calendar-year'),
month: this.elements.popup.querySelector('#calendar-month'),
days: this.elements.popup.querySelector('#calendar-days'),
prevBtn: this.elements.popup.querySelector('.prev-month'),
nextBtn: this.elements.popup.querySelector('.next-month'),
};
this.elements.timeSlotsGrid = this.elements.popup.querySelector('#time-slots-grid');
this.elements.expertSelect = this.elements.popup.querySelector('#expert-select-step1');
this.elements.nav = {
prevBtn: this.elements.popup.querySelector('.btn-prev'),
nextBtn: this.elements.popup.querySelector('.btn-next'),
submitBtn: this.elements.popup.querySelector('.btn-submit'),
};
this.addEventListeners();
},
addEventListeners() {
const closeBtn = this.elements.popup.querySelector('.expert-visit-modal-close');
if (closeBtn) closeBtn.addEventListener('click', () => this.close());
this.elements.overlay.addEventListener('click', e => {
if (e.target === this.elements.overlay) this.close();
});
document.addEventListener('keydown', e => {
if (e.key === 'Escape' && this.elements.overlay.classList.contains('active')) this.close();
});
if (this.elements.calendar.prevBtn) this.elements.calendar.prevBtn.addEventListener('click', () => this.changeMonth(-1));
if (this.elements.calendar.nextBtn) this.elements.calendar.nextBtn.addEventListener('click', () => this.changeMonth(1));
if (this.elements.nav.prevBtn) this.elements.nav.prevBtn.addEventListener('click', () => this.goToStep(this.state.currentStep - 1));
if (this.elements.nav.nextBtn) this.elements.nav.nextBtn.addEventListener('click', () => this.goToNextStep());
if (this.elements.form) {
this.elements.form.addEventListener('submit', e => {
e.preventDefault();
this.submitForm();
});
}
// 전문가 선택 변경 시 달력 갱신
if (this.elements.expertSelect) {
this.elements.expertSelect.addEventListener('change', (e) => {
this.state.expertId = e.target.value;
this.state.selectedDate = null; // 날짜 선택 초기화
this.renderCalendar();
});
}
},
open() {
this.state.currentYear = new Date().getFullYear();
this.state.currentMonth = new Date().getMonth() + 1;
this.goToStep(1);
this.renderCalendar();
this.elements.overlay.classList.add('active');
document.body.style.overflow = 'hidden';
},
close() {
this.elements.overlay.classList.remove('active');
document.body.style.overflow = '';
this.elements.form.reset();
this.state.selectedDate = null;
this.state.selectedTime = null;
this.state.expertId = '';
},
changeMonth(delta) {
this.state.currentMonth += delta;
if (this.state.currentMonth < 1) {
this.state.currentMonth = 12;
this.state.currentYear--;
} else if (this.state.currentMonth > 12) {
this.state.currentMonth = 1;
this.state.currentYear++;
}
this.renderCalendar();
},
async renderCalendar() {
this.elements.calendar.year.textContent = this.state.currentYear;
this.elements.calendar.month.textContent = this.state.currentMonth;
this.elements.calendar.days.innerHTML = '<div class="loading">달력 정보를 불러오는 중...</div>';
const availability = await this.fetchMonthAvailability();
if (!availability) {
this.elements.calendar.days.innerHTML = '<div class="error">달력 정보를 불러올 수 없습니다.</div>';
return;
}
this.elements.calendar.days.innerHTML = '';
const firstDay = new Date(this.state.currentYear, this.state.currentMonth - 1, 1);
const daysInMonth = new Date(this.state.currentYear, this.state.currentMonth, 0).getDate();
const startDayOfWeek = firstDay.getDay();
for (let i = 0; i < startDayOfWeek; i++) {
this.elements.calendar.days.appendChild(this.createDayElement(0, true));
}
for (let day = 1; day <= daysInMonth; day++) {
this.elements.calendar.days.appendChild(this.createDayElement(day, false, availability[day]));
}
},
createDayElement(day, isOtherMonth, availability = null) {
const dayElement = document.createElement('div');
dayElement.className = 'calendar-day';
if (isOtherMonth) {
dayElement.classList.add('other-month');
return dayElement;
}
dayElement.textContent = day;
const dateStr = `${this.state.currentYear}-${String(this.state.currentMonth).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
const today = new Date();
today.setHours(0, 0, 0, 0);
const currentDate = new Date(dateStr);
currentDate.setHours(0, 0, 0, 0);
if (currentDate.getTime() < today.getTime()) {
dayElement.classList.add('unavailable');
} else if (availability && availability.available) {
dayElement.classList.add('available');
// 이미 선택된 날짜라면 selected 클래스 추가
if (this.state.selectedDate === dateStr) {
dayElement.classList.add('selected');
}
dayElement.addEventListener('click', () => this.selectDate(dateStr, dayElement));
} else {
const reason = availability ? availability.reason : 'unavailable';
dayElement.classList.add(reason);
}
if (currentDate.getTime() === today.getTime()) {
dayElement.classList.add('today');
}
return dayElement;
},
selectDate(dateStr, element) {
const prevSelected = this.elements.calendar.days.querySelector('.selected');
if (prevSelected) prevSelected.classList.remove('selected');
element.classList.add('selected');
this.state.selectedDate = dateStr;
},
async fetchMonthAvailability() {
this.showLoading();
try {
const formData = new FormData();
formData.append('action', 'get_expert_visit_month_availability');
formData.append('year', this.state.currentYear);
formData.append('month', this.state.currentMonth);
formData.append('expert_id', this.state.expertId); // 전문가 ID 전송
const response = await fetch(this.config.ajaxUrl, { method: 'POST', body: formData });
const result = await response.json();
return result.success ? result.data : null;
} catch (error) {
console.error('Error fetching month availability:', error);
return null;
} finally {
this.hideLoading();
}
},
async fetchTimeSlots() {
this.showLoading();
this.elements.timeSlotsGrid.innerHTML = '<div class="loading">시간 정보를 불러오는 중...</div>';
try {
const formData = new FormData();
formData.append('action', 'get_expert_visit_time_slots');
formData.append('date', this.state.selectedDate);
formData.append('expert_id', this.state.expertId); // 전문가 ID 전송
const response = await fetch(this.config.ajaxUrl, { method: 'POST', body: formData });
const result = await response.json();
if (result.success) {
this.renderTimeSlots(result.data);
} else {
this.elements.timeSlotsGrid.innerHTML = `<div class="error">${result.message || '시간 정보를 불러올 수 없습니다.'}</div>`;
}
} catch (error) {
console.error('Error fetching time slots:', error);
this.elements.timeSlotsGrid.innerHTML = '<div class="error">오류가 발생했습니다. 다시 시도해주세요.</div>';
} finally {
this.hideLoading();
}
},
renderTimeSlots(slots) {
this.elements.timeSlotsGrid.innerHTML = '';
if (slots.length === 0) {
this.elements.timeSlotsGrid.innerHTML = '<div class="no-slots">해당 날짜에 운영되는 시간이 없습니다.</div>';
return;
}
let hasAvailableSlots = false;
slots.forEach(slot => {
const slotElement = document.createElement('div');
slotElement.className = 'time-slot';
let slotInfoText = '';
if (slot.available) {
hasAvailableSlots = true;
slotElement.classList.add('available');
slotElement.addEventListener('click', () => this.selectTime(slot.time, slotElement));
slotInfoText = `예약 ${slot.reserved_count} / ${slot.max_persons}`;
} else {
slotElement.classList.add(slot.reason || 'full');
slotInfoText = slot.reason === 'too_soon' ? '예약 임박' : '마감';
}
slotElement.innerHTML = `<span class="time-text">${slot.time}</span> <span class="slot-info">${slotInfoText}</span>`;
this.elements.timeSlotsGrid.appendChild(slotElement);
});
if (!hasAvailableSlots) {
const noSlotsMessage = document.createElement('div');
noSlotsMessage.className = 'no-slots';
noSlotsMessage.textContent = '현재 예약 가능한 시간이 없습니다. 다른 날짜를 선택해주세요.';
this.elements.timeSlotsGrid.prepend(noSlotsMessage);
}
},
selectTime(time, element) {
const prevSelected = this.elements.timeSlotsGrid.querySelector('.selected');
if (prevSelected) prevSelected.classList.remove('selected');
element.classList.add('selected');
this.state.selectedTime = time;
},
goToStep(step) {
this.state.currentStep = step;
this.elements.popup.querySelectorAll('.step').forEach((el, i) => {
el.classList.toggle('active', i + 1 === step);
el.classList.toggle('completed', i + 1 < step);
});
this.elements.popup.querySelectorAll('.expert-visit-step-content').forEach(el => {
el.style.display = parseInt(el.dataset.step) === step ? 'block' : 'none';
});
this.elements.nav.prevBtn.style.display = step > 1 ? 'inline-block' : 'none';
this.elements.nav.nextBtn.style.display = step < 3 ? 'inline-block' : 'none';
this.elements.nav.submitBtn.style.display = step === 3 ? 'inline-block' : 'none';
},
goToNextStep() {
if (this.state.currentStep === 1 && !this.state.selectedDate) {
alert('날짜를 선택해주세요.'); return;
}
if (this.state.currentStep === 2 && !this.state.selectedTime) {
alert('시간을 선택해주세요.'); return;
}
if (this.state.currentStep < 3) {
this.goToStep(this.state.currentStep + 1);
if (this.state.currentStep === 2) this.fetchTimeSlots();
if (this.state.currentStep === 3) this.updateSummary();
}
},
updateSummary() {
const date = new Date(this.state.selectedDate);
const options = { year: 'numeric', month: 'long', day: 'numeric', weekday: 'long' };
this.elements.popup.querySelector('#summary-date').textContent = date.toLocaleDateString('ko-KR', options);
this.elements.popup.querySelector('#summary-time').textContent = this.state.selectedTime;
this.elements.popup.querySelector('#selected-date-display').textContent = date.toLocaleDateString('ko-KR', options);
this.elements.form.querySelector('#selected-date').value = this.state.selectedDate;
this.elements.form.querySelector('#selected-time').value = this.state.selectedTime;
// 전문가 ID 동기화
this.elements.form.querySelector('#expert-visit-resource').value = this.state.expertId;
},
async submitForm() {
if (!this.elements.form.checkValidity()) {
alert('필수 입력 항목을 모두 채워주세요.');
this.elements.form.reportValidity();
return;
}
this.showLoading();
this.elements.nav.submitBtn.disabled = true;
this.elements.nav.submitBtn.textContent = '처리 중...';
try {
const formData = new FormData(this.elements.form);
const response = await fetch('<?php echo $form_action_url; ?>', { method: 'POST', body: formData });
const result = await response.json();
if (result.success) {
alert('예약 신청이 완료되었습니다.');
this.close();
// location.reload();
} else {
alert(result.message || '예약 처리 중 오류가 발생했습니다.');
}
} catch (error) {
console.error('Form submission error:', error);
alert('네트워크 오류가 발생했습니다. 다시 시도해주세요.');
} finally {
this.hideLoading();
this.elements.nav.submitBtn.disabled = false;
this.elements.nav.submitBtn.textContent = '예약 신청';
}
},
showLoading() { if (this.elements.loading) this.elements.loading.style.display = 'flex'; },
hideLoading() { if (this.elements.loading) this.elements.loading.style.display = 'none'; },
};
document.addEventListener('DOMContentLoaded', () => ExpertVisitPopup.init());
function openExpertVisitPopup(wr_id = 0) {
if (wr_id) {
const form = ExpertVisitPopup.elements.form;
if(form) {
let wrIdField = form.querySelector('input[name="wr_id"]');
if (!wrIdField) {
wrIdField = document.createElement('input');
wrIdField.type = 'hidden';
wrIdField.name = 'wr_id';
form.appendChild(wrIdField);
}
wrIdField.value = wr_id;
}
}
ExpertVisitPopup.open();
}
</script>