Files
2026-06-11 18:47:38 +09:00

578 lines
17 KiB
PHP

<?php
/**
* 상담 예약 관리 시스템 대시보드
*/
$sub_menu = '850100';
include_once('./_common.php');
// 관리자 권한 확인
if (!$is_admin) {
alert('관리자만 접근할 수 있습니다.');
}
// 설치 확인
if (!is_consultant_installed()) {
alert('상담 예약 시스템이 설치되지 않았습니다.', 'install.php');
}
$g5['title'] = '상담 예약 대시보드';
// 오늘 날짜 기준 통계
$today = date('Y-m-d');
$this_week_start = date('Y-m-d', strtotime('monday this week'));
$this_month_start = date('Y-m-01');
// 오늘 예약 현황
$sql = "SELECT
COUNT(*) as total,
COUNT(CASE WHEN status = 'payment_pending' THEN 1 END) as pending,
COUNT(CASE WHEN status = 'reserved' THEN 1 END) as confirmed,
COUNT(CASE WHEN status = 'completed' THEN 1 END) as completed
FROM consultant_reservations
WHERE reservation_date = '{$today}' AND is_deleted = 0";
$today_stats = sql_fetch($sql);
// 이번 주 예약 현황
$sql = "SELECT
COUNT(*) as total,
SUM(CASE WHEN status = 'completed' THEN payment_amount ELSE 0 END) as revenue
FROM consultant_reservations
WHERE reservation_date >= '{$this_week_start}'
AND reservation_date <= '{$today}'
AND is_deleted = 0";
$week_stats = sql_fetch($sql);
// 이번 달 예약 현황
$sql = "SELECT
COUNT(*) as total,
SUM(CASE WHEN status = 'completed' THEN payment_amount ELSE 0 END) as revenue
FROM consultant_reservations
WHERE reservation_date >= '{$this_month_start}'
AND is_deleted = 0";
$month_stats = sql_fetch($sql);
// 최근 예약 목록 (5개)
$sql = "SELECT * FROM consultant_reservations
WHERE is_deleted = 0
ORDER BY created_at DESC
LIMIT 5";
$recent_reservations = [];
$result = sql_query($sql);
while ($row = sql_fetch_array($result)) {
$recent_reservations[] = $row;
}
// 오늘 예약 목록
$sql = "SELECT * FROM consultant_reservations
WHERE reservation_date = '{$today}'
AND is_deleted = 0
ORDER BY reservation_time";
$today_reservations = [];
$result = sql_query($sql);
while ($row = sql_fetch_array($result)) {
$today_reservations[] = $row;
}
// 입금 대기 예약 수
$sql = "SELECT COUNT(*) as count FROM consultant_reservations
WHERE status = 'payment_pending' AND is_deleted = 0";
$pending_count = sql_fetch($sql)['count'];
// 시간대별 예약 현황 (이번 주)
$sql = "SELECT
reservation_time,
COUNT(*) as count
FROM consultant_reservations
WHERE reservation_date >= '{$this_week_start}'
AND reservation_date <= '{$today}'
AND is_deleted = 0
GROUP BY reservation_time
ORDER BY reservation_time";
$time_stats = [];
$result = sql_query($sql);
while ($row = sql_fetch_array($result)) {
$time_stats[] = $row;
}
include_once(G5_ADMIN_PATH . '/admin.head.php');
?>
<style>
.dashboard-container {
max-width: 1400px;
margin: 0 auto;
padding: 20px;
}
.dashboard-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.stat-card {
background: white;
border: 1px solid #ddd;
border-radius: 8px;
padding: 20px;
text-align: center;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.stat-number {
font-size: 32px;
font-weight: bold;
margin-bottom: 8px;
}
.stat-label {
color: #666;
font-size: 14px;
margin-bottom: 4px;
}
.stat-sublabel {
color: #999;
font-size: 12px;
}
.stat-card.today .stat-number {
color: #007bff;
}
.stat-card.week .stat-number {
color: #28a745;
}
.stat-card.month .stat-number {
color: #17a2b8;
}
.stat-card.pending .stat-number {
color: #ffc107;
}
.content-grid {
display: grid;
grid-template-columns: 2fr 1fr;
gap: 20px;
margin-bottom: 30px;
}
.content-card {
background: white;
border: 1px solid #ddd;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.card-header {
background: #fff;
padding: 15px 20px;
border-bottom: 1px solid #ddd;
font-weight: bold;
display: flex;
justify-content: space-between;
align-items: center;
}
.card-content {
padding: 20px;
}
.reservation-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 0;
border-bottom: 1px solid #f0f0f0;
}
.reservation-item:last-child {
border-bottom: none;
}
.reservation-info {
flex: 1;
}
.reservation-name {
font-weight: bold;
margin-bottom: 4px;
}
.reservation-details {
font-size: 12px;
color: #666;
}
.reservation-status {
padding: 4px 8px;
border-radius: 12px;
font-size: 11px;
font-weight: bold;
text-align: center;
min-width: 60px;
}
.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;
}
.quick-actions {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.btn {
padding: 8px 16px;
border: none;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
display: inline-block;
font-size: 14px;
font-weight: 600;
transition: all 0.2s;
}
.btn-primary {
background: #007bff;
color: white;
}
.btn-success {
background: #28a745;
color: white;
}
.btn-warning {
background: #ffc107;
color: #212529;
}
.btn-info {
background: #17a2b8;
color: white;
}
.btn-sm {
padding: 6px 12px;
font-size: 12px;
}
.empty-state {
text-align: center;
padding: 40px 20px;
color: #666;
}
.chart-container {
height: 200px;
display: flex;
align-items: end;
justify-content: space-around;
padding: 20px 0;
border-top: 1px solid #eee;
margin-top: 20px;
}
.chart-bar {
background: #007bff;
width: 30px;
border-radius: 2px 2px 0 0;
position: relative;
transition: all 0.3s;
}
.chart-bar:hover {
background: #0056b3;
}
.chart-label {
position: absolute;
bottom: -25px;
left: 50%;
transform: translateX(-50%);
font-size: 11px;
color: #666;
white-space: nowrap;
}
.chart-value {
position: absolute;
top: -20px;
left: 50%;
transform: translateX(-50%);
font-size: 10px;
color: #333;
font-weight: bold;
}
.alert {
padding: 15px;
margin-bottom: 20px;
border: 1px solid transparent;
border-radius: 4px;
}
.alert-warning {
color: #856404;
background-color: #fff3cd;
border-color: #ffeaa7;
}
.alert-info {
color: #0c5460;
background-color: #d1ecf1;
border-color: #bee5eb;
}
@media (max-width: 768px) {
.content-grid {
grid-template-columns: 1fr;
}
.dashboard-header {
flex-direction: column;
gap: 15px;
align-items: stretch;
}
.quick-actions {
justify-content: center;
}
}
</style>
<div class="dashboard-container">
<div class="dashboard-header">
<div>
<h2><?php echo $g5['title']; ?></h2>
<p>상담 예약 현황을 한눈에 확인하세요</p>
</div>
<div class="quick-actions">
<a href="reservations.php" class="btn btn-primary">예약 관리</a>
<a href="schedule_generate.php" class="btn btn-success">일정 설정</a>
<a href="statistics.php" class="btn btn-info">통계 보기</a>
</div>
</div>
<!-- 통계 카드 -->
<div class="stats-grid">
<div class="stat-card today">
<div class="stat-number"><?php echo number_format($today_stats['total']); ?></div>
<div class="stat-label">오늘 예약</div>
<div class="stat-sublabel">
확정 <?php echo $today_stats['confirmed']; ?>건 |
대기 <?php echo $today_stats['pending']; ?>건
</div>
</div>
<div class="stat-card week">
<div class="stat-number"><?php echo number_format($week_stats['total']); ?></div>
<div class="stat-label">이번 주 예약</div>
<div class="stat-sublabel">
매출 <?php echo number_format($week_stats['revenue']); ?>원
</div>
</div>
<div class="stat-card month">
<div class="stat-number"><?php echo number_format($month_stats['total']); ?></div>
<div class="stat-label">이번 달 예약</div>
<div class="stat-sublabel">
매출 <?php echo number_format($month_stats['revenue']); ?>원
</div>
</div>
<div class="stat-card pending">
<div class="stat-number"><?php echo number_format($pending_count); ?></div>
<div class="stat-label">입금 대기</div>
<div class="stat-sublabel">
<?php if ($pending_count > 0): ?>
<a href="reservations.php?status=payment_pending" style="color: #856404;">확인 필요</a>
<?php else: ?>
모두 처리됨
<?php endif; ?>
</div>
</div>
</div>
<?php if ($pending_count > 0): ?>
<div class="alert alert-warning">
<strong>알림:</strong> 입금 대기 중인 예약이 <?php echo $pending_count; ?>건 있습니다.
<a href="reservations.php?status=payment_pending">지금 확인하기</a>
</div>
<?php endif; ?>
<!-- 메인 콘텐츠 -->
<div class="content-grid">
<!-- 오늘 예약 현황 -->
<div class="content-card">
<div class="card-header">
<span>오늘 예약 현황 (<?php echo date('Y-m-d'); ?>)</span>
<a href="reservations.php?date=<?php echo $today; ?>" class="btn btn-sm btn-primary">전체 보기</a>
</div>
<div class="card-content">
<?php if (empty($today_reservations)): ?>
<div class="empty-state">
<p>오늘 예약된 상담이 없습니다.</p>
</div>
<?php else: ?>
<?php foreach ($today_reservations as $reservation): ?>
<div class="reservation-item">
<div class="reservation-info">
<div class="reservation-name">
<?php echo htmlspecialchars($reservation['customer_name']); ?>
</div>
<div class="reservation-details">
<?php echo $reservation['reservation_time']; ?> |
<?php echo htmlspecialchars($reservation['customer_phone']); ?> |
<?php echo number_format($reservation['payment_amount']); ?>원
</div>
</div>
<div class="reservation-status status-<?php echo $reservation['status']; ?>">
<?php
$status_labels = [
'payment_pending' => '입금대기',
'reserved' => '예약확정',
'completed' => '상담완료',
'cancelled' => '예약취소'
];
echo $status_labels[$reservation['status']] ?? $reservation['status'];
?>
</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
<!-- 최근 예약 -->
<div class="content-card">
<div class="card-header">
<span>최근 예약</span>
<a href="reservations.php" class="btn btn-sm btn-primary">전체 보기</a>
</div>
<div class="card-content">
<?php if (empty($recent_reservations)): ?>
<div class="empty-state">
<p>최근 예약이 없습니다.</p>
</div>
<?php else: ?>
<?php foreach ($recent_reservations as $reservation): ?>
<div class="reservation-item">
<div class="reservation-info">
<div class="reservation-name">
<?php echo htmlspecialchars($reservation['customer_name']); ?>
</div>
<div class="reservation-details">
<?php echo $reservation['reservation_date']; ?>
<?php echo $reservation['reservation_time']; ?><br>
<?php echo date('m-d H:i', strtotime($reservation['created_at'])); ?> 신청
</div>
</div>
<div class="reservation-status status-<?php echo $reservation['status']; ?>">
<?php
$status_labels = [
'payment_pending' => '입금대기',
'reserved' => '예약확정',
'completed' => '상담완료',
'cancelled' => '예약취소'
];
echo $status_labels[$reservation['status']] ?? $reservation['status'];
?>
</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
</div>
<!-- 시간대별 예약 현황 -->
<?php if (!empty($time_stats)): ?>
<div class="content-card">
<div class="card-header">
<span>시간대별 예약 현황 (이번 주)</span>
</div>
<div class="card-content">
<div class="chart-container">
<?php
$max_count = max(array_column($time_stats, 'count'));
foreach ($time_stats as $stat):
$height = $max_count > 0 ? ($stat['count'] / $max_count) * 150 : 0;
?>
<div class="chart-bar" style="height: <?php echo $height; ?>px;">
<div class="chart-value"><?php echo $stat['count']; ?></div>
<div class="chart-label"><?php echo substr($stat['reservation_time'], 0, 5); ?></div>
</div>
<?php endforeach; ?>
</div>
</div>
</div>
<?php endif; ?>
<!-- 시스템 정보 -->
<div class="alert alert-info">
<strong>시스템 정보:</strong>
상담 예약 시스템 v<?php echo G5_CONSULTANT_VERSION; ?> |
마지막 업데이트: <?php echo date('Y-m-d H:i'); ?> |
<a href="settings.php">시스템 설정</a>
</div>
</div>
<script>
// 실시간 업데이트 (5분마다)
setInterval(function () {
location.reload();
}, 300000);
// 차트 애니메이션
document.addEventListener('DOMContentLoaded', function () {
const bars = document.querySelectorAll('.chart-bar');
bars.forEach((bar, index) => {
setTimeout(() => {
bar.style.opacity = '0';
bar.style.transform = 'scaleY(0)';
bar.style.transformOrigin = 'bottom';
setTimeout(() => {
bar.style.transition = 'all 0.5s ease';
bar.style.opacity = '1';
bar.style.transform = 'scaleY(1)';
}, 100);
}, index * 100);
});
});
</script>
<?php
include_once(G5_ADMIN_PATH . '/admin.tail.php');
?>