HEX
Server: LiteSpeed
System:
User: ()
PHP: 7.4.33
Disabled: sendmail,mail,exec,shell_exec,dl,system,passthru,pclose,proc_open,proc_nice,proc_terminate,proc_get_status,proc_close,leak,apache_child_terminate,posix_kill,posix_mkfifo,posix_setpgid,posix_setsid,posix_setuid,escapeshellcmd,escapeshellarg shell-exec,fpassthru,crack_check,crack_closedict,crack_getlastmessage,crack_opendict,psockopen,php_uname,symlink,ini_restore,posix_getpwuid,posix_getegid,posix_getcwd,posix_geteuid,posix_getgroups,posix_uname,posix_setuid,eval,show_source,passthru,popen,allow_url_fopen,get_current_user,getmyuid,getmygid,entre2v2,index_changer_wp,index_changer_joomla,exec_mode_1,exec_mode_2,exec_mode_3,wsoFooter,wsoEx,wsoViewSize,wsoPerms,wsoPermsColor,ukuran,tulis,ambil,tukar,entre2v2,rapih,magicboom,goaction,scookie,showstat,index_changer
Upload Files
File: /home/muratemr/theotto.tr/admin/dashboard.php
<?php
/**
 * admin/dashboard.php
 */

declare(strict_types=1);

$page_title  = 'Dashboard';
$active_menu = 'dashboard';

require_once __DIR__ . '/../includes/db.php';
require_once __DIR__ . '/../includes/auth.php';
require_once __DIR__ . '/../includes/functions.php';
require_once __DIR__ . '/../includes/csrf.php';

require_permission('dashboard');

require_once __DIR__ . '/../includes/admin_layout.php';

