Files
2026-09-16 23:20:08 +07:00

370 lines
20 KiB
PHP

<?php
require_once 'config.php';
requireLogin();
requireAdmin();
$success = '';
$error = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$days_before = $_POST['days_before_due'] ?? '3';
$notification_time = $_POST['notification_time'] ?? '08:00';
$footer_text = $_POST['footer_text'] ?? 'ระบบแจ้งเตือนส่งงาน';
$telegram_bot_token = $_POST['telegram_bot_token'] ?? '';
$telegram_group_chat_id = $_POST['telegram_group_chat_id'] ?? '';
$line_notify_token = $_POST['line_notify_token'] ?? '';
// Checkboxes
$notify_via_line = isset($_POST['notify_via_line']) ? '1' : '0';
$notify_via_telegram = isset($_POST['notify_via_telegram']) ? '1' : '0';
try {
$pdo->beginTransaction();
$settings = [
'days_before_due' => $days_before,
'notification_time' => $notification_time,
'footer_text' => $footer_text,
'telegram_bot_token' => $telegram_bot_token,
'telegram_group_chat_id' => $telegram_group_chat_id,
'line_notify_token' => $line_notify_token,
'notify_via_line' => $notify_via_line,
'notify_via_telegram' => $notify_via_telegram
];
$stmt = $pdo->prepare("INSERT INTO settings (setting_key, setting_value) VALUES (?, ?) ON DUPLICATE KEY UPDATE setting_value = ?");
foreach ($settings as $key => $val) {
$stmt->execute([$key, $val, $val]);
}
$pdo->commit();
// Exclude sensitive tokens from logs
$safe_settings = $settings;
$safe_settings['telegram_bot_token'] = !empty($safe_settings['telegram_bot_token']) ? '***' : '';
$safe_settings['line_notify_token'] = !empty($safe_settings['line_notify_token']) ? '***' : '';
logActivity($pdo, 'UPDATE_SETTINGS', json_encode($safe_settings, JSON_UNESCAPED_UNICODE));
$success = 'บันทึกการตั้งค่าสำเร็จ';
} catch (Exception $e) {
$pdo->rollBack();
$error = 'เกิดข้อผิดพลาด: ' . $e->getMessage();
}
}
$days_before_due = getSetting($pdo, 'days_before_due') ?: '3';
$notification_time = getSetting($pdo, 'notification_time') ?: '08:00';
$footer_text = getSetting($pdo, 'footer_text') ?: 'ระบบแจ้งเตือนส่งงาน';
$bot_token = getSetting($pdo, 'telegram_bot_token') ?: '';
$group_chat_id = getSetting($pdo, 'telegram_group_chat_id') ?: '';
$line_notify_token = getSetting($pdo, 'line_notify_token') ?: '';
$notify_via_line = getSetting($pdo, 'notify_via_line') === '0' ? false : true; // Default true
$notify_via_telegram = getSetting($pdo, 'notify_via_telegram') === '0' ? false : true; // Default true
include 'includes/header.php';
include 'includes/sidebar.php';
?>
<div class="mb-4">
<h2 class="text-2xl font-bold text-gray-800">ตั้งค่าระบบ</h2>
<p class="text-gray-600 mt-1">ตั้งค่าการแจ้งเตือนและการเชื่อมต่อ API</p>
</div>
<!-- Tabs Navigation -->
<div class="mb-6 border-b border-gray-200">
<nav class="-mb-px flex space-x-8" aria-label="Tabs">
<button onclick="switchTab('general')" id="tab-general" class="border-blue-500 text-blue-600 whitespace-nowrap py-3 px-1 border-b-2 font-bold text-sm transition-colors">
<i class="fas fa-cogs mr-2"></i> การตั้งค่าทั่วไป
</button>
<button onclick="switchTab('logs')" id="tab-logs" class="border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 whitespace-nowrap py-3 px-1 border-b-2 font-medium text-sm transition-colors">
<i class="fas fa-history mr-2"></i> ประวัติการใช้งาน
</button>
</nav>
</div>
<?php if ($error): ?>
<div class="bg-red-100 border-l-4 border-red-500 text-red-700 p-4 mb-6 rounded shadow-sm">
<p><i class="fas fa-exclamation-circle mr-2"></i> <?= htmlspecialchars($error) ?></p>
</div>
<?php endif; ?>
<?php if ($success): ?>
<script>
Swal.fire({
icon: 'success',
title: 'บันทึกสำเร็จ',
text: '<?= addslashes($success) ?>',
timer: 2000,
showConfirmButton: false
});
</script>
<?php endif; ?>
<!-- General Settings Content -->
<div id="content-general">
<form method="POST" action="" id="settingsForm">
<!-- General Settings -->
<div class="bg-white rounded-lg shadow-md p-6 mb-6">
<h3 class="text-lg font-bold mb-4 border-b pb-2"><i class="fas fa-cogs text-gray-500 mr-2"></i> ตั้งค่าทั่วไป (General)</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label class="block text-gray-700 text-sm font-bold mb-2" for="days_before_due">จำนวนวันแจ้งเตือนล่วงหน้า (วัน)</label>
<div class="flex items-center">
<input type="number" name="days_before_due" id="days_before_due" value="<?= htmlspecialchars($days_before_due) ?>" min="1" max="30" class="shadow appearance-none border rounded py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:ring-2 focus:ring-blue-500 w-24 mr-2" required>
<span class="text-gray-600 text-sm">วันก่อนถึงวันกำหนดส่ง</span>
</div>
</div>
<div>
<label class="block text-gray-700 text-sm font-bold mb-2" for="notification_time">เวลาที่จะแจ้งเตือน</label>
<input type="time" name="notification_time" id="notification_time" value="<?= htmlspecialchars($notification_time) ?>" class="shadow appearance-none border rounded py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:ring-2 focus:ring-blue-500 w-32" required>
<p class="text-xs text-gray-500 mt-1">เวลาที่ระบบจะส่งข้อความแจ้งเตือน (ใช้เป็นข้อมูลอ้างอิงในการตั้ง Cron Job)</p>
</div>
<div class="md:col-span-2">
<label class="block text-gray-700 text-sm font-bold mb-2" for="footer_text">ข้อความ Footer ด้านล่างสุด</label>
<input type="text" name="footer_text" id="footer_text" value="<?= htmlspecialchars($footer_text) ?>" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:ring-2 focus:ring-blue-500" placeholder="เช่น ระบบส่งงานนักเรียน" required>
<p class="text-xs text-gray-500 mt-1">ข้อความที่จะแสดงที่แถบด้านล่างสุดของทุกหน้า (ปีปัจจุบันจะถูกเพิ่มให้อัตโนมัติ)</p>
</div>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
<!-- LINE Settings -->
<div class="bg-white rounded-lg shadow-md p-6 border-t-4 border-green-500">
<div class="flex items-center justify-between mb-4 border-b pb-2">
<h3 class="text-lg font-bold text-green-600"><i class="fab fa-line mr-2"></i> LINE Notify</h3>
<label class="inline-flex items-center cursor-pointer">
<input type="checkbox" name="notify_via_line" value="1" class="sr-only peer" <?= $notify_via_line ? 'checked' : '' ?>>
<div class="relative w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-green-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-green-600"></div>
<span class="ml-3 text-sm font-medium text-gray-700">เปิดใช้งาน</span>
</label>
</div>
<div class="mb-4">
<label class="block text-gray-700 text-sm font-bold mb-2" for="line_notify_token">LINE Notify Token</label>
<input type="password" name="line_notify_token" id="line_notify_token" value="<?= htmlspecialchars($line_notify_token) ?>" placeholder="ออก Token จาก notify-bot.line.me" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:ring-2 focus:ring-green-500">
<p class="text-xs text-gray-500 mt-1">ใช้สำหรับส่งแจ้งเตือนเข้ากลุ่ม LINE</p>
</div>
<div class="mt-4 flex space-x-2">
<button type="button" onclick="testLine()" class="bg-gray-100 hover:bg-gray-200 text-gray-800 font-semibold py-2 px-4 border border-gray-300 rounded shadow-sm text-sm flex-1">
<i class="fas fa-paper-plane text-green-500 mr-1"></i> ทดสอบส่ง
</button>
</div>
<div class="mt-6 bg-gray-50 p-3 rounded text-sm text-gray-600">
<strong>วิธีใช้งาน:</strong>
<ol class="list-decimal pl-4 mt-1">
<li>ไปที่ <a href="https://notify-bot.line.me/" target="_blank" class="text-blue-500 hover:underline">notify-bot.line.me</a></li>
<li>ออก Token โดยเลือกกลุ่มที่ต้องการ</li>
<li>เชิญ "LINE Notify" เข้ากลุ่มนั้น</li>
</ol>
</div>
</div>
<!-- Telegram Settings -->
<div class="bg-white rounded-lg shadow-md p-6 border-t-4 border-blue-500">
<div class="flex items-center justify-between mb-4 border-b pb-2">
<h3 class="text-lg font-bold text-blue-600"><i class="fab fa-telegram-plane mr-2"></i> Telegram Bot</h3>
<label class="inline-flex items-center cursor-pointer">
<input type="checkbox" name="notify_via_telegram" value="1" class="sr-only peer" <?= $notify_via_telegram ? 'checked' : '' ?>>
<div class="relative w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div>
<span class="ml-3 text-sm font-medium text-gray-700">เปิดใช้งาน</span>
</label>
</div>
<div class="mb-4">
<label class="block text-gray-700 text-sm font-bold mb-2" for="telegram_bot_token">Telegram Bot Token</label>
<input type="password" name="telegram_bot_token" id="telegram_bot_token" value="<?= htmlspecialchars($bot_token) ?>" placeholder="123456:ABC-DEF1234gh..." class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:ring-2 focus:ring-blue-500 mb-3">
<label class="block text-gray-700 text-sm font-bold mb-2" for="telegram_group_chat_id">Telegram Group Chat ID</label>
<input type="text" name="telegram_group_chat_id" id="telegram_group_chat_id" value="<?= htmlspecialchars($group_chat_id) ?>" placeholder="-1001234567890" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
<div class="mt-4 flex space-x-2">
<button type="button" onclick="testTelegram()" class="bg-gray-100 hover:bg-gray-200 text-gray-800 font-semibold py-2 px-4 border border-gray-300 rounded shadow-sm text-sm flex-1">
<i class="fas fa-paper-plane text-blue-500 mr-1"></i> ทดสอบส่ง
</button>
</div>
<div class="mt-6 bg-gray-50 p-3 rounded text-sm text-gray-600">
<strong>วิธีใช้งาน:</strong>
<ol class="list-decimal pl-4 mt-1">
<li>สร้าง Bot กับ <a href="https://t.me/botfather" target="_blank" class="text-blue-500 hover:underline">@BotFather</a></li>
<li>ดึง Bot เข้ากลุ่มที่ต้องการแจ้งเตือน</li>
<li>หา Chat ID ของกลุ่ม (ใช้ @RawDataBot ช่วยหาได้)</li>
</ol>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow-md p-4 flex justify-between items-center sticky bottom-4 border border-gray-200">
<div>
<a href="cron_notify.php" target="_blank" class="text-blue-600 hover:text-blue-800 text-sm font-medium">
<i class="fas fa-external-link-alt mr-1"></i> เปิดรัน Cron Job แบบ Manual
</a>
</div>
<button type="submit" class="bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-6 rounded shadow-lg flex items-center transition duration-200">
<i class="fas fa-save mr-2"></i> บันทึกการตั้งค่าทั้งหมด
</button>
</div>
</form>
<script>
function testLine() {
const token = document.getElementById('line_notify_token').value;
if (!token) {
Swal.fire('ข้อผิดพลาด', 'กรุณากรอก LINE Notify Token', 'error');
return;
}
Swal.fire({
title: 'กำลังส่ง...',
allowOutsideClick: false,
didOpen: () => { Swal.showLoading(); }
});
$.ajax({
url: 'api/test_notify.php',
method: 'POST',
data: { type: 'line', token: token },
dataType: 'json',
success: function(res) {
Swal.fire(res.status === 'success' ? 'สำเร็จ' : 'ล้มเหลว', res.message, res.status);
},
error: function(xhr, status, error) {
console.error(xhr.responseText);
let errMsg = 'ไม่สามารถเชื่อมต่อเซิร์ฟเวอร์ได้';
if (xhr.responseText) {
// Try to extract some readable error if possible, or just show a portion of it
errMsg += '<br><br><span class="text-xs text-red-500">' + xhr.responseText.substring(0, 200) + '...</span>';
}
Swal.fire({
title: 'ข้อผิดพลาด',
html: errMsg,
icon: 'error'
});
}
});
}
function testTelegram() {
const bot_token = document.getElementById('telegram_bot_token').value;
const chat_id = document.getElementById('telegram_group_chat_id').value;
if (!bot_token || !chat_id) {
Swal.fire('ข้อผิดพลาด', 'กรุณากรอกทั้ง Bot Token และ Chat ID', 'error');
return;
}
Swal.fire({
title: 'กำลังส่ง...',
allowOutsideClick: false,
didOpen: () => { Swal.showLoading(); }
});
$.ajax({
url: 'api/test_notify.php',
method: 'POST',
data: { type: 'telegram', bot_token: bot_token, chat_id: chat_id },
dataType: 'json',
success: function(res) {
Swal.fire(res.status === 'success' ? 'สำเร็จ' : 'ล้มเหลว', res.message, res.status);
},
error: function(xhr, status, error) {
console.error(xhr.responseText);
let errMsg = 'ไม่สามารถเชื่อมต่อเซิร์ฟเวอร์ได้';
if (xhr.responseText) {
errMsg += '<br><br><span class="text-xs text-red-500 text-left overflow-hidden">' + xhr.responseText.substring(0, 200) + '...</span>';
}
Swal.fire({
title: 'ข้อผิดพลาด',
html: errMsg,
icon: 'error'
});
}
});
}
function switchTab(tab) {
// Hide all contents
document.getElementById('content-general').classList.add('hidden');
document.getElementById('content-logs').classList.add('hidden');
// Reset all tabs styling
document.getElementById('tab-general').className = "border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 whitespace-nowrap py-3 px-1 border-b-2 font-medium text-sm transition-colors";
document.getElementById('tab-logs').className = "border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 whitespace-nowrap py-3 px-1 border-b-2 font-medium text-sm transition-colors";
// Show active content and style active tab
document.getElementById('content-' + tab).classList.remove('hidden');
document.getElementById('tab-' + tab).className = "border-blue-500 text-blue-600 whitespace-nowrap py-3 px-1 border-b-2 font-bold text-sm transition-colors";
}
</script>
</div> <!-- End content-general -->
<div id="content-logs" class="hidden">
<!-- Activity Logs Section -->
<div class="bg-white rounded-lg shadow-md p-6 mb-6">
<h3 class="text-lg font-bold mb-4 border-b pb-2"><i class="fas fa-history text-gray-500 mr-2"></i> ประวัติการใช้งานระบบ (Activity Logs)</h3>
<table id="logsTable" class="dataTable display min-w-full bg-white w-full">
<thead class="bg-gray-100 text-gray-600">
<tr>
<th class="py-3 px-4 text-center font-semibold text-sm border-b w-32">วันเวลา</th>
<th class="py-3 px-4 text-center font-semibold text-sm border-b w-48">ผู้ใช้งาน</th>
<th class="py-3 px-4 text-center font-semibold text-sm border-b w-48">การกระทำ (Action)</th>
<th class="py-3 px-4 text-center font-semibold text-sm border-b">รายละเอียด (Details)</th>
<th class="py-3 px-4 text-center font-semibold text-sm border-b w-32">IP Address</th>
</tr>
</thead>
<tbody class="text-gray-700">
<!-- Data will be loaded via AJAX -->
</tbody>
</table>
</div>
<!-- DataTables Export Plugins for Logs -->
<script src="https://cdn.datatables.net/buttons/2.4.1/js/dataTables.buttons.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
<script src="https://cdn.datatables.net/buttons/2.4.1/js/buttons.html5.min.js"></script>
<script>
$(document).ready(function() {
$('#logsTable').DataTable({
ajax: 'api/get_logs_table.php',
columns: [
{ data: 'created_at' },
{ data: 'user' },
{ data: 'action' },
{ data: 'details' },
{ data: 'ip_address' }
],
order: [[0, 'desc']], // Sort by date descending
scrollX: true,
columnDefs: [{ className: "dt-head-center", targets: "_all" }],
pageLength: 10,
dom: '<"flex justify-between items-center mb-4"lBf>rt<"flex justify-between items-center mt-4"ip>',
buttons: [
{
extend: 'excelHtml5',
text: '<i class="fas fa-file-excel mr-1"></i> ส่งออก Excel',
className: 'bg-green-600 text-white hover:bg-green-700 px-3 py-1 rounded text-sm',
title: 'ประวัติการใช้งานระบบ',
exportOptions: {
columns: ':visible'
}
}
],
language: {
url: '//cdn.datatables.net/plug-ins/1.13.6/i18n/th.json'
}
});
});
</script>
</div>
<?php include 'includes/footer.php'; ?>