268 lines
16 KiB
PHP
268 lines
16 KiB
PHP
<?php
|
|
require_once 'init.php';
|
|
|
|
// Fetch websites
|
|
$stmt = $pdo->query("SELECT * FROM websites ORDER BY display_order ASC, name ASC");
|
|
$websites = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
$summary = ['total' => count($websites), 'normal' => 0, 'maintenance' => 0, 'cancelled' => 0];
|
|
foreach ($websites as $web) {
|
|
if(isset($summary[$web['status']])) {
|
|
$summary[$web['status']]++;
|
|
}
|
|
}
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html lang="th">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title><?= htmlspecialchars($globalSettings['site_name'] ?? 'ศูนย์รวมเว็บหน่วยงาน') ?></title>
|
|
<link rel="icon" type="image/png" href="assets/images/logo.png">
|
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
<link rel="stylesheet" href="assets/css/style.css">
|
|
<!-- FontAwesome for icons -->
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
|
<!-- SweetAlert2 -->
|
|
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
|
</head>
|
|
<body class="antialiased min-h-screen flex flex-col items-center py-10 px-4">
|
|
|
|
<div class="w-full max-w-6xl glass-container">
|
|
|
|
<div class="text-center mb-10">
|
|
<!-- Logo -->
|
|
<img src="assets/images/logo.png" alt="Logo" class="logo-img mx-auto mb-4" onerror="this.src='https://via.placeholder.com/150x150?text=LOGO'">
|
|
<h1 class="text-3xl md:text-4xl font-bold text-gray-800 mb-2"><?= htmlspecialchars($globalSettings['site_name'] ?? 'ศูนย์รวมเว็บหน่วยงาน') ?></h1>
|
|
<p class="text-gray-600 text-lg"><?= htmlspecialchars($globalSettings['site_subtitle'] ?? 'One Stop Web Service') ?></p>
|
|
</div>
|
|
|
|
<!-- Status Summary Cards -->
|
|
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-8" id="statusSummary">
|
|
<div id="filter-card-all" onclick="setStatusFilter('all')" class="cursor-pointer bg-white rounded-xl shadow-sm border border-gray-100 p-4 text-center transform hover:-translate-y-1 transition duration-300 ring-2 ring-emerald-500 opacity-100">
|
|
<div class="text-gray-500 text-sm font-medium mb-1">ทั้งหมด</div>
|
|
<div class="text-2xl font-bold text-gray-800"><span id="sum-total"><?= $summary['total'] ?></span></div>
|
|
</div>
|
|
<div id="filter-card-normal" onclick="setStatusFilter('normal')" class="cursor-pointer bg-green-50 rounded-xl shadow-sm border border-green-100 p-4 text-center transform hover:-translate-y-1 transition duration-300 opacity-60 hover:opacity-100">
|
|
<div class="text-green-600 text-sm font-medium mb-1"><i class="fa-solid fa-circle-check mr-1"></i>ปกติ</div>
|
|
<div class="text-2xl font-bold text-green-700"><span id="sum-normal"><?= $summary['normal'] ?></span></div>
|
|
</div>
|
|
<div id="filter-card-maintenance" onclick="setStatusFilter('maintenance')" class="cursor-pointer bg-orange-50 rounded-xl shadow-sm border border-orange-100 p-4 text-center transform hover:-translate-y-1 transition duration-300 opacity-60 hover:opacity-100">
|
|
<div class="text-orange-600 text-sm font-medium mb-1"><i class="fa-solid fa-person-digging mr-1"></i>ปรับปรุง</div>
|
|
<div class="text-2xl font-bold text-orange-700"><span id="sum-maintenance"><?= $summary['maintenance'] ?></span></div>
|
|
</div>
|
|
<div id="filter-card-cancelled" onclick="setStatusFilter('cancelled')" class="cursor-pointer bg-red-50 rounded-xl shadow-sm border border-red-100 p-4 text-center transform hover:-translate-y-1 transition duration-300 opacity-60 hover:opacity-100">
|
|
<div class="text-red-600 text-sm font-medium mb-1"><i class="fa-solid fa-circle-xmark mr-1"></i>ยกเลิก</div>
|
|
<div class="text-2xl font-bold text-red-700"><span id="sum-cancelled"><?= $summary['cancelled'] ?></span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex justify-between items-center mb-8 flex-wrap gap-4">
|
|
<div class="w-full md:w-1/2 lg:w-1/3 relative">
|
|
<i class="fa-solid fa-magnifying-glass absolute left-4 top-1/2 transform -translate-y-1/2 text-gray-400"></i>
|
|
<input type="text" id="searchInput" class="magic-input pl-10" placeholder="ค้นหาเว็บไซต์...">
|
|
</div>
|
|
<div class="flex gap-2 items-center w-full md:w-auto mt-4 md:mt-0">
|
|
<a href="admin/login.php" class="magic-btn-outline text-sm">
|
|
<i class="fa-solid fa-gear mr-2"></i> ระบบจัดการ
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="max-h-[500px] overflow-y-auto pr-2 custom-scrollbar">
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4" id="websitesContainer">
|
|
<?php if(count($websites) > 0): ?>
|
|
<?php foreach($websites as $web): ?>
|
|
<?php
|
|
$targetUrl = ($web['status'] == 'normal') ? 'go.php?id='.$web['id'] : '#';
|
|
$logoUrl = !empty($web['logo_url']) ? htmlspecialchars($web['logo_url']) : 'assets/images/logo.png';
|
|
?>
|
|
<a href="<?= $targetUrl ?>" <?= ($web['status'] == 'normal') ? 'target="_blank"' : '' ?> data-status="<?= $web['status'] ?>" data-name="<?= htmlspecialchars($web['name']) ?>" class="magic-card p-4 flex flex-row items-center text-left website-item relative group">
|
|
|
|
<?php if($web['status'] == 'cancelled'): ?>
|
|
<div class="absolute top-3 right-3"><span class="flex h-3 w-3 relative"><span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-red-400 opacity-75"></span><span class="relative inline-flex rounded-full h-3 w-3 bg-red-500" title="ยกเลิกใช้งาน"></span></span></div>
|
|
<?php elseif($web['status'] == 'maintenance'): ?>
|
|
<div class="absolute top-3 right-3"><span class="flex h-3 w-3 relative"><span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-orange-400 opacity-75"></span><span class="relative inline-flex rounded-full h-3 w-3 bg-orange-500" title="ปรับปรุง"></span></span></div>
|
|
<?php endif; ?>
|
|
|
|
<div class="w-12 h-12 flex-shrink-0 rounded-full bg-white flex items-center justify-center mr-4 shadow-sm border border-gray-100 overflow-hidden logo-container">
|
|
<img src="<?= $logoUrl ?>" alt="logo" class="w-full h-full object-cover" onerror="this.src='assets/images/logo.png'">
|
|
</div>
|
|
|
|
<div class="flex-1 min-w-0">
|
|
<h3 class="font-semibold text-base text-gray-800 web-name truncate"><?= htmlspecialchars($web['name']) ?></h3>
|
|
<span class="hidden web-url"><?= htmlspecialchars($web['url']) ?></span>
|
|
</div>
|
|
|
|
<div class="ml-2 flex-shrink-0 text-xs px-2 py-1 bg-gray-100 text-gray-500 rounded-lg">
|
|
<i class="fa-solid fa-mouse-pointer mr-1"></i> เปิดแล้ว <span id="click-count-<?= $web['id'] ?>"><?= number_format($web['click_count']) ?></span> ครั้ง
|
|
</div>
|
|
</a>
|
|
<?php endforeach; ?>
|
|
<?php else: ?>
|
|
<div class="col-span-full text-center py-10 text-gray-500">
|
|
<i class="fa-solid fa-folder-open text-4xl mb-3 text-gray-300"></i>
|
|
<p>ยังไม่มีข้อมูลเว็บไซต์</p>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<script>
|
|
function bindClickEvents() {
|
|
document.querySelectorAll('.website-item').forEach(item => {
|
|
item.addEventListener('click', function(e) {
|
|
const status = this.getAttribute('data-status');
|
|
const name = this.getAttribute('data-name');
|
|
|
|
if (status === 'cancelled') {
|
|
e.preventDefault();
|
|
Swal.fire({
|
|
icon: 'error',
|
|
title: 'ยกเลิกการใช้งาน',
|
|
text: `เว็บไซต์ ${name} ถูกยกเลิกการใช้งานแล้ว`,
|
|
confirmButtonColor: '#047857'
|
|
});
|
|
} else if (status === 'maintenance') {
|
|
e.preventDefault();
|
|
Swal.fire({
|
|
icon: 'warning',
|
|
title: 'ปิดปรับปรุง',
|
|
text: `เว็บไซต์ ${name} กำลังอยู่ในช่วงปิดปรับปรุงระบบชั่วคราว`,
|
|
confirmButtonColor: '#047857'
|
|
});
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
bindClickEvents();
|
|
|
|
let currentStatusFilter = 'all';
|
|
|
|
function setStatusFilter(status) {
|
|
currentStatusFilter = status;
|
|
|
|
const allStatuses = ['all', 'normal', 'maintenance', 'cancelled'];
|
|
allStatuses.forEach(s => {
|
|
const card = document.getElementById('filter-card-' + s);
|
|
if(s === status) {
|
|
card.classList.remove('opacity-60', 'hover:opacity-100');
|
|
card.classList.add('opacity-100', 'ring-2', 'ring-emerald-500');
|
|
} else {
|
|
card.classList.add('opacity-60', 'hover:opacity-100');
|
|
card.classList.remove('opacity-100', 'ring-2', 'ring-emerald-500');
|
|
}
|
|
});
|
|
|
|
filterItems();
|
|
}
|
|
|
|
function filterItems() {
|
|
const searchTerm = document.getElementById('searchInput').value.toLowerCase();
|
|
const items = document.querySelectorAll('.website-item');
|
|
|
|
items.forEach(item => {
|
|
const name = item.querySelector('.web-name').innerText.toLowerCase();
|
|
const url = item.querySelector('.web-url').innerText.toLowerCase();
|
|
const status = item.getAttribute('data-status');
|
|
|
|
const matchesSearch = name.includes(searchTerm) || url.includes(searchTerm);
|
|
const matchesStatus = (currentStatusFilter === 'all' || status === currentStatusFilter);
|
|
|
|
if(matchesSearch && matchesStatus) {
|
|
item.style.display = 'flex';
|
|
} else {
|
|
item.style.display = 'none';
|
|
}
|
|
});
|
|
}
|
|
|
|
// Simple client-side search filtering
|
|
document.getElementById('searchInput').addEventListener('input', filterItems);
|
|
|
|
function escapeHtml(unsafe) {
|
|
return (unsafe || '').toString()
|
|
.replace(/&/g, "&")
|
|
.replace(/</g, "<")
|
|
.replace(/>/g, ">")
|
|
.replace(/"/g, """)
|
|
.replace(/'/g, "'");
|
|
}
|
|
|
|
let lastDataString = '';
|
|
|
|
// Real-time list and stats polling (every 3 seconds)
|
|
setInterval(() => {
|
|
fetch('get_stats.php')
|
|
.then(res => res.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
const currentDataString = JSON.stringify(data.websites);
|
|
if (currentDataString !== lastDataString) {
|
|
lastDataString = currentDataString;
|
|
|
|
// Update Summary
|
|
document.getElementById('sum-total').innerText = data.summary.total;
|
|
document.getElementById('sum-normal').innerText = data.summary.normal;
|
|
document.getElementById('sum-maintenance').innerText = data.summary.maintenance;
|
|
document.getElementById('sum-cancelled').innerText = data.summary.cancelled;
|
|
|
|
// Re-render websites
|
|
const container = document.getElementById('websitesContainer');
|
|
if (data.websites.length === 0) {
|
|
container.innerHTML = `
|
|
<div class="col-span-full text-center py-10 text-gray-500">
|
|
<i class="fa-solid fa-folder-open text-4xl mb-3 text-gray-300"></i>
|
|
<p>ยังไม่มีข้อมูลเว็บไซต์</p>
|
|
</div>
|
|
`;
|
|
} else {
|
|
let html = '';
|
|
data.websites.forEach(web => {
|
|
const targetUrl = (web.status === 'normal') ? 'go.php?id=' + web.id : '#';
|
|
const logoUrl = web.logo_url ? escapeHtml(web.logo_url) : 'assets/images/logo.png';
|
|
const clickCount = new Intl.NumberFormat('en-US').format(web.click_count);
|
|
|
|
let badgeHtml = '';
|
|
if(web.status === 'cancelled') {
|
|
badgeHtml = `<div class="absolute top-3 right-3"><span class="flex h-3 w-3 relative"><span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-red-400 opacity-75"></span><span class="relative inline-flex rounded-full h-3 w-3 bg-red-500" title="ยกเลิกใช้งาน"></span></span></div>`;
|
|
} else if(web.status === 'maintenance') {
|
|
badgeHtml = `<div class="absolute top-3 right-3"><span class="flex h-3 w-3 relative"><span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-orange-400 opacity-75"></span><span class="relative inline-flex rounded-full h-3 w-3 bg-orange-500" title="ปรับปรุง"></span></span></div>`;
|
|
}
|
|
|
|
const targetAttr = (web.status === 'normal') ? 'target="_blank"' : '';
|
|
|
|
html += `
|
|
<a href="${targetUrl}" ${targetAttr} data-status="${web.status}" data-name="${escapeHtml(web.name)}" class="magic-card p-4 flex flex-row items-center text-left website-item relative group">
|
|
${badgeHtml}
|
|
<div class="w-12 h-12 flex-shrink-0 rounded-full bg-white flex items-center justify-center mr-4 shadow-sm border border-gray-100 overflow-hidden logo-container">
|
|
<img src="${logoUrl}" alt="logo" class="w-full h-full object-cover" onerror="this.src='assets/images/logo.png'">
|
|
</div>
|
|
<div class="flex-1 min-w-0">
|
|
<h3 class="font-semibold text-base text-gray-800 web-name truncate">${escapeHtml(web.name)}</h3>
|
|
<span class="hidden web-url">${escapeHtml(web.url)}</span>
|
|
</div>
|
|
<div class="ml-2 flex-shrink-0 text-xs px-2 py-1 bg-gray-100 text-gray-500 rounded-lg">
|
|
<i class="fa-solid fa-mouse-pointer mr-1"></i> เปิดแล้ว <span id="click-count-${web.id}">${clickCount}</span> ครั้ง
|
|
</div>
|
|
</a>
|
|
`;
|
|
});
|
|
container.innerHTML = html;
|
|
}
|
|
|
|
// Re-bind click handlers and re-apply search filter
|
|
bindClickEvents();
|
|
filterItems();
|
|
}
|
|
}
|
|
})
|
|
.catch(err => console.error('Error fetching data:', err));
|
|
}, 3000);
|
|
</script>
|
|
</body>
|
|
</html>
|