Files
dnssash/adm/consultant_manage/reservations.php
T
2026-06-11 18:47:38 +09:00

500 lines
17 KiB
PHP

<?php
/**
* 예약 관리
*/
$sub_menu = '850200';
include_once('./_common.php');
// 관리자 권한 확인
if (!$is_admin) {
alert('관리자만 접근할 수 있습니다.');
}
// 설치 확인
if (!is_consultant_installed()) {
alert('상담 예약 시스템이 설치되지 않았습니다.', 'install.php');
}
$g5['title'] = '예약 관리';
// 필터 파라미터
$status = $_GET['status'] ?? '';
$date = $_GET['date'] ?? '';
$search = $_GET['search'] ?? '';
$page = (int)($_GET['page'] ?? 1);
$per_page = 20;
// 상태 변경 처리
if ($_POST['action'] == 'update_status') {
$reservation_id = (int)$_POST['reservation_id'];
$new_status = $_POST['new_status'];
$memo = $_POST['memo'] ?? '';
$send_sms = isset($_POST['send_sms']) && $_POST['send_sms'] == '1';
$send_email = isset($_POST['send_email']) && $_POST['send_email'] == '1';
if ($reservation_id && $new_status) {
// 기존 상태 조회
$old_res = sql_fetch("SELECT * FROM consultant_reservations WHERE id = {$reservation_id}");
$sql = "UPDATE consultant_reservations
SET status = '" . sql_real_escape_string($new_status) . "',
admin_memo = '" . sql_real_escape_string($memo) . "',
updated_at = NOW()
WHERE id = {$reservation_id}";
if (sql_query($sql)) {
// 💡 [추가] 알림 발송 로직
if ($send_sms || $send_email) {
// 템플릿 키 결정
$template_key = '';
if ($new_status == 'reserved') {
$template_key = 'consultant_confirmed_customer';
} elseif ($new_status == 'cancelled') {
$template_key = 'consultant_cancelled_customer';
}
if ($template_key) {
// 알림 데이터 준비
$noti_data = [
'customer_name' => $old_res['customer_name'],
'customer_phone' => $old_res['customer_phone'],
'customer_email' => $old_res['customer_email'],
'reservation_date' => $old_res['reservation_date'],
'reservation_time' => substr($old_res['reservation_time'], 0, 5),
'payment_amount' => number_format($old_res['payment_amount']),
'cancel_reason' => $memo // 취소 사유로 메모 사용
];
if ($send_sms) {
consultant_send_notification('sms', $template_key, $noti_data);
}
if ($send_email) {
consultant_send_notification('email', $template_key, $noti_data);
}
}
}
alert('상태가 변경되었습니다.', $_SERVER['PHP_SELF'] . '?' . $_SERVER['QUERY_STRING']);
} else {
alert('상태 변경에 실패했습니다.');
}
}
}
// 검색 조건 구성
$where_conditions = ["is_deleted = 0"];
if ($status) {
$where_conditions[] = "status = '" . sql_real_escape_string($status) . "'";
}
if ($date) {
$where_conditions[] = "reservation_date = '" . sql_real_escape_string($date) . "'";
}
if ($search) {
$search_escaped = sql_real_escape_string($search);
$where_conditions[] = "(customer_name LIKE '%{$search_escaped}%' OR customer_phone LIKE '%{$search_escaped}%')";
}
$where_clause = implode(' AND ', $where_conditions);
// 전체 개수 조회
$count_sql = "SELECT COUNT(*) as total FROM consultant_reservations WHERE {$where_clause}";
$count_result = sql_fetch($count_sql);
$total = $count_result['total'];
// 페이징 계산
$offset = ($page - 1) * $per_page;
$total_pages = ceil($total / $per_page);
// 예약 목록 조회
$sql = "SELECT * FROM consultant_reservations
WHERE {$where_clause}
ORDER BY created_at DESC
LIMIT {$offset}, {$per_page}";
$reservations = [];
$result = sql_query($sql);
while ($row = sql_fetch_array($result)) {
$reservations[] = $row;
}
// 상태 라벨
$status_labels = [
'payment_pending' => '입금대기',
'reserved' => '예약확정',
'completed' => '상담완료',
'cancelled' => '예약취소'
];
include_once(G5_ADMIN_PATH . '/admin.head.php');
?>
<style>
.reservations-container {
max-width: 1400px;
margin: 0 auto;
padding: 20px;
}
.filter-form {
background: white;
border: 1px solid #ddd;
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
display: flex;
align-items: center;
gap: 15px;
flex-wrap: wrap;
}
.reservations-table {
background: white;
border: 1px solid #ddd;
border-radius: 8px;
overflow: hidden;
}
.table-header {
background: #fff;
padding: 15px 20px;
font-weight: bold;
border-bottom: 1px solid #ddd;
display: flex;
justify-content: space-between;
align-items: center;
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #eee;
}
th {
background: #fff;
font-weight: bold;
}
.status-badge {
padding: 4px 8px;
border-radius: 12px;
font-size: 11px;
font-weight: bold;
text-align: center;
}
.status-payment_pending {
background: #fff3cd;
color: #856404;
}
.status-reserved {
background: #d4edda;
color: #155724;
}
.status-completed {
background: #cce5ff;
color: #004085;
}
.status-cancelled {
background: #f8d7da;
color: #721c24;
}
.btn {
padding: 6px 12px;
border: none;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
display: inline-block;
font-size: 12px;
font-weight: 600;
margin: 2px;
}
.btn-primary { background: #007bff; color: white; }
.btn-success { background: #28a745; color: white; }
.btn-warning { background: #ffc107; color: #212529; }
.btn-danger { background: #dc3545; color: white; }
.btn-secondary { background: #6c757d; color: white; }
.btn-info { background: #17a2b8; color: white; } /* 견적작성 버튼용 */
.form-control {
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
.pagination {
display: flex;
justify-content: center;
align-items: center;
gap: 10px;
margin-top: 20px;
}
.pagination a,
.pagination span {
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
text-decoration: none;
color: #333;
}
.pagination .current {
background: #007bff;
color: white;
border-color: #007bff;
}
.modal {
display: none;
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.5);
}
.modal-content {
background-color: white;
margin: 15% auto;
padding: 20px;
border-radius: 8px;
width: 400px;
max-width: 90%;
}
.close {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
cursor: pointer;
}
.close:hover {
color: black;
}
.no-data {
text-align: center;
padding: 40px;
color: #666;
font-style: italic;
}
@media (max-width: 768px) {
.filter-form {
flex-direction: column;
align-items: stretch;
}
table {
font-size: 12px;
}
th, td {
padding: 8px 4px;
}
}
</style>
<div class="reservations-container">
<h2><?php echo $g5['title']; ?></h2>
<!-- 필터 폼 -->
<form method="get" class="filter-form">
<select name="status" class="form-control">
<option value="">전체 상태</option>
<?php foreach ($status_labels as $key => $label): ?>
<option value="<?php echo $key; ?>" <?php echo $status == $key ? 'selected' : ''; ?>>
<?php echo $label; ?>
</option>
<?php endforeach; ?>
</select>
<input type="date" name="date" value="<?php echo $date; ?>" class="form-control" placeholder="예약일">
<input type="text" name="search" value="<?php echo htmlspecialchars($search); ?>"
class="form-control" placeholder="고객명 또는 연락처 검색">
<button type="submit" class="btn btn-primary">검색</button>
<a href="<?php echo $_SERVER['PHP_SELF']; ?>" class="btn btn-secondary">초기화</a>
<a href="dashboard.php" class="btn btn-secondary">대시보드로</a>
</form>
<!-- 예약 목록 -->
<div class="reservations-table">
<div class="table-header">
<span>예약 목록 (총 <?php echo number_format($total); ?>건)</span>
<span>페이지 <?php echo $page; ?> / <?php echo $total_pages; ?></span>
</div>
<?php if (!empty($reservations)): ?>
<table>
<thead>
<tr>
<th>예약번호</th>
<th>고객정보</th>
<th>예약일시</th>
<th>상담유형</th>
<th>상담비</th>
<th>상태</th>
<th>신청일</th>
<th>관리</th>
</tr>
</thead>
<tbody>
<?php foreach ($reservations as $reservation): ?>
<tr>
<td>#<?php echo $reservation['id']; ?></td>
<td>
<strong><?php echo htmlspecialchars($reservation['customer_name']); ?></strong><br>
<small><?php echo htmlspecialchars($reservation['customer_phone']); ?></small><br>
<small><?php echo htmlspecialchars($reservation['customer_email']); ?></small>
</td>
<td>
<?php echo $reservation['reservation_date']; ?><br>
<small><?php echo substr($reservation['reservation_time'], 0, 5); ?></small>
</td>
<td>
<?php
$types = ['onsite' => '현장', 'online' => '온라인', 'phone' => '전화'];
echo $types[$reservation['consultation_type']] ?? $reservation['consultation_type'];
?>
</td>
<td><?php echo number_format($reservation['payment_amount']); ?>원</td>
<td>
<span class="status-badge status-<?php echo $reservation['status']; ?>">
<?php echo $status_labels[$reservation['status']] ?? $reservation['status']; ?>
</span>
</td>
<td><?php echo date('m-d H:i', strtotime($reservation['created_at'])); ?></td>
<td>
<button onclick="openStatusModal(<?php echo $reservation['id']; ?>, '<?php echo $reservation['status']; ?>')" class="btn btn-warning btn-sm">상태변경</button>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php else: ?>
<div class="no-data">
검색 조건에 맞는 예약이 없습니다.
</div>
<?php endif; ?>
</div>
<!-- 페이징 -->
<?php if ($total_pages > 1): ?>
<div class="pagination">
<?php if ($page > 1): ?>
<a href="?<?php echo http_build_query(array_merge($_GET, ['page' => $page - 1])); ?>">이전</a>
<?php endif; ?>
<?php
$start_page = max(1, $page - 5);
$end_page = min($total_pages, $page + 5);
for ($i = $start_page; $i <= $end_page; $i++):
?>
<?php if ($i == $page): ?>
<span class="current"><?php echo $i; ?></span>
<?php else: ?>
<a href="?<?php echo http_build_query(array_merge($_GET, ['page' => $i])); ?>"><?php echo $i; ?></a>
<?php endif; ?>
<?php endfor; ?>
<?php if ($page < $total_pages): ?>
<a href="?<?php echo http_build_query(array_merge($_GET, ['page' => $page + 1])); ?>">다음</a>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
<!-- 상태 변경 모달 -->
<div id="statusModal" class="modal">
<div class="modal-content">
<span class="close">&times;</span>
<h3>예약 상태 변경</h3>
<form method="post">
<input type="hidden" name="action" value="update_status">
<input type="hidden" name="reservation_id" id="modal_reservation_id">
<div style="margin: 15px 0;">
<label for="modal_new_status">변경할 상태</label>
<select name="new_status" id="modal_new_status" class="form-control" required>
<?php foreach ($status_labels as $key => $label): ?>
<option value="<?php echo $key; ?>"><?php echo $label; ?></option>
<?php endforeach; ?>
</select>
</div>
<div style="margin: 15px 0;">
<label for="modal_memo">관리자 메모 (선택)</label>
<textarea name="memo" rows="3" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;"></textarea>
</div>
<!-- 💡 [추가] 알림 발송 옵션 -->
<div style="margin: 15px 0; background: #f8f9fa; padding: 10px; border-radius: 4px;">
<div style="font-weight: bold; margin-bottom: 5px;">알림 발송</div>
<label style="margin-right: 15px;">
<input type="checkbox" name="send_sms" value="1" checked> 문자(SMS) 발송
</label>
<label>
<input type="checkbox" name="send_email" value="1" checked> 이메일 발송
</label>
<div style="font-size: 12px; color: #666; margin-top: 5px;">
* 예약확정/취소 상태 변경 시에만 발송됩니다.
</div>
</div>
<div style="text-align: center; margin-top: 20px;">
<button type="submit" class="btn btn-primary">확인</button>
<button type="button" onclick="closeModal()" class="btn btn-secondary">취소</button>
</div>
</form>
</div>
</div>
<script>
function openStatusModal(reservationId, currentStatus) {
document.getElementById('modal_reservation_id').value = reservationId;
document.getElementById('modal_new_status').value = currentStatus;
document.getElementById('statusModal').style.display = 'block';
}
function closeModal() {
document.getElementById('statusModal').style.display = 'none';
}
// 모달 외부 클릭시 닫기
window.onclick = function(event) {
const modal = document.getElementById('statusModal');
if (event.target == modal) {
modal.style.display = 'none';
}
}
// 닫기 버튼
document.querySelector('.close').onclick = function() {
closeModal();
}
</script>
<?php
include_once(G5_ADMIN_PATH . '/admin.tail.php');
?>