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

191 lines
12 KiB
PHP

<?php
session_start();
// ถ้าล็อกอินอยู่แล้ว ให้ไปหน้า dashboard ทันที
if (isset($_SESSION['user_id'])) {
header("Location: index.php");
exit();
}
$error = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
require_once 'includes/db.php';
$username = trim($_POST['username'] ?? '');
$password = $_POST['password'] ?? '';
if (empty($username) || empty($password)) {
$error = 'กรุณากรอกข้อมูลให้ครบถ้วน';
} else {
$stmt = $pdo->prepare("SELECT * FROM users WHERE id_card = ? OR username = ?");
$stmt->execute([$username, $username]);
$user = $stmt->fetch();
if ($user && password_verify($password, $user['password'])) {
// รหัสผ่านถูกต้อง สร้าง Session
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
$_SESSION['full_name'] = $user['full_name'];
$_SESSION['role'] = $user['role'];
require_once 'includes/logger.php';
logActivity('Login', 'users', $user['id']);
// ส่ง JSON กลับไปให้ Alpine.js ทำงานต่อ
header('Content-Type: application/json');
echo json_encode(['success' => true]);
exit();
} else {
$error = 'เลขบัตรประชาชน/ชื่อผู้ใช้งาน หรือรหัสผ่านไม่ถูกต้อง';
}
}
// ส่ง Error กลับไป
if (!empty($error)) {
header('Content-Type: application/json');
echo json_encode(['success' => false, 'message' => $error]);
exit();
}
}
$pageTitle = 'เข้าสู่ระบบ | ระบบจัดการข้อมูลยานพาหนะ โรงพยาบาลเกาะสมุย';
$bodyClass = 'bg-slate-50 dark:bg-slate-900 min-h-screen relative overflow-hidden flex flex-col items-center justify-center';
$bodyAttributes = '';
require_once 'includes/header.php';
?>
<!-- Animated Soft Background -->
<div class="absolute inset-0 z-0 overflow-hidden pointer-events-none">
<!-- Animated Blobs -->
<div class="absolute top-[-10%] left-[-10%] w-[50vw] h-[50vw] rounded-full bg-primary-200/40 dark:bg-primary-900/30 blur-[100px] animate-blob mix-blend-multiply dark:mix-blend-screen"></div>
<div class="absolute top-[20%] right-[-10%] w-[40vw] h-[40vw] rounded-full bg-teal-200/40 dark:bg-teal-900/20 blur-[100px] animate-blob mix-blend-multiply dark:mix-blend-screen" style="animation-delay: 2s;"></div>
<div class="absolute bottom-[-20%] left-[20%] w-[60vw] h-[60vw] rounded-full bg-blue-300/30 dark:bg-blue-900/20 blur-[120px] animate-blob mix-blend-multiply dark:mix-blend-screen" style="animation-delay: 4s;"></div>
</div>
<!-- Main Content -->
<div class="relative z-10 w-full flex flex-col items-center justify-center min-h-screen p-4 sm:p-6 lg:p-8">
<!-- Logo Header -->
<div class="mb-8 text-center page-transition">
<img src="public/images/logo.png" alt="Logo" class="w-[120px] h-[120px] rounded-full object-cover border-4 border-white shadow-xl mx-auto mb-4 hover:scale-105 transition-transform duration-300 bg-white relative z-10">
<h1 class="text-3xl font-bold text-slate-800 dark:text-white tracking-tight drop-shadow-sm">โรงพยาบาลเกาะสมุย</h1>
<p class="text-lg text-slate-600 dark:text-slate-300 font-medium mt-1">ระบบจัดการข้อมูลยานพาหนะ-พนักงานขับรถ</p>
</div>
<!-- Login Card -->
<div class="w-full max-w-md page-transition" style="animation-delay: 0.1s;">
<div class="glass-card p-8 relative overflow-hidden group">
<!-- Inner Glow -->
<div class="absolute inset-0 bg-white/10 dark:bg-black/10 opacity-0 group-hover:opacity-100 transition-opacity duration-500 pointer-events-none"></div>
<h2 class="text-2xl font-semibold mb-6 text-center dark:text-white">เข้าสู่ระบบ / Login</h2>
<!-- Alpine Component สำหรับระบบ Login แบบไม่โหลดหน้าเว็บใหม่ -->
<form class="space-y-6"
x-data="{
isLoading: false,
isSuccess: false,
errorMsg: '',
submitForm() {
this.isLoading = true;
this.errorMsg = '';
let formData = new FormData(this.$refs.form);
fetch('login.php', {
method: 'POST',
body: formData
})
.then(res => res.json())
.then(data => {
if(data.success) {
this.isSuccess = true;
setTimeout(() => window.location.href='index.php', 1000);
} else {
this.isLoading = false;
this.errorMsg = data.message;
// สั่นกล่องข้อความเมื่อมี Error
this.$refs.errorBox.classList.add('animate-shake');
setTimeout(() => this.$refs.errorBox.classList.remove('animate-shake'), 500);
}
})
.catch(err => {
this.isLoading = false;
this.errorMsg = 'เกิดข้อผิดพลาดในการเชื่อมต่อเซิร์ฟเวอร์';
});
}
}"
x-ref="form"
@submit.prevent="submitForm">
<!-- Error Message Box -->
<div x-show="errorMsg" x-transition.opacity x-ref="errorBox" class="bg-danger/10 border border-danger/30 text-danger text-sm rounded-lg p-3 text-center mb-4 font-medium" style="display: none;">
<i class="fa-solid fa-triangle-exclamation mr-1"></i> <span x-text="errorMsg"></span>
</div>
<!-- Username -->
<div class="relative z-0 w-full mb-6 group">
<input type="text" name="username" id="username" class="glass-input block py-3 px-0 w-full text-sm text-gray-900 bg-transparent border-0 border-b-2 border-gray-300 appearance-none dark:text-white dark:border-gray-600 dark:focus:border-primary-500 focus:outline-none focus:ring-0 focus:border-primary-600 peer pl-8" placeholder=" " required />
<label for="username" class="peer-focus:font-medium absolute text-sm text-gray-500 dark:text-gray-400 duration-300 transform -translate-y-6 scale-75 top-3 -z-10 origin-[0] peer-focus:start-0 rtl:peer-focus:translate-x-1/4 rtl:peer-focus:left-auto peer-focus:text-primary-600 peer-focus:dark:text-primary-500 peer-placeholder-shown:scale-100 peer-placeholder-shown:translate-y-0 peer-focus:scale-75 peer-focus:-translate-y-6 pl-8">เลขบัตรประชาชน / ชื่อผู้ใช้งาน</label>
<div class="absolute inset-y-0 left-0 flex items-center pl-2 pointer-events-none">
<i class="fa-regular fa-user text-gray-400 peer-focus:text-primary-600 transition-colors"></i>
</div>
</div>
<!-- Password -->
<div class="relative z-0 w-full mb-6 group">
<input type="password" name="password" id="password" class="glass-input block py-3 px-0 w-full text-sm text-gray-900 bg-transparent border-0 border-b-2 border-gray-300 appearance-none dark:text-white dark:border-gray-600 dark:focus:border-primary-500 focus:outline-none focus:ring-0 focus:border-primary-600 peer pl-8" placeholder=" " required />
<label for="password" class="peer-focus:font-medium absolute text-sm text-gray-500 dark:text-gray-400 duration-300 transform -translate-y-6 scale-75 top-3 -z-10 origin-[0] peer-focus:start-0 rtl:peer-focus:translate-x-1/4 peer-focus:text-primary-600 peer-focus:dark:text-primary-500 peer-placeholder-shown:scale-100 peer-placeholder-shown:translate-y-0 peer-focus:scale-75 peer-focus:-translate-y-6 pl-8">รหัสผ่าน</label>
<div class="absolute inset-y-0 left-0 flex items-center pl-2 pointer-events-none">
<i class="fa-solid fa-lock text-gray-400 peer-focus:text-primary-600 transition-colors"></i>
</div>
</div>
<div class="flex items-center justify-between">
<div class="flex items-start">
<div class="flex items-center h-5">
<input id="remember" type="checkbox" value="" class="w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-primary-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-primary-600 dark:ring-offset-gray-800 dark:focus:ring-offset-gray-800" />
</div>
<label for="remember" class="ms-2 text-sm font-medium text-gray-900 dark:text-gray-300 cursor-pointer">จดจำฉัน</label>
</div>
<a href="#" class="text-sm text-primary-600 hover:underline dark:text-primary-500">ลืมรหัสผ่าน?</a>
</div>
<button type="submit" :disabled="isLoading || isSuccess" class="btn-gradient w-full py-3 flex items-center justify-center space-x-2 relative overflow-hidden"
:class="{ 'opacity-80 cursor-not-allowed': isLoading || isSuccess }">
<span x-show="!isLoading && !isSuccess">เข้าสู่ระบบ</span>
<span x-show="isLoading" class="flex items-center" style="display: none;">
<svg class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
กำลังตรวจสอบ...
</span>
<span x-show="isSuccess" class="flex items-center text-white" x-transition:enter="transition ease-out duration-300" x-transition:enter-start="opacity-0 scale-50" x-transition:enter-end="opacity-100 scale-100" style="display: none;">
<i class="fa-solid fa-check-circle mr-2"></i> เข้าสู่ระบบสำเร็จ
</span>
</button>
</form>
</div>
<div class="mt-6 text-center text-sm text-slate-300">
<p>&copy; 2026 Koh Samui Hospital. All rights reserved.</p>
<p class="mt-1 opacity-70">Version 1.0.0 (Production)</p>
</div>
</div>
</div>
<!-- Add animation for error shake -->
<style>
@keyframes shake {
0%, 100% { transform: translateX(0); }
10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }
20%, 40%, 60%, 80% { transform: translateX(5px); }
}
.animate-shake {
animation: shake 0.5s;
}
</style>
<?php require_once 'includes/footer.php'; ?>