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
@@ -0,0 +1,334 @@
<?php
require_once 'includes/auth.php';
require_once 'includes/db.php';
$pageTitle = 'ตรวจสภาพรถยนต์ | ระบบจัดการข้อมูลยานพาหนะ โรงพยาบาลเกาะสมุย';
$pageTitleDisplay = 'ตรวจสภาพรถยนต์ประจำวัน';
$breadcrumbs = ['ตรวจสภาพรถยนต์'];
require_once 'includes/header.php';
// ดึงข้อมูลรถยนต์สำหรับการเลือก
$stmt = $pdo->query("SELECT id, license_plate, type, brand, model FROM vehicles ORDER BY license_plate ASC");
$vehicles = $stmt->fetchAll();
$defaultCid = isset($_GET['cid']) ? $_GET['cid'] : '1840400002613';
?>
<!-- Floating Background -->
<div class="fixed top-0 left-0 w-full h-full overflow-hidden -z-10 pointer-events-none">
<div class="absolute top-[-10%] left-[-10%] w-[40%] h-[40%] bg-primary-500/10 rounded-full blur-[100px]"></div>
<div class="absolute bottom-[-10%] right-[-10%] w-[40%] h-[40%] bg-secondary/10 rounded-full blur-[100px]"></div>
</div>
<!-- Layout Wrapper -->
<div class="flex h-screen overflow-hidden" x-data="inspectionForm()">
<!-- Sidebar -->
<?php include 'includes/sidebar.php'; ?>
<!-- Main Content -->
<main class="flex-1 flex flex-col min-w-0 overflow-hidden relative z-10">
<!-- Top Navbar -->
<?php include 'includes/navbar.php'; ?>
<!-- Dashboard Content -->
<div class="flex-1 overflow-y-auto p-4 md:p-6 custom-scrollbar page-transition">
<div class="max-w-4xl mx-auto glass-card p-6 md:p-8">
<!-- Header part -->
<div class="text-center mb-8 border-b border-slate-200 dark:border-slate-700 pb-6">
<h1 class="text-2xl md:text-3xl font-bold dark:text-white mb-2">แบบรายงานตรวจสภาพรถยนต์ราชการ</h1>
<p class="text-lg text-slate-700 dark:text-slate-300">หน่วยงานยานพาหนะ โรงพยาบาลเกาะสมุย กระทรวงสาธารณสุข</p>
<p class="text-md text-primary-600 dark:text-primary-400 font-medium italic mt-2">"บริการมีน้ำใจ ปลอดภัย ทันเวลา"</p>
</div>
<form @submit.prevent="submitForm" class="space-y-6">
<!-- General Info Section -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 p-5 bg-slate-50/50 dark:bg-slate-800/50 rounded-xl border border-slate-200 dark:border-slate-700">
<div>
<label class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1">เลขประจำตัวประชาชน (ผู้ตรวจ) <span class="text-danger">*</span></label>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<i class="fa-solid fa-id-card text-slate-400"></i>
</div>
<input type="text" x-model="form.inspector_cid" required class="glass-input block w-full pl-10 p-2.5 text-sm font-semibold text-primary-700" placeholder="เลขบัตรประชาชน 13 หลัก">
</div>
</div>
<div>
<label class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1">รถยนต์หมายเลขทะเบียน <span class="text-danger">*</span></label>
<select x-model="form.vehicle_id" required class="glass-input block w-full p-2.5 text-sm">
<option value="">-- เลือกรถยนต์ --</option>
<?php foreach($vehicles as $v): ?>
<option value="<?= $v['id'] ?>"><?= htmlspecialchars($v['license_plate'] . ' (' . $v['brand'] . ' ' . $v['model'] . ')') ?></option>
<?php endforeach; ?>
</select>
</div>
<div x-init="initDatepicker($refs.dateInput)">
<label class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1">ประจำวันที่ <span class="text-danger">*</span></label>
<input type="text" x-ref="dateInput" required class="glass-input block w-full p-2.5 text-sm" placeholder="เลือกวันที่">
</div>
<div>
<label class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1">เวลา (น.) <span class="text-danger">*</span></label>
<input type="time" x-model="form.inspection_time" required class="glass-input block w-full p-2.5 text-sm">
</div>
</div>
<!-- Inspection Table -->
<div class="overflow-x-auto border border-slate-200 dark:border-slate-700 rounded-xl shadow-sm">
<table class="w-full text-sm text-left text-slate-600 dark:text-slate-400">
<thead class="text-xs text-slate-700 uppercase bg-slate-100 dark:bg-slate-800 dark:text-slate-300">
<tr>
<th scope="col" class="sticky left-0 z-20 bg-slate-100 dark:bg-slate-800 px-3 py-3 text-center whitespace-nowrap border-r border-slate-200 dark:border-slate-700 w-[60px] min-w-[60px] shadow-[1px_0_0_0_#e2e8f0] dark:shadow-[1px_0_0_0_#334155]">ลำดับที่</th>
<th scope="col" class="sticky left-[60px] z-20 bg-slate-100 dark:bg-slate-800 px-4 py-3 text-center whitespace-nowrap border-r border-slate-200 dark:border-slate-700 min-w-[200px] shadow-[1px_0_0_0_#e2e8f0] dark:shadow-[1px_0_0_0_#334155]">รายงานสภาพรถยนต์</th>
<th scope="col" class="px-3 py-3 text-center whitespace-nowrap border-r border-slate-200 dark:border-slate-700 w-20">ปกติ</th>
<th scope="col" class="px-3 py-3 text-center whitespace-nowrap border-r border-slate-200 dark:border-slate-700 w-20">ไม่ปกติ</th>
<th scope="col" class="px-4 py-3 text-center whitespace-nowrap border-r border-slate-200 dark:border-slate-700 min-w-[150px]">ไม่ปกติเนื่องจาก</th>
<th scope="col" class="px-4 py-3 text-center whitespace-nowrap min-w-[150px]">หมายเหตุ</th>
</tr>
</thead>
<tbody>
<template x-for="(item, index) in inspectionItems" :key="index">
<tr class="group bg-white dark:bg-slate-900 border-b border-slate-200 dark:border-slate-700 hover:bg-slate-50 dark:hover:bg-slate-800/50 transition-colors">
<td class="sticky left-0 z-10 bg-white dark:bg-slate-900 group-hover:bg-slate-50 dark:group-hover:bg-slate-800/50 px-3 py-3 text-center border-r border-slate-200 dark:border-slate-700 font-medium shadow-[1px_0_0_0_#e2e8f0] dark:shadow-[1px_0_0_0_#334155]" x-text="index + 1"></td>
<td class="sticky left-[60px] z-10 bg-white dark:bg-slate-900 group-hover:bg-slate-50 dark:group-hover:bg-slate-800/50 px-4 py-3 border-r border-slate-200 dark:border-slate-700 font-medium text-slate-800 dark:text-slate-200 shadow-[1px_0_0_0_#e2e8f0] dark:shadow-[1px_0_0_0_#334155]" x-text="item.name"></td>
<!-- Normal Radio -->
<td class="px-3 py-3 text-center border-r border-slate-200 dark:border-slate-700">
<div class="flex items-center justify-center">
<input type="radio" :name="'status_'+index" value="normal" x-model="inspectionItems[index].status" @change="inspectionItems[index].reason = ''" class="w-5 h-5 cursor-pointer" style="appearance: auto; -webkit-appearance: auto; accent-color: #2563eb;">
</div>
</td>
<!-- Abnormal Radio -->
<td class="px-3 py-3 text-center border-r border-slate-200 dark:border-slate-700">
<div class="flex items-center justify-center">
<input type="radio" :name="'status_'+index" value="abnormal" x-model="inspectionItems[index].status" class="w-5 h-5 cursor-pointer" style="appearance: auto; -webkit-appearance: auto; accent-color: #dc2626;">
</div>
</td>
<!-- Reason -->
<td class="px-3 py-2 border-r border-slate-200 dark:border-slate-700">
<input type="text" x-model="inspectionItems[index].reason" :disabled="inspectionItems[index].status !== 'abnormal'" :required="inspectionItems[index].status === 'abnormal'" class="glass-input block w-full p-1.5 text-xs disabled:opacity-50 disabled:bg-slate-100 dark:disabled:bg-slate-800" placeholder="ระบุสาเหตุ...">
</td>
<!-- Remark -->
<td class="px-3 py-2">
<input type="text" x-model="inspectionItems[index].remark" class="glass-input block w-full p-1.5 text-xs" placeholder="หมายเหตุเพิ่มเติม">
</td>
</tr>
</template>
</tbody>
</table>
</div>
<!-- Additional Details -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-700 rounded-xl p-4 flex flex-col justify-center shadow-sm">
<label class="flex items-center justify-between text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">
<span>12. เลขไมล์ประจำวันก่อนนำรถออกใช้บริการ</span>
<span class="text-xs text-slate-500">กิโลเมตร</span>
</label>
<input type="number" x-model="form.mileage" min="0" step="1" required class="glass-input block w-full p-2.5 text-sm font-medium" placeholder="ระบุเลขไมล์">
</div>
<div class="bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-700 rounded-xl p-4 flex flex-col justify-center shadow-sm">
<label class="flex items-center justify-between text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">
<span>13. น้ำมันเชื้อเพลิงเหลือในถังประจำวันประมาณ</span>
<span class="text-xs text-slate-500">ลิตร</span>
</label>
<input type="number" x-model="form.fuel_level" min="0" step="0.1" required class="glass-input block w-full p-2.5 text-sm font-medium" placeholder="ระบุปริมาณน้ำมัน">
</div>
<div class="md:col-span-2 bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-700 rounded-xl p-4 shadow-sm">
<label class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">14. รายการอื่นๆ</label>
<textarea x-model="form.other_items" rows="3" class="glass-input block w-full p-2.5 text-sm" placeholder="ระบุรายการตรวจเช็คอื่นๆ (ถ้ามี)"></textarea>
</div>
<!-- New Status Field -->
<div class="md:col-span-2 bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-700 rounded-xl p-4 shadow-sm mt-2">
<label class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-3">15. สรุปสถานะรถยนต์หลังการตรวจสอบ <span class="text-danger">*</span></label>
<div class="flex items-center space-x-6">
<label class="flex items-center cursor-pointer">
<input type="radio" x-model="form.vehicle_status" value="available" required class="w-5 h-5 text-success border-slate-300 focus:ring-success dark:border-slate-600 dark:bg-slate-700 accent-success">
<span class="ml-2 text-sm text-slate-700 dark:text-slate-300 font-medium">พร้อมใช้งาน</span>
</label>
<label class="flex items-center cursor-pointer">
<input type="radio" x-model="form.vehicle_status" value="maintenance" required class="w-5 h-5 text-danger border-slate-300 focus:ring-danger dark:border-slate-600 dark:bg-slate-700 accent-danger">
<span class="ml-2 text-sm text-slate-700 dark:text-slate-300 font-medium">ไม่พร้อมใช้งาน / ส่งซ่อม</span>
</label>
</div>
</div>
</div>
<!-- Submit Button -->
<div class="flex justify-end pt-4 border-t border-slate-200 dark:border-slate-700 mt-6">
<button type="button" @click="resetForm" class="px-5 py-2.5 text-sm font-medium text-slate-700 bg-white border border-slate-300 rounded-xl hover:bg-slate-50 mr-3 dark:bg-slate-800 dark:text-slate-300 dark:border-slate-600 dark:hover:bg-slate-700 transition-colors">
ล้างข้อมูล
</button>
<button type="submit" :disabled="isSubmitting" class="btn-gradient px-6 py-2.5 text-sm font-medium flex items-center shadow-lg shadow-primary-500/30 hover:shadow-primary-500/50 transition-all hover:-translate-y-0.5">
<i class="fa-solid fa-save mr-2" x-show="!isSubmitting"></i>
<i class="fa-solid fa-spinner fa-spin mr-2" x-show="isSubmitting" style="display: none;"></i>
<span x-text="isSubmitting ? 'กำลังบันทึก...' : 'บันทึกแบบรายงาน'"></span>
</button>
</div>
</form>
</div>
</div>
</main>
</div>
<!-- Flatpickr CSS & JS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css">
<script src="https://cdn.jsdelivr.net/npm/flatpickr"></script>
<script src="https://cdn.jsdelivr.net/npm/flatpickr/dist/l10n/th.js"></script>
<!-- Alpine.js logic for form -->
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('inspectionForm', () => ({
isSubmitting: false,
form: {
inspector_cid: '<?= htmlspecialchars($defaultCid) ?>',
vehicle_id: '',
inspection_date: new Date().toISOString().split('T')[0],
inspection_time: new Date().toLocaleTimeString('th-TH', { hour: '2-digit', minute: '2-digit' }),
mileage: '',
fuel_level: '',
other_items: '',
vehicle_status: 'available'
},
initDatepicker(element) {
const self = this;
flatpickr(element, {
locale: "th",
defaultDate: this.form.inspection_date,
dateFormat: "Y-m-d",
altInput: true,
altFormat: "j F Y",
formatDate: (date, format, locale) => {
if (format === "Y-m-d") {
const y = date.getFullYear();
const m = String(date.getMonth() + 1).padStart(2, '0');
const d = String(date.getDate()).padStart(2, '0');
return `${y}-${m}-${d}`;
}
// ปรับแสดงผลเป็นปี พ.ศ. เฉพาะใน input ที่ผู้ใช้เห็น (altInput)
const d = date.getDate();
const m = flatpickr.l10ns.th.months.longhand[date.getMonth()];
const y = date.getFullYear() + 543;
return `${d} ${m} ${y}`;
},
onChange: function(selectedDates, dateStr) {
self.form.inspection_date = dateStr; // ค่าที่เก็บในตัวแปรจะเป็น ค.ศ. (Y-m-d)
},
onReady: function(selectedDates, dateStr, instance) {
// เปลี่ยนปีในปฏิทินให้แสดงเป็น พ.ศ.
const updateYear = () => {
if (instance.currentYearElement) {
instance.currentYearElement.value = instance.currentYear + 543;
}
};
updateYear();
instance.config.onMonthChange.push(updateYear);
instance.config.onYearChange.push(updateYear);
}
});
},
inspectionItems: [
{ name: 'ความสะอาดของรถยนต์', status: '', reason: '', remark: '' },
{ name: 'น้ำมันเครื่อง', status: '', reason: '', remark: '' },
{ name: 'น้ำมันครัทช์', status: '', reason: '', remark: '' },
{ name: 'น้ำหม้อน้ำ', status: '', reason: '', remark: '' },
{ name: 'น้ำกลั่นแบตเตอรี่', status: '', reason: '', remark: '' },
{ name: 'น้ำมันเบรค', status: '', reason: '', remark: '' },
{ name: 'ระบบเบรค', status: '', reason: '', remark: '' },
{ name: 'ระบบไฟรถยนต์', status: '', reason: '', remark: '' },
{ name: 'ระบบจ่ายน้ำมันเชื้อเพลิง', status: '', reason: '', remark: '' },
{ name: 'ระบบเครื่องปรับอากาศ (แอร์)', status: '', reason: '', remark: '' },
{ name: 'ยางรถยนต์', status: '', reason: '', remark: '' }
],
resetForm() {
if(confirm('ต้องการล้างข้อมูลในฟอร์มทั้งหมดใช่หรือไม่?')) {
this.form.vehicle_id = '';
this.form.mileage = '';
this.form.fuel_level = '';
this.form.other_items = '';
this.inspectionItems.forEach(item => {
item.status = '';
item.reason = '';
item.remark = '';
});
}
},
async submitForm() {
// Validation checking
for(let i=0; i<this.inspectionItems.length; i++) {
let item = this.inspectionItems[i];
if(!item.status) {
Swal.fire({ icon: 'warning', title: 'ข้อมูลไม่ครบถ้วน', text: `กรุณาระบุสถานะ ปกติ/ไม่ปกติ สำหรับรายการที่ ${i+1} "${item.name}"`, confirmButtonColor: '#3b82f6' });
return;
}
if(item.status === 'abnormal' && !item.reason.trim()) {
Swal.fire({ icon: 'warning', title: 'ข้อมูลไม่ครบถ้วน', text: `กรุณาระบุสาเหตุที่รายการ "${item.name}" ไม่ปกติ`, confirmButtonColor: '#3b82f6' });
return;
}
}
if(!this.form.vehicle_id) {
Swal.fire({ icon: 'warning', title: 'ข้อมูลไม่ครบถ้วน', text: 'กรุณาเลือกรถยนต์ที่ต้องการตรวจเช็ค', confirmButtonColor: '#3b82f6' });
return;
}
this.isSubmitting = true;
// เตรียมข้อมูลเพื่อส่งไปยัง API
const payload = {
...this.form,
items: this.inspectionItems
};
try {
// API endpoint for saving
const response = await fetch('api/inspection_save.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
const result = await response.json();
if(result.success) {
Swal.fire({
icon: 'success',
title: 'บันทึกสำเร็จ',
text: 'บันทึกแบบรายงานตรวจสภาพรถยนต์เรียบร้อยแล้ว',
timer: 2000,
showConfirmButton: false
}).then(() => {
window.location.href = 'vehicles.php';
});
} else {
Swal.fire({ icon: 'error', title: 'เกิดข้อผิดพลาด', text: result.message || 'ไม่สามารถบันทึกข้อมูลได้', confirmButtonColor: '#ef4444' });
}
} catch (error) {
Swal.fire({ icon: 'error', title: 'เกิดข้อผิดพลาด', text: 'ไม่สามารถเชื่อมต่อเซิร์ฟเวอร์ได้ หรือเกิดข้อผิดพลาดในการบันทึกข้อมูล', confirmButtonColor: '#ef4444' });
console.error(error);
} finally {
this.isSubmitting = false;
}
}
}));
});
</script>
<?php require_once 'includes/footer.php'; ?>