diff --git a/app.js b/app.js index 21a60fbf8668a3ba44b651d4540dd4097be54d61..281d4b0de2d6c5507225085e4bafa7289235be18 100644 --- a/app.js +++ b/app.js @@ -1,4 +1,3 @@ -// app.js (ไฟล์หลัก) require('dotenv').config(); const express = require('express'); const session = require('express-session'); @@ -7,37 +6,28 @@ const cors = require("cors"); const bodyParser = require('body-parser'); const db = require('./db'); const authRoutes = require('./routes/auth'); -const activityRoutes = require("./routes/activityRoutes"); +const postRoutes = require('./routes/posts'); // เส้นทางสำหรับบทความ +const settingsRoutes = require('./routes/settings'); const app = express(); -// ทดสอบการเชื่อมต่อฐานข้อมูล -async function testConnection() { - try { - const connection = await db.getConnection(); - await connection.ping(); - console.log('✅ Database connection succeeded.'); - connection.release(); - } catch (err) { - console.error('❌ Database connection failed:', err); - process.exit(1); - } -} -testConnection(); - // Middleware app.use(cors()); app.use(express.json()); app.use(express.urlencoded({ extended: true })); app.use(bodyParser.json()); -// การตั้งค่า Session + +// ตั้งค่า Session app.use(session({ secret: process.env.SESSION_SECRET || 'beaglelover', resave: false, saveUninitialized: false })); - +app.use((req, res, next) => { + res.locals.user = req.session.user; + next(); +}); // เสิร์ฟไฟล์สแตติก app.use(express.static(path.join(__dirname, 'public'))); @@ -45,52 +35,41 @@ app.use(express.static(path.join(__dirname, 'public'))); app.set('view engine', 'ejs'); app.set('views', path.join(__dirname, 'views')); -// ใช้เส้นทางจาก authRoutes -app.use((req, res, next) => { - req.user = req.session.user; - res.locals.user = req.session.user; - next(); -}); - -// ใช้งาน route app.use('/auth', authRoutes); -app.use("/activities", activityRoutes); +app.use('/settings', settingsRoutes); +app.use('/posts', postRoutes); + +// Middleware ตรวจสอบการล็อกอิน // ---------------------- เส้นทางหลัก ---------------------- +// หน้า Login app.get('/', (req, res) => { res.render('auth/login', { error: null }); }); -app.get('/register', (req, res) => { - res.render('auth/register', { error: null }); -}); - +// หน้า Index (เฉพาะผู้ที่ล็อกอิน) +// ตัวอย่างใน app.js app.get('/index', async (req, res) => { if (!req.session.user) return res.redirect('/'); try { - const [activities] = await db.query('SELECT * FROM activity'); - res.render('pages/index', { activities, user: req.session.user }); + // ดึง posts จากฐานข้อมูล (หากต้องการ) + const [posts] = await db.query( + `SELECT p.*, u.username, c.name AS category_name + FROM posts p + JOIN users u ON p.user_id = u.id + JOIN categories c ON p.category_id = c.id + ORDER BY p.created_at DESC` + ); + res.render('pages/index', { posts, user: req.session.user }); } catch (error) { - console.error('เกิดข้อผิดพลาดในการดึงข้อมูล:', error); - res.status(500).send('เกิดข้อผิดพลาดในการดึงข้อมูล'); + console.error('Error fetching posts:', error); + res.render('pages/index', { posts: [], user: req.session.user }); } }); -// ตรวจสอบการล็อกอินสำหรับเส้นทางที่ต้องการ -const requireAuth = (req, res, next) => { - if (!req.session.user) return res.redirect('/'); - next(); -}; - -const protectedRoutes = ['/booking', '/chat', '/dashboard', '/settings']; -protectedRoutes.forEach(route => { - app.get(route, requireAuth, (req, res) => { - res.render(`pages${route}`, { user: req.session.user }); - }); -}); // เริ่มต้นเซิร์ฟเวอร์ const port = process.env.PORT || 3000; app.listen(port, () => { - console.log(`🚀 เซิร์ฟเวอร์ทำงานที่พอร์ต ${port}`); -}); \ No newline at end of file + console.log(`Server is running on port ${port}`); +}); diff --git a/public/css/auth/auth.css b/public/css/auth/auth.css index 5660e6853f859979c95e8b3536374427d9ea2a92..a5e1342fc87eafeeb54e794fa997c8aa8949b9b3 100644 --- a/public/css/auth/auth.css +++ b/public/css/auth/auth.css @@ -35,10 +35,9 @@ } .logo img { - width: 80px; - height: 80px; - object-fit: contain; - filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.1)); + width: 280px; + height: 150px; + object-fit: cover; } h1 { diff --git a/public/css/chatAI.css b/public/css/chatAI.css deleted file mode 100644 index 51ba0bd118134acb96a090187eaf61709dc54e88..0000000000000000000000000000000000000000 --- a/public/css/chatAI.css +++ /dev/null @@ -1,23 +0,0 @@ -.main-content { - margin-left: 220px; /* เว้นช่องซ้ายเท่ากับ sidebar */ - padding: 20px; -} - -#chatBox { - height: 400px; - border: 1px solid #ccc; - padding: 20px; - margin: 20px 0; - overflow-y: auto; -} -.message { - margin: 10px 0; - padding: 8px; - border-radius: 5px; -} -.user-message { - background-color: #e3f2fd; -} -.bot-message { - background-color: #f5f5f5; -} \ No newline at end of file diff --git a/public/css/main.css b/public/css/main.css index 91985ea9a41ead808cc943a8d1605c626c8b1953..41cc0e3864301455a05ef7652b06118bb43e8deb 100644 --- a/public/css/main.css +++ b/public/css/main.css @@ -1,5 +1,128 @@ +/* Main content layout */ .main-content { - margin-left: 220px; /* เว้นช่องซ้ายเท่ากับ sidebar */ - padding: 20px; + margin-left: 280px; + padding: 2rem; + min-height: 100vh; + background-color: #f9fafb; } +/* Header section */ +.main-content h1 { + font-weight: 700; + color: #0d6efd; + letter-spacing: -0.02em; +} + +.badge { + font-size: 0.875rem; + padding: 0.5rem 1rem; +} + +/* Post Cards */ +.post-list { + display: grid; + gap: 1.5rem; +} + +.post-card { + border: none; + border-radius: 0.75rem; + transition: transform 0.2s, box-shadow 0.2s; + overflow: hidden; + background-color: #fff; +} + +.post-card:hover { + transform: translateY(-5px); + box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1) !important; +} + +.card-body { + padding: 1.75rem; +} + +.card-title { + font-size: 1.5rem; + font-weight: 600; + line-height: 1.3; +} + +.card-title a { + transition: color 0.2s; +} + +.card-title a:hover { + color: #0d6efd !important; +} + +.card-text { + color: #6c757d; + line-height: 1.6; + font-size: 1rem; +} + +/* Post meta info */ +.post-meta { + font-size: 0.875rem; + color: #6c757d; + border-top: 1px solid #eee; + padding-top: 1rem; + margin-top: 0.5rem; + flex-wrap: wrap; +} + +.post-meta i { + font-size: 1rem; + margin-right: 0.375rem; + opacity: 0.8; +} + +/* Alert for no posts */ +.alert { + border-radius: 0.75rem; + padding: 1.25rem; + font-size: 1rem; +} + +.alert i { + font-size: 1.25rem; +} + +/* Responsive adjustments */ +@media (max-width: 992px) { + .post-list { + grid-template-columns: repeat(2, 1fr); + } +} + +@media (max-width: 768px) { + .main-content { + margin-left: 0; + padding: 1.5rem; + } + + .post-list { + grid-template-columns: 1fr; + } + + .post-meta { + flex-direction: column; + gap: 0.5rem !important; + } +} + +@media (min-width: 993px) { + .post-list { + grid-template-columns: repeat(3, 1fr); + } +} + +/* Thai Font Support */ +body { + font-family: 'Sarabun', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; +} + +/* Smooth transitions */ +a, button, .card, .badge { + transition: all 0.3s ease; +} \ No newline at end of file diff --git a/public/css/pages/index.css b/public/css/pages/index.css index c5ca9f84ea77d04d4bbccf388fd9e3f43515ac4a..925f018b361f332fe06651265cc14443698c2d40 100644 --- a/public/css/pages/index.css +++ b/public/css/pages/index.css @@ -1,38 +1,65 @@ -/* index.css */ +/* ปรับโครงสร้างหลัก */ +.main-content { + padding: 2rem; + transition: margin 0.3s; +} -/* กำหนดสไตล์สำหรับ container หลัก */ -.activity-content { - max-width: 80000px; - margin: 0 auto; - padding: 20px; +@media (max-width: 768px) { + .main-content { + margin-left: 0 !important; + padding: 1rem; } - - /* ลบจุดหน้ารายการใน ul */ - .activities-list ul { - list-style: none; - margin: 0; - padding: 0; - } - - /* สไตล์สำหรับแต่ละกิจกรรมให้มีแถบแยกของตัวเอง */ - .activity-item { - border: 1px solid #ccc; /* เส้นขอบสีเทาอ่อน */ - border-radius: 5px; /* มุมโค้งมน */ - padding: 15px; - margin-bottom: 20px; /* ระยะห่างระหว่างกิจกรรมแต่ละรายการ */ - background-color: #f9f9f9; /* สีพื้นหลังอ่อน */ - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); /* เงาเพื่อความลึก */ +} + +/* การ์ดโพสต์ */ +.post-card { + border: none; + border-radius: 12px; + transition: transform 0.2s, box-shadow 0.2s; +} + +.post-card:hover { + transform: translateY(-3px); + box-shadow: 0 8px 20px rgba(0,0,0,0.1); +} + +.card-title { + font-size: 1.5rem; + font-weight: 600; + color: #2c3e50; +} + +.card-title a:hover { + color: #0d6efd !important; +} + +.card-text { + font-size: 1rem; + line-height: 1.7; +} + +/* เมตาดาต้าโพสต์ */ +.post-meta { + font-size: 0.9rem; + color: #6c757d; +} + +.post-meta i { + font-size: 1rem; + color: #0d6efd; +} + +@media (max-width: 576px) { + .post-meta { + flex-wrap: wrap; + gap: 1rem; } - /* สไตล์สำหรับหัวข้อของกิจกรรม */ - .activity-item h3 { - margin-top: 0; - color: #333; + .card-title { + font-size: 1.3rem; } - /* สไตล์สำหรับข้อความรายละเอียด */ - .activity-item p { - margin: 5px 0; - line-height: 1.5; + .card-text { + font-size: 0.95rem; } - \ No newline at end of file +} \ No newline at end of file diff --git a/public/css/partials/header.css b/public/css/partials/header.css index b6e49cf4be602116ace72c32d7c9cf77151a297a..da3561671e537452682377b51767bb67395714c0 100644 --- a/public/css/partials/header.css +++ b/public/css/partials/header.css @@ -1,74 +1,145 @@ +/* Sidebar Base Styles */ .sidebar { - width: 220px; - height: 100vh; - background: linear-gradient(180deg, #f8f9fa 0%, #e9ecef 100%); - position: fixed; - top: 0; - left: 0; - z-index: 1000; - box-shadow: 3px 0 15px rgba(0, 0, 0, 0.05); - padding: 1.5rem; - display: flex; - flex-direction: column; - gap: 1.5rem; + width: 280px; + height: 100vh; + position: fixed; + left: 0; + top: 0; + background-color: #f8f9fa; + border-right: 1px solid #dee2e6; + padding: 1.5rem; + overflow-y: auto; + transition: all 0.3s; + z-index: 1000; +} + +/* Sidebar Header */ +.sidebar-header { + margin-bottom: 2rem; } .sidebar-header h2 { - font-weight: 700; - color: #001d3d; - margin-bottom: 0.5rem; - font-size: 1.5rem; + font-size: 1.75rem; + font-weight: 600; + color: #0d6efd; + margin-bottom: 1rem; } .user-info { - background: rgba(0, 56, 92, 0.05); - padding: 1rem; - border-radius: 0.5rem; - margin-bottom: 1rem; + background-color: #e9ecef; + padding: 0.75rem; + border-radius: 0.5rem; + margin-bottom: 1rem; +} + +/* Search Form */ +.sidebar-header form { + display: flex; + margin-top: 1rem; +} + +.sidebar-header input { + flex: 1; + padding: 0.5rem 0.75rem; + border: 1px solid #ced4da; + border-radius: 0.25rem 0 0 0.25rem; + font-size: 0.875rem; +} + +.sidebar-header button { + background-color: #0d6efd; + color: white; + border: none; + padding: 0.5rem 0.75rem; + border-radius: 0 0.25rem 0.25rem 0; + font-size: 0.875rem; +} + +.sidebar-header button:hover { + background-color: #0b5ed7; +} + +/* Navigation Links */ +.sidebar-nav { + margin-bottom: 2rem; } -.user-info p { - margin-bottom: 0.3rem; +.nav-item { + margin-bottom: 0.5rem; } -.sidebar-nav .nav-link { - color: #495057; - font-weight: 500; - padding: 0.75rem 1rem; - border-radius: 0.5rem; - display: flex; - align-items: center; - gap: 0.75rem; - transition: all 0.2s ease; +.nav-link { + display: flex; + align-items: center; + padding: 0.75rem 1rem; + color: #495057; + border-radius: 0.5rem; + transition: all 0.2s; } -.sidebar-nav .nav-link:hover { - background-color: #e2e6ea; - color: #001d3d; - transform: translateX(5px); +.nav-link:hover { + background-color: #e9ecef; + color: #0d6efd; } -.sidebar-nav .nav-link.active { - background: #004b92; - color: white !important; - box-shadow: 0 4px 6px rgba(0, 75, 146, 0.15); +.nav-link.active { + background-color: #0d6efd; + color: white; } -.sidebar-nav .nav-link i { - width: 24px; - text-align: center; +.nav-link i { + margin-right: 0.75rem; + font-size: 1.25rem; } +/* Logout Button */ .logout-btn { - margin-top: auto; - padding: 1rem; - border-top: 1px solid #dee2e6; + margin-top: 2rem; } .logout-btn .btn { - width: 100%; - display: flex; - align-items: center; - gap: 0.5rem; - justify-content: center; + display: flex; + align-items: center; + justify-content: center; + width: 100%; + padding: 0.75rem; +} + +.logout-btn i { + margin-right: 0.5rem; +} + +/* Thai Font Support */ +body { + font-family: 'Sarabun', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; +} + +/* Responsive adjustments */ +@media (max-width: 768px) { + .sidebar { + width: 240px; + transform: translateX(-100%); + } + + .sidebar.show { + transform: translateX(0); + } + + .content { + margin-left: 0; + } +} + +/* Light scrollbar for the sidebar */ +.sidebar::-webkit-scrollbar { + width: 6px; +} + +.sidebar::-webkit-scrollbar-track { + background: #f8f9fa; +} + +.sidebar::-webkit-scrollbar-thumb { + background-color: #adb5bd; + border-radius: 3px; } \ No newline at end of file diff --git a/public/images/logo.png b/public/images/logo.png index 5182c101c6fe73a7c290d53be0d7404f680ab71f..6134ef27b55cade5494c27b95a81bd23adbce99c 100644 Binary files a/public/images/logo.png and b/public/images/logo.png differ diff --git a/routes/activityRoutes.js b/routes/activityRoutes.js deleted file mode 100644 index 09acf0d59b28730731a3e4ba29782c8662dbbbdf..0000000000000000000000000000000000000000 --- a/routes/activityRoutes.js +++ /dev/null @@ -1,83 +0,0 @@ -const express = require("express"); -const router = express.Router(); -const db = require('../db'); - -// 🟢 [เพิ่มกิจกรรม] GET ฟอร์มเพิ่มกิจกรรม -router.get("/new", (req, res) => { - if (!req.session.user || req.session.user.role !== "admin") { - return res.status(403).send("คุณไม่มีสิทธิ์เข้าถึงหน้านี้"); - } - res.render("activities/new"); // แสดงฟอร์ม -}); - -// 🟢 [เพิ่มกิจกรรม] POST บันทึกข้อมูล -router.post("/new", (req, res) => { - if (!req.user || req.user.role !== "admin") { - return res.status(403).send("คุณไม่มีสิทธิ์เพิ่มกิจกรรม"); - } - - const { act_name, detail, start_date, end_date, start_time, end_time, place_id } = req.body; - - const sql = `INSERT INTO activity (act_name, detail, start_date, end_date, start_time, end_time, place_id) - VALUES (?, ?, ?, ?, ?, ?, ?)`; - db.query(sql, [act_name, detail, start_date, end_date, start_time, end_time, place_id], (err, result) => { - if (err) { - console.error(err); - return res.status(500).send("เกิดข้อผิดพลาด"); - } - res.redirect("/"); - }); -}); - -// 🟡 [แก้ไขกิจกรรม] GET ฟอร์มแก้ไข -router.get("/edit/:id", (req, res) => { - if (!req.user || req.user.role !== "admin") { - return res.status(403).send("คุณไม่มีสิทธิ์แก้ไขกิจกรรม"); - } - - const activity_id = req.params.id; - db.query("SELECT * FROM activity WHERE activity_id = ?", [activity_id], (err, results) => { - if (err || results.length === 0) { - return res.status(404).send("ไม่พบกิจกรรม"); - } - res.render("activities/edit", { activity: results[0] }); - }); -}); - -// 🟡 [แก้ไขกิจกรรม] POST อัปเดตข้อมูล -router.post("/edit/:id", (req, res) => { - if (!req.user || req.user.role !== "admin") { - return res.status(403).send("คุณไม่มีสิทธิ์แก้ไขกิจกรรม"); - } - - const activity_id = req.params.id; - const { act_name, detail, start_date, end_date, start_time, end_time, place_id } = req.body; - - const sql = `UPDATE activity SET act_name = ?, detail = ?, start_date = ?, end_date = ?, start_time = ?, end_time = ?, place_id = ? - WHERE activity_id = ?`; - db.query(sql, [act_name, detail, start_date, end_date, start_time, end_time, place_id, activity_id], (err, result) => { - if (err) { - console.error(err); - return res.status(500).send("เกิดข้อผิดพลาดในการอัปเดตข้อมูล"); - } - res.redirect("/"); - }); -}); - -// 🔴 [ลบกิจกรรม] -router.post("/delete/:id", (req, res) => { - if (!req.user || req.user.role !== "admin") { - return res.status(403).send("คุณไม่มีสิทธิ์ลบกิจกรรม"); - } - - const activity_id = req.params.id; - db.query("DELETE FROM activity WHERE activity_id = ?", [activity_id], (err, result) => { - if (err) { - console.error(err); - return res.status(500).send("เกิดข้อผิดพลาดในการลบ"); - } - res.redirect("/"); - }); -}); - -module.exports = router; diff --git a/routes/auth.js b/routes/auth.js index a4fd139415f6d6fa319818c2add69968692af409..bbb7115ee42249777704f918f821d0f36a644fa4 100644 --- a/routes/auth.js +++ b/routes/auth.js @@ -36,15 +36,15 @@ router.get('/register', (req, res) => { }); router.post('/register', async (req, res) => { - const { username, email, password, phone, f_name, l_name, club_id } = req.body; + const { username, email, password, phone, f_name, l_name, } = req.body; try { const hashedPassword = await bcrypt.hash(password, 10); // เปลี่ยนจาก `user` เป็น `users` await db.query( `INSERT INTO users - (username, email, password, phone, f_name, l_name, club_id, role) - VALUES (?, ?, ?, ?, ?, ?, ?, 'member')`, - [username, email, hashedPassword, phone, f_name, l_name, club_id || null] + (username, email, password, phone, f_name, l_name, role) + VALUES (?, ?, ?, ?, ?, ?,'member')`, + [username, email, hashedPassword, phone, f_name, l_name] ); res.redirect('/auth/login'); } catch (error) { diff --git a/routes/posts.js b/routes/posts.js new file mode 100644 index 0000000000000000000000000000000000000000..9286dd0611a8306ef0731731f7218ad0693067b2 --- /dev/null +++ b/routes/posts.js @@ -0,0 +1,146 @@ +const express = require('express'); +const router = express.Router(); +const db = require('../db'); + +// ตรวจสอบว่าผู้ใช้ล็อกอินหรือไม่ (middleware) +function isAuthenticated(req, res, next) { + if (req.session.user) return next(); + res.redirect('/'); +} + +// แสดงรายการบทความทั้งหมด (แสดงข้อมูลที่มีความสัมพันธ์ระหว่าง posts, users และ categories) +// แสดงรายการโพสต์ของผู้ใช้ที่ล็อกอินอยู่เท่านั้น +router.get('/', isAuthenticated, async (req, res) => { + try { + const [posts] = await db.query( + `SELECT p.*, u.username, c.name AS category_name + FROM posts p + JOIN users u ON p.user_id = u.id + JOIN categories c ON p.category_id = c.id + WHERE p.user_id = ? + ORDER BY p.created_at DESC`, + [req.session.user.id] + ); + res.render('posts/index', { posts }); + } catch (error) { + console.error('Error fetching posts:', error); + res.render('posts/index', { posts: [] }); + } + }); + + +// ฟอร์มเพิ่มบทความใหม่ +router.get('/add', isAuthenticated, async (req, res) => { + try { + // ดึงหมวดหมู่ทั้งหมดเพื่อให้เลือก + const [categories] = await db.query(`SELECT * FROM categories ORDER BY name ASC`); + res.render('posts/add', { categories, error: null }); + } catch (error) { + console.error('Error fetching categories:', error); + res.status(500).send('Internal Server Error'); + } +}); + +// สร้างบทความใหม่ +router.post('/add', isAuthenticated, async (req, res) => { + const { title, content, category_id } = req.body; + try { + await db.query( + 'INSERT INTO posts (title, content, user_id, category_id) VALUES (?, ?, ?, ?)', + [title, content, req.session.user.id, category_id] + ); + res.redirect('/posts'); + } catch (error) { + console.error('Error adding post:', error); + res.render('posts/add', { error: 'เกิดข้อผิดพลาดในการเพิ่มบทความ', categories: [] }); + } +}); + +// ฟอร์มแก้ไขบทความ +router.get('/edit/:id', isAuthenticated, async (req, res) => { + const postId = req.params.id; + try { + const [postResult] = await db.query('SELECT * FROM posts WHERE id = ?', [postId]); + if (postResult.length === 0) { + return res.status(404).send('ไม่พบบทความ'); + } + const post = postResult[0]; + // ตรวจสอบเฉพาะเจ้าของบทความหรือ admin + if (post.user_id !== req.session.user.id && req.session.user.role !== 'admin') { + return res.status(403).send('ไม่อนุญาตให้แก้ไขบทความนี้'); + } + const [categories] = await db.query(`SELECT * FROM categories ORDER BY name ASC`); + res.render('posts/edit', { post, categories, error: null }); + } catch (error) { + console.error('Error fetching post for edit:', error); + res.status(500).send('Internal Server Error'); + } +}); + +// แก้ไขบทความ +router.post('/edit/:id', isAuthenticated, async (req, res) => { + const postId = req.params.id; + const { title, content, category_id } = req.body; + try { + // ตรวจสอบสิทธิ์ก่อนแก้ไข + const [postResult] = await db.query('SELECT * FROM posts WHERE id = ?', [postId]); + if (postResult.length === 0) { + return res.status(404).send('ไม่พบบทความ'); + } + const post = postResult[0]; + if (post.user_id !== req.session.user.id && req.session.user.role !== 'admin') { + return res.status(403).send('ไม่อนุญาตให้แก้ไขบทความนี้'); + } + await db.query( + 'UPDATE posts SET title = ?, content = ?, category_id = ? WHERE id = ?', + [title, content, category_id, postId] + ); + res.redirect('/posts'); + } catch (error) { + console.error('Error updating post:', error); + res.render('posts/edit', { error: 'เกิดข้อผิดพลาดในการแก้ไขบทความ', post: req.body, categories: [] }); + } +}); + +// ลบบทความ +router.post('/delete/:id', isAuthenticated, async (req, res) => { + const postId = req.params.id; + try { + // ตรวจสอบสิทธิ์ก่อนลบ + const [postResult] = await db.query('SELECT * FROM posts WHERE id = ?', [postId]); + if (postResult.length === 0) { + return res.status(404).send('ไม่พบบทความ'); + } + const post = postResult[0]; + if (post.user_id !== req.session.user.id && req.session.user.role !== 'admin') { + return res.status(403).send('ไม่อนุญาตให้ลบบทความนี้'); + } + await db.query('DELETE FROM posts WHERE id = ?', [postId]); + res.redirect('/posts'); + } catch (error) { + console.error('Error deleting post:', error); + res.status(500).send('Internal Server Error'); + } +}); + +// ค้นหาบทความ (ค้นหาจาก title) +router.get('/search', async (req, res) => { + const { keyword } = req.query; + try { + const [posts] = await db.query( + `SELECT p.*, u.username, c.name AS category_name + FROM posts p + JOIN users u ON p.user_id = u.id + JOIN categories c ON p.category_id = c.id + WHERE p.title LIKE ? + ORDER BY p.created_at DESC`, + [`%${keyword}%`] + ); + res.render('posts/index', { posts }); + } catch (error) { + console.error('Error searching posts:', error); + res.status(500).send('Internal Server Error'); + } +}); + +module.exports = router; diff --git a/routes/settings.js b/routes/settings.js new file mode 100644 index 0000000000000000000000000000000000000000..542141c5ebbf0f842bbe115c8e36435bda1e46b6 --- /dev/null +++ b/routes/settings.js @@ -0,0 +1,38 @@ +// routes/settings.js +const express = require('express'); +const router = express.Router(); +const db = require('../db'); + +// Middleware ตรวจสอบการล็อกอิน +function isAuthenticated(req, res, next) { + if (req.session.user) return next(); + res.redirect('/'); +} + +// หน้า Settings +router.get('/', isAuthenticated, (req, res) => { + res.render('settings', { user: req.session.user }); +}); + +// อัปเดต settings โดยรองรับการแก้ไข email ด้วย +router.post('/update', isAuthenticated, async (req, res) => { + const { f_name, l_name, email, phone } = req.body; + try { + // อัปเดตข้อมูลในฐานข้อมูล + await db.query( + 'UPDATE users SET f_name = ?, l_name = ?, email = ?, phone = ? WHERE id = ?', + [f_name, l_name, email, phone, req.session.user.id] + ); + // อัปเดต session ด้วยข้อมูลใหม่ + req.session.user.f_name = f_name; + req.session.user.l_name = l_name; + req.session.user.email = email; + req.session.user.phone = phone; + res.redirect('/settings'); + } catch (error) { + console.error('Error updating settings:', error); + res.render('settings', { user: req.session.user, error: 'เกิดข้อผิดพลาดในการอัปเดต' }); + } +}); + +module.exports = router; diff --git a/views/activities/edit.ejs b/views/activities/edit.ejs deleted file mode 100644 index 8dc468281a656975342d8c23ca64683f7712b994..0000000000000000000000000000000000000000 --- a/views/activities/edit.ejs +++ /dev/null @@ -1,29 +0,0 @@ -<%- include('../partials/header') %> -<link rel="stylesheet" href="/css/main.css"> -<div class="main-content"> -<h2>แก้ไขกิจกรรม</h2> -<form action="/activities/edit/<%= activity.activity_id %>" method="POST"> - <label>ชื่อกิจกรรม:</label> - <input type="text" name="act_name" value="<%= activity.act_name %>" required> - - <label>รายละเอียด:</label> - <textarea name="detail"><%= activity.detail %></textarea> - - <label>วันที่เริ่ม:</label> - <input type="date" name="start_date" value="<%= activity.start_date.toISOString().split('T')[0] %>" required> - - <label>วันที่สิ้นสุด:</label> - <input type="date" name="end_date" value="<%= activity.end_date.toISOString().split('T')[0] %>" required> - - <label>เวลาเริ่ม:</label> - <input type="time" name="start_time" value="<%= activity.start_time %>" required> - - <label>เวลาสิ้นสุด:</label> - <input type="time" name="end_time" value="<%= activity.end_time %>" required> - - <label>สถานที่:</label> - <input type="number" name="place_id" value="<%= activity.place_id %>"> - - <button type="submit">บันทึก</button> -</form> -</div> \ No newline at end of file diff --git a/views/activities/new.ejs b/views/activities/new.ejs deleted file mode 100644 index 7987ace40785fdc68fe25b391b30d1086fd4b254..0000000000000000000000000000000000000000 --- a/views/activities/new.ejs +++ /dev/null @@ -1,29 +0,0 @@ -<%- include('../partials/header') %> -<link rel="stylesheet" href="/css/main.css"> -<div class="main-content"> -<h2>เพิ่มกิจกรรมใหม่</h2> -<form action="/activities/new" method="POST"> - <label>ชื่อกิจกรรม:</label> - <input type="text" name="act_name" required> - - <label>รายละเอียด:</label> - <textarea name="detail"></textarea> - - <label>วันที่เริ่ม:</label> - <input type="date" name="start_date" required> - - <label>วันที่สิ้นสุด:</label> - <input type="date" name="end_date" required> - - <label>เวลาเริ่ม:</label> - <input type="time" name="start_time" required> - - <label>เวลาสิ้นสุด:</label> - <input type="time" name="end_time" required> - - <label>สถานที่:</label> - <input type="number" name="place_id"> - - <button type="submit">บันทึก</button> -</form> -</div> \ No newline at end of file diff --git a/views/auth/login.ejs b/views/auth/login.ejs index 682837c6ee6cc056053b4d210e1dda86dba19188..4d48f32cc6107938388053ab9de10602b9854be6 100644 --- a/views/auth/login.ejs +++ b/views/auth/login.ejs @@ -11,7 +11,6 @@ <div class="logo"> <img src="/images/logo.png" alt="โลโก้เว็บจัดการชมรม"> </div> - <h1>เว็บจัดการชมรม</h1> <h2>ลงชื่อเข้าใช้</h2> <% if (error) { %> diff --git a/views/auth/register.ejs b/views/auth/register.ejs index 74cd6c0a7621e26faed652bcab36b07bf311cd5e..4ce9a2546caff48c98011cad5bcfd4ef7ac0b87d 100644 --- a/views/auth/register.ejs +++ b/views/auth/register.ejs @@ -11,20 +11,15 @@ <div class="logo"> <img src="/images/logo.png" alt="โลโก้เว็บจัดการชมรม"> </div> - <h1>เว็บจัดการชมรม</h1> - <h2>Register</h2> + <h2>สมัครสมาชิก</h2> <% if (error) { %> <div class="error-message"><%= error %></div> <% } %> <form action="/auth/register" method="POST" onsubmit="return validatePasswords()"> - <input type="text" name="username" placeholder="Username" required> <input type="email" name="email" placeholder="Email" required> - <input type="text" name="f_name" placeholder="First Name" required> - <input type="text" name="l_name" placeholder="Last Name" required> - <input type="text" name="phone" placeholder="Phone" required> - + <input type="text" name="username" placeholder="Username" required> <div class="password-container"> <input type="password" id="password" name="password" placeholder="Password" required> <span id="togglePassword" class="toggle-password">Show</span> @@ -33,8 +28,10 @@ <input type="password" id="confirm_password" name="confirm_password" placeholder="Confirm Password" required> <span id="toggleConfirmPassword" class="toggle-password">Show</span> </div> + <input type="text" name="f_name" placeholder="First Name" required> + <input type="text" name="l_name" placeholder="Last Name" required> + <input type="text" name="phone" placeholder="Phone" required> - <input type="text" name="club_id" placeholder="Club ID (optional)"> <button type="submit">Register</button> </form> diff --git a/views/pages/booking.ejs b/views/pages/booking.ejs deleted file mode 100644 index 310b7fce31fa43a45f1f8a0ddb880dda92a1d1bd..0000000000000000000000000000000000000000 --- a/views/pages/booking.ejs +++ /dev/null @@ -1,5 +0,0 @@ -<%- include('../partials/header') %> -<link rel="stylesheet" href="/css/main.css"> -<div class="main-content"> - -</div> \ No newline at end of file diff --git a/views/pages/chat.ejs b/views/pages/chat.ejs deleted file mode 100644 index 310b7fce31fa43a45f1f8a0ddb880dda92a1d1bd..0000000000000000000000000000000000000000 --- a/views/pages/chat.ejs +++ /dev/null @@ -1,5 +0,0 @@ -<%- include('../partials/header') %> -<link rel="stylesheet" href="/css/main.css"> -<div class="main-content"> - -</div> \ No newline at end of file diff --git a/views/pages/dashboard.ejs b/views/pages/dashboard.ejs deleted file mode 100644 index 310b7fce31fa43a45f1f8a0ddb880dda92a1d1bd..0000000000000000000000000000000000000000 --- a/views/pages/dashboard.ejs +++ /dev/null @@ -1,5 +0,0 @@ -<%- include('../partials/header') %> -<link rel="stylesheet" href="/css/main.css"> -<div class="main-content"> - -</div> \ No newline at end of file diff --git a/views/pages/index.ejs b/views/pages/index.ejs index fabb8b5b67ceb70b5c89634b21af9c05e156dec7..efebb4908532919e1d4cb841721cdba4fed97858 100644 --- a/views/pages/index.ejs +++ b/views/pages/index.ejs @@ -1,62 +1,42 @@ <%- include('../partials/header') %> <link rel="stylesheet" href="/css/main.css"> <link rel="stylesheet" href="/css/pages/index.css"> - -<body> - <div class="main-content"> - <h1>ชมรม</h1> - <!-- แสดงจำนวนกิจกรรม --> - <h2>จำนวนกิจกรรมทั้งหมด: <%= activities.length %></h2> - - <!-- ถ้าเป็น admin ให้แสดงปุ่มเพิ่มกิจกรรม --> - <% if (user && user.role === 'admin') { %> - <a href="/activities/new" class="btn btn-primary">เพิ่มกิจกรรม</a> - <% } %> - - <div class="activity-content"> - <div class="activities-list"> - <ul> - <% activities.forEach(function(activity) { %> - <li class="activity-item"> - <!-- ชื่อกิจกรรม --> - <h3>กิจกรรม: <%= activity.act_name %></h3> - - <!-- รายละเอียด --> - <p><strong>รายละเอียด:</strong> <%= activity.detail %></p> - - <!-- วันที่เริ่มกิจกรรม --> - <% if (activity.start_date) { %> - <p><strong>วันที่เริ่ม:</strong> - <%= new Date(activity.start_date).toLocaleDateString() %> - </p> - <% } %> - - <!-- เวลาเริ่มต้น - เวลาสิ้นสุด --> - <% if (activity.start_time && activity.end_time) { %> - <p><strong>เวลา:</strong> <%= activity.start_time %> - <%= activity.end_time %></p> - <% } %> - - <!-- วันที่สิ้นสุดกิจกรรม --> - <% if (activity.end_date) { %> - <p><strong>สิ้นสุดกิจกรรม:</strong> - <%= new Date(activity.end_date).toLocaleDateString() %> - </p> - <% } %> - - <!-- ถ้าเป็น admin ให้แสดงปุ่มแก้ไขและลบ --> - <% if (user && user.role === 'admin') { %> - <div class="admin-actions"> - <a href="/activities/edit/<%= activity.activity_id %>" class="btn btn-warning">แก้ไข</a> - <form action="/activities/delete/<%= activity.activity_id %>" method="POST" style="display:inline;"> - <button type="submit" class="btn btn-danger" onclick="return confirm('ยืนยันการลบกิจกรรมนี้?')">ลบ</button> - </form> - </div> - <% } %> - - </li> - <% }); %> - </ul> - </div> - </div> +<div class="main-content"> + <div class="d-flex justify-content-between align-items-center mb-5"> + <h1 class="h2 fw-bold text-primary">All Posts</h1> + <div class="badge bg-primary rounded-pill"><%= posts.length %> posts</div> </div> -</body> + + <% if (typeof posts !== 'undefined' && posts.length > 0) { %> + <div class="post-list"> + <% posts.forEach(post => { %> + <article class="post-card card mb-4 shadow-sm"> + <div class="card-body"> + <h2 class="card-title mb-3"> + <a href="/posts/<%= post.id %>" class="text-decoration-none text-dark"><%= post.title %></a> + </h2> + <p class="card-text text-muted mb-4"><%= post.content.substring(0, 150) %>...</p> + <div class="post-meta d-flex gap-4 text-muted"> + <div class="d-flex align-items-center gap-1"> + <i class="bi bi-person-circle"></i> + <%= post.username %> + </div> + <div class="d-flex align-items-center gap-1"> + <i class="bi bi-tag"></i> + <%= post.category_name %> + </div> + <div class="d-flex align-items-center gap-1"> + <i class="bi bi-clock"></i> + <%= new Date(post.created_at).toLocaleString() %> + </div> + </div> + </div> + </article> + <% }); %> + </div> + <% } else { %> + <div class="alert alert-info"> + <i class="bi bi-info-circle me-2"></i>No posts available. + </div> + <% } %> +</div> \ No newline at end of file diff --git a/views/pages/settings.ejs b/views/pages/settings.ejs deleted file mode 100644 index 310b7fce31fa43a45f1f8a0ddb880dda92a1d1bd..0000000000000000000000000000000000000000 --- a/views/pages/settings.ejs +++ /dev/null @@ -1,5 +0,0 @@ -<%- include('../partials/header') %> -<link rel="stylesheet" href="/css/main.css"> -<div class="main-content"> - -</div> \ No newline at end of file diff --git a/views/partials/header.ejs b/views/partials/header.ejs index 672822f9544bbd10a2a0e5341b065ba7cff7cdda..c60f3a3287082ed534db40974bc6d19e00d6868a 100644 --- a/views/partials/header.ejs +++ b/views/partials/header.ejs @@ -1,68 +1,57 @@ <!-- เพิ่ม Bootstrap 5 CSS และ Icon ใน head --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"> -<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.1/font/bootstrap-icons.css"> +<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.1/font/bootstrap-icons.css" rel="stylesheet"> +<link href="https://fonts.googleapis.com/css2?family=Sarabun:wght@300;400;500;600;700&display=swap" rel="stylesheet"> <link rel="stylesheet" href="/css/partials/header.css"> - <aside class="sidebar"> - <div class="sidebar-header"> - <h2></i> เว็บจัดการชมรม</h2> - <% if (user) { %> - <div class="user-info"> - <p class="mb-1 fw-bold text-dark">สวัสดี <%= user.username %></p> - <small class="text-muted"><i class="bi bi-person-badge"></i> <%= user.role %></small> - </div> - <% } else { %> - <div class="user-info"> - <p class="mb-2 fw-bold text-dark">สวัสดี Guest</p> - <div class="d-grid gap-2"> - <a href="/" class="btn btn-sm btn-outline-primary"><i class="bi bi-box-arrow-in-right"></i> Login</a> - <a href="/register" class="btn btn-sm btn-primary"><i class="bi bi-person-plus"></i> Register</a> - </div> - </div> - <% } %> - </div> - - <nav class="sidebar-nav"> - <ul class="nav flex-column"> - <li class="nav-item"> - <a href="/index" class="nav-link" id="index"> - <i class="bi bi-calendar-event"></i> - Activity - </a> - </li> - <li class="nav-item"> - <a href="/dashboard" class="nav-link" id="dashboard"> - <i class="bi bi-speedometer2"></i> - Dashboard - </a> - </li> - <li class="nav-item"> - <a href="/booking" class="nav-link" id="booking"> - <i class="bi bi-journal-check"></i> - Booking - </a> - </li> - <li class="nav-item"> - <a href="/chat" class="nav-link" id="chat"> - <i class="bi bi-chat-dots"></i> - Chat - </a> - </li> - <li class="nav-item"> - <a href="/settings" class="nav-link" id="settings"> - <i class="bi bi-gear"></i> - Settings - </a> - </li> - </ul> - </nav> - + <div class="sidebar-header"> + <h2>WebPosts</h2> <% if (user) { %> - <div class="logout-btn"> - <a href="/auth/logout" class="btn btn-outline-danger"> - <i class="bi bi-box-arrow-left"></i> - Logout - </a> - </div> + <div class="user-info"> + <p class="mb-1 fw-bold text-dark"><%= user.username %></p> + </div> <% } %> -</aside> \ No newline at end of file + <form action="/posts/search" method="get"> + <input type="text" name="keyword" placeholder="ค้นหาบทความ"> + <button type="submit">ค้นหา</button> + </form> + </div> + + <nav class="sidebar-nav"> + <ul class="nav flex-column"> + <li class="nav-item"> + <a href="/index" class="nav-link" id="index"> + <i class="bi bi-calendar-event"></i> + All posts + </a> + </li> + <li class="nav-item"> + <a href="/posts" class="nav-link" id="posts"> + <i class="bi bi-journal-check"></i> + My posts + </a> + </li> + <li class="nav-item"> + <a href="/posts/add" class="nav-link" id="add"> + <i class="bi bi-plus-circle"></i> + Add post + </a> + </li> + <li class="nav-item"> + <a href="/settings" class="nav-link" id="settings"> + <i class="bi bi-gear"></i> + Settings + </a> + </li> + </ul> + </nav> + + <% if (user) { %> + <div class="logout-btn"> + <a href="/auth/logout" class="btn btn-outline-danger"> + <i class="bi bi-box-arrow-left"></i> + Logout + </a> + </div> + <% } %> +</aside> diff --git a/views/posts/add.ejs b/views/posts/add.ejs new file mode 100644 index 0000000000000000000000000000000000000000..2e0a8f063e6896247f2a4c60b182358415e8c58a --- /dev/null +++ b/views/posts/add.ejs @@ -0,0 +1,34 @@ +<%- include('../partials/header') %> +<link rel="stylesheet" href="/css/main.css"> +<link rel="stylesheet" href="/css/pages/index.css"> + +<div class="main-content"> + <h1>Add post</h1> + + <% if (error) { %> + <div class="alert alert-danger"><%= error %></div> + <% } %> + + <form action="/posts/add" method="post"> + <div class="mb-3"> + <label for="title" class="form-label">หัวข้อ</label> + <input type="text" class="form-control" id="title" name="title" required> + </div> + + <div class="mb-3"> + <label for="content" class="form-label">เนื้อหา</label> + <textarea class="form-control" id="content" name="content" rows="5" required></textarea> + </div> + + <div class="mb-3"> + <label for="category" class="form-label">หมวดหมู่</label> + <select class="form-select" id="category" name="category_id" required> + <% categories.forEach(category => { %> + <option value="<%= category.id %>"><%= category.name %></option> + <% }); %> + </select> + </div> + + <button type="submit" class="btn btn-primary">เพิ่มบทความ</button> + </form> +</div> diff --git a/views/posts/edit.ejs b/views/posts/edit.ejs new file mode 100644 index 0000000000000000000000000000000000000000..f8cdc23785e2777903c8d8269fc0de6ebf8bfd48 --- /dev/null +++ b/views/posts/edit.ejs @@ -0,0 +1,34 @@ +<%- include('../partials/header') %> +<link rel="stylesheet" href="/css/main.css"> +<link rel="stylesheet" href="/css/pages/index.css"> + +<div class="main-content"> + <h1>Edit</h1> + + <% if (error) { %> + <div class="alert alert-danger"><%= error %></div> + <% } %> + + <form action="/posts/edit/<%= post.id %>" method="post"> + <div class="mb-3"> + <label for="title" class="form-label">หัวข้อ</label> + <input type="text" class="form-control" id="title" name="title" value="<%= post.title %>" required> + </div> + + <div class="mb-3"> + <label for="content" class="form-label">เนื้อหา</label> + <textarea class="form-control" id="content" name="content" rows="5" required><%= post.content %></textarea> + </div> + + <div class="mb-3"> + <label for="category" class="form-label">หมวดหมู่</label> + <select class="form-select" id="category" name="category_id" required> + <% categories.forEach(category => { %> + <option value="<%= category.id %>" <%= category.id === post.category_id ? 'selected' : '' %>><%= category.name %></option> + <% }); %> + </select> + </div> + + <button type="submit" class="btn btn-primary">บันทึกการแก้ไข</button> + </form> +</div> diff --git a/views/posts/index.ejs b/views/posts/index.ejs new file mode 100644 index 0000000000000000000000000000000000000000..f1ead3ef1d02a28f2f36c9a70d95ba79f97ec78d --- /dev/null +++ b/views/posts/index.ejs @@ -0,0 +1,72 @@ +<%- include('../partials/header') %> +<link rel="stylesheet" href="/css/main.css"> +<link rel="stylesheet" href="/css/pages/index.css"> + +<div class="main-content"> + <div class="d-flex justify-content-between align-items-center mb-5"> + <h1 class="h2 fw-bold text-primary">My Posts</h1> + <div class="badge bg-primary rounded-pill"><%= posts.length %> posts</div> + </div> + + <% if (posts.length > 0) { %> + <div class="post-list"> + <% posts.forEach(post => { %> + <article class="post-card card mb-4 shadow-sm"> + <div class="card-body"> + <h2 class="card-title mb-3"> + <a href="/posts/<%= post.id %>" class="text-decoration-none text-dark"><%= post.title %></a> + </h2> + <p class="card-text text-muted mb-4"><%= post.content.substring(0, 150) %>...</p> + + <!-- Metadata Section --> + <div class="post-meta d-flex flex-wrap gap-4 text-muted mb-3"> + <% if (post.username) { %> + <div class="d-flex align-items-center gap-1"> + <i class="bi bi-person-circle"></i> + <%= post.username %> + </div> + <% } %> + + <% if (post.category_name) { %> + <div class="d-flex align-items-center gap-1"> + <i class="bi bi-tag"></i> + <%= post.category_name %> + </div> + <% } %> + + <div class="d-flex align-items-center gap-1"> + <i class="bi bi-clock"></i> + <%= new Date(post.created_at).toLocaleString() %> + </div> + </div> + + <!-- Action Buttons --> + <div class="post-actions row justify-content-end g-2"> + <div class="col-auto"> + <a href="/posts/edit/<%= post.id %>" + class="btn btn-sm btn-outline-primary d-flex align-items-center text-nowrap"> + <i class="bi bi-pencil me-2"></i> + แก้ไข + </a> + </div> + <div class="col-auto"> + <form action="/posts/delete/<%= post.id %>" method="post"> + <button type="submit" + class="btn btn-sm btn-outline-danger d-flex align-items-center text-nowrap"> + <i class="bi bi-trash me-2"></i> + ลบ + </button> + </form> + </div> + </div> + + </div> + </article> + <% }); %> + </div> + <% } else { %> + <div class="alert alert-info"> + <i class="bi bi-info-circle me-2"></i>No posts available. + </div> + <% } %> +</div> \ No newline at end of file diff --git a/views/settings.ejs b/views/settings.ejs new file mode 100644 index 0000000000000000000000000000000000000000..b0b93a618566fb011d1535efad2e9a2dede46f6c --- /dev/null +++ b/views/settings.ejs @@ -0,0 +1,46 @@ +<%- include('./partials/header') %> +<link rel="stylesheet" href="/css/main.css"> +<link rel="stylesheet" href="/css/pages/settings.css"> + +<div class="main-content"> + <h1>Settings</h1> + <p>ปรับแต่งข้อมูลส่วนตัวและตั้งค่าต่าง ๆ ได้ที่หน้านี้</p> + + <!-- ฟอร์มแสดงข้อมูลผู้ใช้ (เริ่มต้นเป็นแบบอ่านอย่างเดียว) --> + <form id="settingsForm" action="/settings/update" method="post"> + <div class="mb-3"> + <label for="f_name" class="form-label">ชื่อ</label> + <input type="text" class="form-control" id="f_name" name="f_name" value="<%= user.f_name %>" disabled required> + </div> + <div class="mb-3"> + <label for="l_name" class="form-label">นามสกุล</label> + <input type="text" class="form-control" id="l_name" name="l_name" value="<%= user.l_name %>" disabled required> + </div> + <div class="mb-3"> + <label for="email" class="form-label">อีเมล</label> + <input type="email" class="form-control" id="email" name="email" value="<%= user.email %>" disabled required> + </div> + <div class="mb-3"> + <label for="phone" class="form-label">เบอร์โทรศัพท์</label> + <input type="text" class="form-control" id="phone" name="phone" value="<%= user.phone %>" disabled> + </div> + + <!-- ปุ่มสำหรับเปิดให้แก้ไขข้อมูล --> + <button type="button" class="btn btn-secondary" onclick="enableEdit()">แก้ไขข้อมูล</button> + + <!-- ปุ่มสำหรับบันทึกข้อมูล (ซ่อนไว้จนกว่าจะกดแก้ไข) --> + <button type="submit" class="btn btn-primary d-none" id="saveBtn">บันทึกการตั้งค่า</button> + </form> +</div> + +<script> + function enableEdit() { + // เปิดให้แก้ไขฟิลด์ทั้งหมด + document.getElementById('f_name').disabled = false; + document.getElementById('l_name').disabled = false; + document.getElementById('email').disabled = false; + document.getElementById('phone').disabled = false; + // แสดงปุ่มบันทึกการตั้งค่า + document.getElementById('saveBtn').classList.remove('d-none'); + } +</script>