Initial commit

This commit is contained in:
Porawit Dongwang
2026-09-16 23:20:08 +07:00
commit 0041668dbb
32577 changed files with 3687927 additions and 0 deletions
+267
View File
@@ -0,0 +1,267 @@
<?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, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}
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>