// ---- İstatistikler ----
function get_dashboard_stats(): array
{
    $today = date('Y-m-d');
    $month = date('Y-m');

    return [
        'res_pending'  => (int)db_value("SELECT COUNT(*) FROM reservations WHERE status='bekliyor'"),
        'res_confirmed'=> (int)db_value("SELECT COUNT(*) FROM reservations WHERE status='onaylandi'"),
        'res_archive'  => (int)db_value("SELECT COUNT(*) FROM reservations WHERE status='arsiv'"),
        'res_total'    => (int)db_value("SELECT COUNT(*) FROM reservations"),
        'customers'    => (int)db_value("SELECT COUNT(*) FROM customers"),
        'suppliers'    => (int)db_value("SELECT COUNT(*) FROM suppliers WHERE is_active=1"),
        'orders_today' => (int)db_value("SELECT COUNT(*) FROM daily_orders WHERE order_date=?", [$today]),
        'orders_month' => (int)db_value("SELECT COUNT(*) FROM daily_orders WHERE DATE_FORMAT(order_date,'%Y-%m')=?", [$month]),
        'orders_today_amt' => (float)db_value("
            SELECT COALESCE(SUM(doi.quantity * COALESCE(doi.approx_price,0)),0)
            FROM daily_orders do2
            JOIN daily_order_items doi ON doi.order_id=do2.id
            WHERE do2.order_date=?", [$today]),
        'orders_month_amt' => (float)db_value("
            SELECT COALESCE(SUM(doi.quantity * COALESCE(doi.approx_price,0)),0)
            FROM daily_orders do2
            JOIN daily_order_items doi ON doi.order_id=do2.id
            WHERE DATE_FORMAT(do2.order_date,'%Y-%m')=?", [$month]),
        'staff_working' => (int)db_value("SELECT COUNT(*) FROM staff_shifts WHERE date=? AND is_work_day=1", [$today]),
        'staff_leave'   => (int)db_value("SELECT COUNT(*) FROM staff_shifts WHERE date=? AND is_work_day=0", [$today]),
        'late_today'    => (int)db_value("SELECT COUNT(*) FROM staff_attendance WHERE date=? AND status='late'", [$today]),
        'late_month'    => (int)db_value("SELECT COUNT(*) FROM staff_attendance WHERE DATE_FORMAT(date,'%Y-%m')=? AND status='late'", [$month]),
    ];
}

$stats = get_dashboard_stats();

// Son bekleyen rezervasyonlar
$pending_res = db_rows("
    SELECT id, full_name, phone, guest_count, date, time, created_at
    FROM reservations
    WHERE status='bekliyor'
    ORDER BY created_at DESC
    LIMIT 8
");

// Son 7 günlük rezervasyon grafiği verisi
$last7 = db_rows("
    SELECT DATE_FORMAT(date,'%d.%m') AS d, COUNT(*) AS cnt
    FROM reservations
    WHERE date BETWEEN DATE_SUB(CURDATE(),INTERVAL 6 DAY) AND CURDATE()
    GROUP BY date
    ORDER BY date ASC
");
?>

<!-- Refresh seçimi -->
<div class="d-flex align-items-center justify-content-between mb-4">
    <h5 class="mb-0 text-gold"><i class="bi bi-speedometer2 me-2"></i>Genel Bakış</h5>
    <div class="d-flex align-items-center gap-2">
        <select id="refreshSelect" class="form-select form-select-sm" style="width:auto;background:#1a1a1a;color:var(--text-primary);border-color:var(--dark-border)">
            <option value="0">Otomatik yenileme kapalı</option>
            <option value="15">15 saniye</option>
            <option value="30" selected>30 saniye</option>
            <option value="60">1 dakika</option>
            <option value="120">2 dakika</option>
        </select>
        <span class="refresh-badge" id="refreshBadge" onclick="loadStats()" title="Şimdi yenile">
            <i class="bi bi-arrow-clockwise me-1"></i><span id="refreshCountdown">—</span>
        </span>
    </div>
</div>

<!-- Ana Layout: Sol=Stat Kartlar, Sağ=Hızlı İşlemler+Grafik -->
<div class="row g-3 mb-3">

    <!-- SOL: 8 stat kartı 2 sütun grid -->
    <div class="col-12 col-lg-8">
        <div class="row g-3">

            <div class="col-6">
                <div class="stat-card">
                    <div class="stat-icon" style="background:rgba(255,193,7,0.15)">
                        <i class="bi bi-hourglass-split" style="color:#ffc107"></i>
                    </div>
                    <div>
                        <div class="stat-val" id="s-res-pending"><?= $stats['res_pending'] ?></div>
                        <div class="stat-lbl">Bekleyen Rezervasyon</div>
                    </div>
                </div>
            </div>

            <div class="col-6">
                <div class="stat-card">
                    <div class="stat-icon" style="background:rgba(40,167,69,0.15)">
                        <i class="bi bi-calendar-check" style="color:#28a745"></i>
                    </div>
                    <div>
                        <div class="stat-val" id="s-res-confirmed"><?= $stats['res_confirmed'] ?></div>
                        <div class="stat-lbl">Onaylı Rezervasyon</div>
                    </div>
                </div>
            </div>

            <div class="col-6">
                <div class="stat-card">
                    <div class="stat-icon" style="background:rgba(111,66,193,0.15)">
                        <i class="bi bi-truck" style="color:#6f42c1"></i>
                    </div>
                    <div>
                        <div class="stat-val" id="s-suppliers"><?= $stats['suppliers'] ?></div>
                        <div class="stat-lbl">Aktif Tedarikçi</div>
                    </div>
                </div>
            </div>

            <div class="col-6">
                <div class="stat-card">
                    <div class="stat-icon" style="background:rgba(253,126,20,0.15)">
                        <i class="bi bi-cart3" style="color:#fd7e14"></i>
                    </div>
                    <div>
                        <div class="stat-val" id="s-orders-today"><?= $stats['orders_today'] ?></div>
                        <div class="stat-lbl">Bugün Sipariş</div>
                    </div>
                </div>
            </div>

            <div class="col-6">
                <div class="stat-card">
                    <div class="stat-icon" style="background:rgba(23,162,184,0.15)">
                        <i class="bi bi-archive" style="color:#17a2b8"></i>
                    </div>
                    <div>
                        <div class="stat-val" id="s-res-archive"><?= $stats['res_archive'] ?></div>
                        <div class="stat-lbl">Arşiv</div>
                    </div>
                </div>
            </div>

            <div class="col-6">
                <div class="stat-card">
                    <div class="stat-icon" style="background:rgba(201,168,76,0.15)">
                        <i class="bi bi-people" style="color:var(--gold)"></i>
                    </div>
                    <div>
                        <div class="stat-val" id="s-customers"><?= $stats['customers'] ?></div>
                        <div class="stat-lbl">Kayıtlı Müşteri</div>
                    </div>
                </div>
            </div>

            <div class="col-6">
                <div class="stat-card">
                    <div class="stat-icon" style="background:rgba(32,201,151,0.15)">
                        <i class="bi bi-person-badge" style="color:#20c997"></i>
                    </div>
                    <div>
                        <div class="stat-val" id="s-staff-working"><?= $stats['staff_working'] ?></div>
                        <div class="stat-lbl">Bugün Vardiyada</div>
                    </div>
                </div>
            </div>

            <div class="col-6">
                <div class="stat-card">
                    <div class="stat-icon" style="background:rgba(220,53,69,0.15)">
                        <i class="bi bi-clock-history" style="color:#dc3545"></i>
                    </div>
                    <div>
                        <div class="stat-val" id="s-late-today"><?= $stats['late_today'] ?></div>
                        <div class="stat-lbl">Bugün Geç Kalan</div>
                    </div>
                </div>
            </div>

        </div>
    </div>

    <!-- SAĞ: Hızlı İşlemler + Son 7 Gün -->
    <div class="col-12 col-lg-4 d-flex flex-column gap-3">

        <!-- Hızlı İşlemler -->
        <div class="card">
            <div class="card-header">
                <i class="bi bi-lightning me-2 text-gold"></i>Hızlı İşlemler
            </div>
            <div class="card-body">
                <div class="row g-2">
                    <div class="col-6">
                        <a href="reservations.php" class="btn btn-outline-secondary w-100 text-start fs-12">
                            <i class="bi bi-calendar-plus me-1"></i>Rezervasyon Yönet
                        </a>
                    </div>
                    <div class="col-6">
                        <a href="daily_orders.php" class="btn btn-outline-secondary w-100 text-start fs-12">
                            <i class="bi bi-plus-circle me-1"></i>Yeni Sipariş
                        </a>
                    </div>
                    <div class="col-6">
                        <a href="customers.php" class="btn btn-outline-secondary w-100 text-start fs-12">
                            <i class="bi bi-person-plus me-1"></i>Müşteri CRM
                        </a>
                    </div>
                    <div class="col-6">
                        <a href="staff.php" class="btn btn-outline-secondary w-100 text-start fs-12">
                            <i class="bi bi-person-check me-1"></i>Günlük Devam
                        </a>
                    </div>
                </div>
            </div>
        </div>

        <!-- Son 7 Gün Rezervasyon -->
        <div class="card flex-grow-1">
            <div class="card-header">
                <i class="bi bi-bar-chart me-2 text-gold"></i>Son 7 Gün Rezervasyon
            </div>
            <div class="card-body d-flex align-items-center">
                <canvas id="resChart" style="width:100%;max-height:180px"></canvas>
            </div>
        </div>

    </div>

</div>

<!-- Alt: Bekleyen Rezervasyonlar - tam genişlik -->
<div class="row g-3">
    <div class="col-12">
        <div class="card">
            <div class="card-header d-flex justify-content-between align-items-center">
                <span><i class="bi bi-hourglass-split me-2 text-warning"></i>Bekleyen Rezervasyonlar</span>
                <a href="reservations.php" class="btn btn-sm btn-outline-secondary fs-12">Tümü</a>
            </div>
            <div class="card-body p-0">
                <div class="table-responsive">
                    <table class="table table-hover mb-0">
                        <thead>
                            <tr>
                                <th>Tarih/Saat</th>
                                <th>Ad Soyad</th>
                                <th>Kişi</th>
                                <th>İşlem</th>
                            </tr>
                        </thead>
                        <tbody id="pendingResList">
                        <?php foreach ($pending_res as $r): ?>
                            <tr>
                                <td>
                                    <div><?= format_date($r['date']) ?></div>
                                    <div class="text-muted fs-12"><?= format_time($r['time']) ?></div>
                                </td>
                                <td>
                                    <div><?= e($r['full_name']) ?></div>
                                    <div class="text-muted fs-12"><?= e($r['phone']) ?></div>
                                </td>
                                <td><span class="badge bg-secondary"><?= $r['guest_count'] ?> kişi</span></td>
                                <td>
                                    <a href="reservations.php?id=<?= $r['id'] ?>" class="btn btn-sm btn-outline-warning btn-action">
                                        <i class="bi bi-eye"></i>
                                    </a>
                                </td>
                            </tr>
                        <?php endforeach; ?>
                        <?php if (empty($pending_res)): ?>
                            <tr><td colspan="4" class="text-center text-muted py-4">Bekleyen rezervasyon yok.</td></tr>
                        <?php endif; ?>
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>

</div>

<!-- Yeni rezervasyon bildirim modalı -->
<div class="notification-modal-overlay d-none" id="newResModal">
    <div class="notification-modal-box alarm-box">
        <div class="alarm-bell-wrap mb-3">
            <i class="bi bi-bell-fill alarm-bell-icon"></i>
        </div>
        <div class="alarm-title mb-1">YENİ REZERVASYON!</div>
        <div class="alarm-subtitle mb-3">Onay bekliyor</div>
        <div id="newResContent" class="mb-4 text-start alarm-content"></div>
        <button class="btn btn-gold px-5 py-2 fw-bold" id="newResOkBtn" style="font-size:15px;min-width:200px">
            <i class="bi bi-check-lg me-2"></i>TAMAM, GÖRDÜM
        </button>
        <div class="mt-3" style="font-size:11px;color:var(--text-muted)">
            Bu butona basana kadar alarm çalmaya devam eder
        </div>
    </div>
</div>

<?php
$chartLabels = array_column($last7, 'd');
$chartData   = array_column($last7, 'cnt');
?>

<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.2/dist/chart.umd.min.js"></script>
<script>

// ---- Chart ----
(function() {
    const ctx = document.getElementById('resChart').getContext('2d');
    new Chart(ctx, {
        type: 'bar',
        data: {
            labels: <?= json_encode($chartLabels) ?>,
            datasets: [{
                label: 'Rezervasyon',
                data: <?= json_encode($chartData) ?>,
                backgroundColor: 'rgba(201,168,76,0.5)',
                borderColor: '#c9a84c',
                borderWidth: 1,
                borderRadius: 4,
            }]
        },
        options: {
            responsive: true,
            plugins: { legend: { display: false } },
            scales: {
                x: { ticks: { color: '#666' }, grid: { color: 'rgba(255,255,255,0.05)' } },
                y: { beginAtZero: true, ticks: { color: '#666', stepSize: 1 }, grid: { color: 'rgba(255,255,255,0.05)' } }
            }
        }
    });
})();

// ---- Stats JSON yenileme ----
async function loadStats() {
    try {
        const data = await apiGet('/api/admin/dashboard_stats.php');
        if (!data.success) return;
        const s = data.stats;
        document.getElementById('s-res-pending').textContent    = s.res_pending;
        document.getElementById('s-res-confirmed').textContent  = s.res_confirmed;
        document.getElementById('s-res-archive').textContent    = s.res_archive;
        document.getElementById('s-customers').textContent      = s.customers;
        document.getElementById('s-suppliers').textContent      = s.suppliers;
        document.getElementById('s-orders-today').textContent   = s.orders_today;
        document.getElementById('s-staff-working').textContent  = s.staff_working;
        document.getElementById('s-late-today').textContent     = s.late_today;
    } catch(e) {}
}

// ================================================================
// YENİ REZERVASYON ALARM SİSTEMİ
// ================================================================

// Son görülen rezervasyon ID'si - DB'den al, sessionStorage ile say. yenilemede koru
const _DB_MAX_ID = <?= (int)(db_value('SELECT COALESCE(MAX(id),0) FROM reservations') ?: 0) ?>;
(function() {
    const stored = parseInt(sessionStorage.getItem('otto_last_res_id') || '0');
    // stored varsa ve dbMaxId'den büyükse stored'u kullan (bir önceki oturumda yeni res gördük)
    // stored dbMaxId'den büyükse: büyük olasılıkla eski session, dbMaxId kullan
    if (stored > 0 && stored <= _DB_MAX_ID) {
        window._lastKnownResId = stored;
    } else {
        window._lastKnownResId = _DB_MAX_ID;
        sessionStorage.setItem('otto_last_res_id', _DB_MAX_ID);
    }
})();
let lastKnownResId = window._lastKnownResId;
let audioInterval  = null;
let audioCtx       = null;

// AudioContext - kullanıcı etkileşiminde unlock
function getAudioCtx() {
    if (!audioCtx) {
        try { audioCtx = new (window.AudioContext || window.webkitAudioContext)(); }
        catch(e) { console.warn('AudioContext oluşturulamadı:', e); return null; }
    }
    return audioCtx;
}

// Her etkileşimde unlock dene
['click','keydown','touchend','touchstart'].forEach(ev => {
    document.addEventListener(ev, function _unlock() {
        const ctx = getAudioCtx();
        if (ctx && ctx.state === 'suspended') ctx.resume();
    }, { once: false, passive: true });
});

function _playRing(ctx, t) {
    [440, 480].forEach(freq => {
        try {
            const osc = ctx.createOscillator(), g = ctx.createGain();
            osc.connect(g); g.connect(ctx.destination);
            osc.type = 'sine'; osc.frequency.value = freq;
            g.gain.setValueAtTime(0, t);
            g.gain.linearRampToValueAtTime(0.6, t + 0.03);
            g.gain.setValueAtTime(0.6, t + 0.37);
            g.gain.linearRampToValueAtTime(0, t + 0.42);
            osc.start(t); osc.stop(t + 0.45);
        } catch(e) {}
    });
}

function playAlarm() {
    const ctx = getAudioCtx();
    if (!ctx) return;
    const go = () => {
        const t = ctx.currentTime;
        _playRing(ctx, t + 0.05); _playRing(ctx, t + 0.55);
        _playRing(ctx, t + 1.40); _playRing(ctx, t + 1.90);
    };
    if (ctx.state === 'suspended') { ctx.resume().then(go); } else { go(); }
}

function showAlarmModal(reservations) {
    const r = reservations[0];
    document.getElementById('newResContent').innerHTML =
        `<b>Ad Soyad:</b> ${r.full_name}<br>
         <b>Telefon:</b> ${r.phone}<br>
         <b>Tarih:</b> ${r.date}<br>
         <b>Saat:</b> ${r.time}<br>
         <b>Kişi Sayısı:</b> ${r.guest_count}` +
        (reservations.length > 1 ? `<br><br><span class="text-warning">+${reservations.length-1} rezervasyon daha var!</span>` : '');
    document.getElementById('newResModal').classList.remove('d-none');
    if (!audioInterval) { audioInterval = setInterval(playAlarm, 2800); }
    playAlarm();
}

async function checkNewReservations() {
    try {
        const url = '/api/admin/check_new_reservations.php?last_id=' + lastKnownResId + '&_=' + Date.now();
        const res  = await fetch(url, {
            headers: { 'X-CSRF-Token': CSRF_TOKEN, 'X-Requested-With': 'XMLHttpRequest' },
            cache: 'no-store'
        });

        if (!res.ok) {
            console.warn('Rezervasyon kontrol hatası:', res.status);
            return;
        }

        let data;
        try { data = await res.json(); }
        catch(e) { console.warn('JSON parse hatası - session süresi dolmuş olabilir'); return; }

        if (!data.success) { console.warn('API hatası:', data.message); return; }

        // max_id'yi her zaman güncelle (yeni res yoksa da)
        if (data.max_id > lastKnownResId) {
            lastKnownResId = data.max_id;
            sessionStorage.setItem('otto_last_res_id', lastKnownResId);
        }

        if (data.new_count > 0) {
            showAlarmModal(data.reservations);
        }

    } catch(e) { console.warn('checkNewReservations hata:', e.message); }
}

document.getElementById('newResOkBtn').addEventListener('click', () => {
    document.getElementById('newResModal').classList.add('d-none');
    if (audioInterval) { clearInterval(audioInterval); audioInterval = null; }
    // Rezervasyonlar sayfasına git
    // location.href = '/admin/reservations.php'; // İstersen açabilirsiniz
});
</script>


<?php require_once __DIR__ . '/../includes/admin_layout_end.php'; ?>
<script>
// ---- Auto refresh ----
let refreshSeconds = 30;
const refreshSelect = document.getElementById('refreshSelect');

refreshSelect.addEventListener('change', () => {
    refreshSeconds = parseInt(refreshSelect.value);
    stopAutoRefresh();
    if (refreshSeconds > 0) {
        document.getElementById('refreshCountdown').textContent = refreshSeconds + 's';
        startAutoRefresh(refreshSeconds, loadStats);
    } else {
        document.getElementById('refreshCountdown').textContent = '—';
    }
});

// Sayfa açılır açılmaz hemen geri sayımı başlat
document.getElementById('refreshCountdown').textContent = refreshSeconds + 's';
startAutoRefresh(refreshSeconds, loadStats);

// Yeni rezervasyon kontrolü her 15 sn + sayfa açılınca hemen bir kez
setInterval(checkNewReservations, 15000);
checkNewReservations();
</script>