diff --git a/server.js b/server.js new file mode 100644 index 0000000000000000000000000000000000000000..55565729d97652bf4dc9986f07bea1afa4c1096e --- /dev/null +++ b/server.js @@ -0,0 +1,291 @@ +// โหลดไฟล์ .env เพื่อดึงค่าคอนฟิกต่าง ๆ +require('dotenv').config(); +const express = require('express'); +const session = require('express-session'); +const bodyParser = require('body-parser'); +const bcrypt = require('bcryptjs'); +const mysql = require('mysql2'); + +const app = express(); +const port = process.env.PORT; + +// ตั้งค่า view engine เป็น EJS +app.set('view engine', 'ejs'); +app.use(bodyParser.urlencoded({ extended: false })); +app.use(express.static('public')); + +// ตั้งค่าระบบ session +app.use(session({ + secret: process.env.SESSION_SECRET || 'default_secret_key', + resave: false, + saveUninitialized: false +})); + +// เชื่อมต่อฐานข้อมูล MySQL +const connection = mysql.createConnection({ + host: process.env.DB_HOST || 'localhost', + user: process.env.DB_USER || 'root', + password: process.env.DB_PASS || '', + database: process.env.DB_NAME || 'cloud_db_threads', + port: process.env.DB_PORT || 3306 +}); + +connection.connect(err => { + if (err) { + console.error('Error connecting to MySQL:', err); + process.exit(1); + } + console.log('Connected to MySQL Successfully'); +}); + +// Middleware ที่ใช้เช็คว่าผู้ใช้ล็อกอินหรือยัง +function isAuthenticated(req, res, next) { + if (req.session.userId) return next(); + res.redirect('/login'); +} + +// หน้าแรก: พาไป dashboard ทันที ( User แบบ Guest สามารถเข้าได้) +app.get('/', (req, res) => { + res.redirect('/dashboard'); +}); + +// หน้า register +app.get('/register', (req, res) => { + res.render('register', { error: null }); +}); + +// ดำเนินการ register ผู้ใช้ใหม่ +app.post('/register', async (req, res) => { + const { username, email, password } = req.body; + try { + const hashedPassword = await bcrypt.hash(password, 10); + connection.query( + 'INSERT INTO users (username, email, password) VALUES (?, ?, ?)', + [username, email, hashedPassword], + (err) => { + if (err) { + console.error(err); + if (err.code === 'ER_DUP_ENTRY') { + return res.render('register', { error: 'ชื่อผู้ใช้นี้ถูกใช้ไปแล้ว' }); + } + return res.render('register', { error: 'เกิดข้อผิดพลาดในการลงทะเบียน' }); + } + res.redirect('/login'); + } + ); + } catch (error) { + console.error(error); + res.render('register', { error: 'เกิดข้อผิดพลาดระหว่างดำเนินการลงทะเบียน' }); + } +}); + +// หน้า login +app.get('/login', (req, res) => { + res.render('login', { error: null }); +}); + +// ดำเนินการ login +app.post('/login', (req, res) => { + const { userInput, password } = req.body; + connection.query( + 'SELECT * FROM users WHERE username = ? OR email = ?', + [userInput, userInput], + async (err, results) => { + if (err) return res.render('login', { error: 'เกิดข้อผิดพลาด' }); + if (results.length === 0) return res.render('login', { error: 'ไม่พบผู้ใช้งาน' }); + const user = results[0]; + const match = await bcrypt.compare(password, user.password); + if (match) { + req.session.userId = user.id; + req.session.username = user.username; + res.redirect('/dashboard'); + } else { + res.render('login', { error: 'รหัสผ่านไม่ถูกต้อง' }); + } + } + ); +}); + +// หน้า dashboard แสดงรายการ threads ทั้งหมด +app.get('/dashboard', (req, res) => { + const searchQuery = req.query.search; + let sql = ` + SELECT threads.*, users.username AS ownerName + FROM threads + JOIN users ON threads.user_id = users.id + `; + const params = []; + if (searchQuery) { + sql += `WHERE threads.thread_name LIKE ? OR threads.description LIKE ?`; + params.push(`%${searchQuery}%`, `%${searchQuery}%`); + } + sql += ' ORDER BY threads.id DESC'; + connection.query(sql, params, (err, threads) => { + if (err) return res.send('เกิดข้อผิดพลาดในการดึงข้อมูล threads'); + res.render('dashboard', { + username: req.session.username, + userId: req.session.userId, + threads, + query: searchQuery || '' + }); + }); +}); + +// หน้าเพิ่ม thread ใหม่ +app.get('/thread/create', isAuthenticated, (req, res) => { + res.render('thread-create', { username: req.session.username }); +}); + +// บันทึก thread ใหม่ลงฐานข้อมูล +app.post('/thread/create', isAuthenticated, (req, res) => { + const { thread_name, description } = req.body; + connection.query( + 'INSERT INTO threads (user_id, thread_name, description) VALUES (?, ?, ?)', + [req.session.userId, thread_name, description], + (err) => { + if (err) return res.send('เกิดข้อผิดพลาดในการสร้าง thread'); + res.redirect('/dashboard'); + } + ); +}); + +// หน้าแก้ไข thread +app.get('/thread/edit/:id', isAuthenticated, (req, res) => { + const threadId = req.params.id; + connection.query( + 'SELECT * FROM threads WHERE id = ? AND user_id = ?', + [threadId, req.session.userId], + (err, results) => { + if (err || results.length === 0) return res.send('ไม่พบ thread ที่ต้องการแก้ไข'); + res.render('thread-edit', { + username: req.session.username, + thread: results[0] + }); + } + ); +}); + +// อัปเดตข้อมูล thread ที่แก้ไข +app.post('/thread/edit/:id', isAuthenticated, (req, res) => { + const threadId = req.params.id; + const { thread_name, description } = req.body; + connection.query( + 'UPDATE threads SET thread_name = ?, description = ? WHERE id = ? AND user_id = ?', + [thread_name, description, threadId, req.session.userId], + (err) => { + if (err) return res.send('เกิดข้อผิดพลาดในการอัปเดต thread'); + res.redirect('/dashboard'); + } + ); +}); + +// ลบ thread +app.get('/thread/delete/:id', isAuthenticated, (req, res) => { + const threadId = req.params.id; + connection.query( + 'DELETE FROM threads WHERE id = ? AND user_id = ?', + [threadId, req.session.userId], + (err) => { + if (err) return res.send('เกิดข้อผิดพลาดในการลบ thread'); + res.redirect('/dashboard'); + } + ); +}); + +// หน้าแสดง thread พร้อมคอมเมนต์ +app.get('/thread/:id', (req, res) => { + const threadId = req.params.id; + + connection.query( + `SELECT threads.*, users.username AS ownerName + FROM threads + JOIN users ON threads.user_id = users.id + WHERE threads.id = ?`, + [threadId], + (err, results) => { + if (err || results.length === 0) return res.send('ไม่พบ thread ที่ต้องการ'); + + const thread = results[0]; + + connection.query( + `SELECT comments.*, users.username AS commentUser, users.email + FROM comments + JOIN users ON comments.user_id = users.id + WHERE comments.thread_id = ? + ORDER BY comments.comment_id DESC`, // ชื่อคอลัมน์ถูกต้อง + [threadId], + (err2, comments) => { + if (err2) return res.send('เกิดข้อผิดพลาดในการดึงคอมเมนต์'); + + res.render('thread-view', { + username: req.session.username, + userId: req.session.userId, + thread, + comments + }); + } + ); + } + ); +}); + +// เพิ่มคอมเมนต์ใหม่ +app.post('/thread/:id/comment', isAuthenticated, (req, res) => { + const threadId = req.params.id; + const { comment } = req.body; + connection.query( + 'INSERT INTO comments (thread_id, user_id, comment) VALUES (?, ?, ?)', + [threadId, req.session.userId, comment], + (err) => { + if (err) return res.send('เกิดข้อผิดพลาดในการเพิ่มคอมเมนต์'); + res.redirect(`/thread/${threadId}`); + } + ); +}); + +// ลบคอมเมนต์ +app.get('/comment/delete/:id', isAuthenticated, (req, res) => { + const commentId = req.params.id; + connection.query( + 'SELECT * FROM comments WHERE id = ? AND user_id = ?', + [commentId, req.session.userId], + (err, results) => { + if (err || results.length === 0) return res.send('ไม่พบคอมเมนต์ที่ต้องการลบ'); + const comment = results[0]; + connection.query('DELETE FROM comments WHERE id = ?', [commentId], (err2) => { + if (err2) return res.send('เกิดข้อผิดพลาดในการลบคอมเมนต์'); + res.redirect(`/thread/${comment.thread_id}`); + }); + } + ); +}); + + +// ดึงคอมเมนต์ล่าสุดจากทุกกระทู้ +app.get('/api/comments/latest',isAuthenticated, (req, res) => { + const sql = ` + SELECT c.comment, u.id AS user_id, u.email, u.username, c.created_at + FROM comments c + JOIN users u ON c.user_id = u.id + ORDER BY c.created_at DESC + LIMIT 10 + `; + connection.query(sql, (err, results) => { + if (err) { + console.error('ดึงคอมเมนต์ล่าสุดล้มเหลว:', err); + return res.status(500).json({ error: 'ดึงคอมเมนต์ไม่สำเร็จ' }); + } + res.json(results); + }); +}); + +// ออกจากระบบ +app.get('/logout', (req, res) => { + req.session.destroy(); + res.redirect('/dashboard'); +}); + +// เริ่มต้นเซิร์ฟเวอร์ +app.listen(port, '0.0.0.0', () => { + console.log(`Server เปิดอยู่ที่ http://localhost:${port}`); +}); diff --git a/views/dashboard.ejs b/views/dashboard.ejs new file mode 100644 index 0000000000000000000000000000000000000000..975f7fad7ad103587d0c03aaa36ccc2475ddb079 --- /dev/null +++ b/views/dashboard.ejs @@ -0,0 +1,144 @@ +<%- include('partials/header', { username: username }) %> + +<div class="container my-5"> + <div class="row"> + <!-- LEFT: THREAD LIST --> + <div class="col-md-8" style="background-color: #a86dcff5; color: #333; border-radius: 0px; padding: 30px;"> + <div class="d-flex justify-content-between align-items-center mb-4"> + <h2 class="mb-0" style="color: #000000; font-size: 30px; font-weight: 600;">THREAD TOPIC</h2> + <a href="/thread/create" class="btn" style="background-color: #000000; color: #fff; font-weight: 600; border-radius: 20px; padding: 10px 20px;"> + Create New Thread Topic + </a> + </div> + + <div class="list-group"> + <% threads.forEach(thread => { %> + <div class="list-group-item mb-4" style="background-color: #FFF; color: #333; border: none; border-radius: 0px; padding: 20px;"> + <div class="d-flex justify-content-between"> + <h5 class="mb-1" style="font-size: 20px; font-weight: 700;"> + <a href="#" onclick="loadComments"(<%= thread.id %>) style="color: #000000; text-decoration: none;"> + <%= thread.thread_name %> + </a> + </h5> + <small style="color: #999; font-size: 16px;"> + <span style="color: black; font-weight: bold;">Owner: <%= thread.ownerName %></span> + </small> + </div> + <p class="mb-3" style="font-size: 15px; line-height: 1.5;"> + <%= thread.description.length > 100 + ? thread.description.substring(0, 100) + '...' + : thread.description + %> + </p> + + <div class="mt-3"> + <a href="/thread/<%= thread.id %>" class="btn btn-sm btn-outline-info" style="border-radius: 20px;">View</a> + <% if (thread.user_id === userId) { %> + <a href="/thread/edit/<%= thread.id %>" class="btn btn-sm btn-outline-warning" style="border-radius: 20px;">Edit</a> + <a href="/thread/delete/<%= thread.id %>" + class="btn btn-sm btn-outline-danger" style="border-radius: 20px;" + onclick="return confirm('Are you sure you want to delete this thread?');"> + Delete + </a> + <% } %> + </div> + </div> + <% }) %> + </div> + </div> + + <style> + .col-md-4 { + display: flex; + + } + + #comment-box { + width: 300%; + text-align: center; + } + </style> + <!-- RIGHT: COMMENT BOX --> + <div class="col-md-4"> + <div id="comment-box" style="background-color: #000000; color: White; padding: 20px; min-height: 700px;"> + <h5 class="mb-3">Latest User's Comment</h5> + <div id="comment-list"></div> + </div> + </div> + </div> +</div> + +<script> +function loadComments(threadId) { + fetch(`/api/thread/${threadId}/comments`) + .then(res => res.json()) + .then(data => { + const list = document.getElementById('comment-list'); + list.innerHTML = ''; + if (data.length === 0) { + list.innerHTML = '<p>ยังไม่มีความคิดเห็น</p>'; + } else { + data.forEach(comment => { + const item = document.createElement('div'); + item.innerHTML = ` + <p><strong>${comment.username}</strong> (${comment.email}, ID: ${comment.user_id})</p> + <p>${comment.content}</p> + <hr style="border-color: white;"> + `; + list.appendChild(item); + }); + } + }) + .catch(err => { + document.getElementById('comment-list').innerHTML = '<p>เกิดข้อผิดพลาดในการโหลดคอมเมนต์</p>'; + console.error(err); + }); +} + +// โหลดคอมเมนต์ล่าสุดและแสดงแบบมีลำดับ + เวลา +document.addEventListener('DOMContentLoaded', function () { + fetch('/api/comments/latest') + .then(res => res.json()) + .then(data => { + const list = document.getElementById('comment-list'); + list.innerHTML = ''; + + if (data.length === 0) { + list.innerHTML = '<p>ยังไม่มีความคิดเห็น</p>'; + } else { + data.forEach((comment, index) => { + const item = document.createElement('div'); + item.style.textAlign = 'left'; + item.style.marginBottom = '15px'; + + // แปลงวันที่ให้ดูง่ายขึ้น + const createdAt = new Date(comment.created_at).toLocaleString + ('th-TH', { + dateStyle: 'medium', + timeStyle: 'short' + }); + + item.innerHTML = ` + <p><strong>--------------------------------------------------------------</strong></p> + <p><strong>#${index + 1}</strong></p> + <p><strong>Username : </strong> ${comment.username}</p> + <p><strong>User Email : </strong> ${comment.email}</p> + <p><strong>User ID : </strong> ${comment.user_id}</p> + <p><strong>Comment : </strong> ${comment.comment}</p> + <p><strong>Comment Since :</strong> ${createdAt}</p> + <p><strong>--------------------------------------------------------------</strong></p> + `; + list.appendChild(item); + }); + } + }) + .catch(err => { + document.getElementById('comment-list').innerHTML = '<p>โปรดเข้าสู่ระบบก่อนเพื่อดูประวัติการ Comment ล่าสุด</p>'; + console.error(err); + }); +}); + + +</script> + +<%- include('partials/footer') %> diff --git a/views/login.ejs b/views/login.ejs new file mode 100644 index 0000000000000000000000000000000000000000..dc098e43df5fd098653ed10b98bea797dbda2167 --- /dev/null +++ b/views/login.ejs @@ -0,0 +1,43 @@ +<!-- แสดง Header จาก partial/header.ejs --> +<%- include('partials/header') %> + +<!-- Container หลักของหน้า Login --> +<div class="container my-5" style="max-width: 450px;"> + <h2 class="text-center mb-4" style="color: #000000; font-weight: bold;">Login</h2> + + <!-- แสดงข้อความ error ถ้ามี error ถูกส่งมาจาก backend --> + <% if (error) { %> + <div class="alert alert-danger text-center" role="alert" style="font-size: 16px;"> + <%= error %> + </div> + <% } %> + + <!-- ฟอร์ม Login ส่งข้อมูลผ่าน POST ไปยัง /login --> + <form action="/login" method="POST" class="shadow p-4 rounded-lg" style="background-color: #F4F7FA;"> + + <!-- ช่องกรอก Username หรือ Email --> + <div class="form-group"> + <label for="userInput" style="font-weight: bold;">Username or Email:</label> + <input type="text" class="form-control" name="userInput" id="userInput" placeholder="Enter Username or Email" required> + </div> + + <!-- ช่องกรอก Password --> + <div class="form-group"> + <label for="password" style="font-weight: bold;">Password:</label> + <input type="password" class="form-control" name="password" id="password" placeholder="Enter Password" required> + </div> + + <!-- ปุ่มกดเข้าสู่ระบบ --> + <button type="submit" class="btn btn-primary btn-block" style="border-radius: 30px; padding: 12px; background-color: #000000; border-color: #000000;"> + Login + </button> + </form> + + <!-- ลิงก์ไปหน้า Register สำหรับผู้ที่ยังไม่มีบัญชี --> + <p class="text-center mt-3"> + Don't have an account yet? <a href="/register" style="color: #000000; font-weight: bold;">Register Your New Account here</a> + </p> +</div> + +<!-- แสดง Footer จาก partial/footer.ejs --> +<%- include('partials/footer') %> diff --git a/views/partials/footer.ejs b/views/partials/footer.ejs new file mode 100644 index 0000000000000000000000000000000000000000..a2bdb71d3e032ba8f28936a31e7731316b93f708 --- /dev/null +++ b/views/partials/footer.ejs @@ -0,0 +1,5 @@ +</div> <!-- end container --> +<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script> +<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.0/dist/js/bootstrap.bundle.min.js"></script> +</body> +</html> diff --git a/views/partials/header.ejs b/views/partials/header.ejs new file mode 100644 index 0000000000000000000000000000000000000000..f7bfe4c2e8e3ca6548ddee052ad54ce04915ad9f --- /dev/null +++ b/views/partials/header.ejs @@ -0,0 +1,97 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title>Reddit Forum</title> + <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css"> + <!-- เพิ่ม Favicon --> + <link rel="icon" href="path/to/your/favicon.ico" type="image/x-icon"> + + <style> + /* ปรับ Navbar ให้เป็นสีส้ม Shopee */ + .navbar { + background-color: #5c64f3 !important; /* สีส้ม Shopee */ + } + /* ตัวหนังสือบน Navbar ให้เป็นสีขาว */ + .navbar-brand, .nav-link { + color: #000000!important; + font-weight: bold; + } + .navbar-brand:hover, .nav-link:hover { + color: #FFD2C3 !important; /* สีส้มอ่อนลงเมื่อ hover */ + } + + /* ปรับฟอร์มค้นหาให้สวยงาม */ + .search-form { + width: 500px; + margin-left: auto; + margin-right: auto; + } + .search-form .form-control { + border-radius: 50px; + padding-left: 20px; + font-size: 16px; + } + .search-form .input-group-append .btn { + background-color: #232428; + color: white; + /* border-radius: 50px; */ + border: none; + padding-left: 20px; + padding-right: 20px; + } + .search-form .input-group-append .btn:hover { + background-color: #410445; + } + + </style> +</head> +<body> +<nav class="navbar navbar-expand-lg navbar-dark"> + <!-- ชื่อ Website --> + <a class="navbar-brand" href="/dashboard">REDDIT-FORUM</a> + <!-- ปุ่ม toggle สำหรับ mobile --> + <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarContent" + aria-controls="navbarContent" aria-expanded="false" aria-label="Toggle navigation"> + <span class="navbar-toggler-icon"></span> + </button> + + <!-- ฟอร์ม Search จะปรากฏเฉพาะเมื่อผู้ใช้เข้าสู่ระบบ --> + <div class="collapse navbar-collapse" id="navbarContent"> + <% if (typeof username !== 'undefined') { %> + <!-- แสดงฟอร์มค้นหาสำหรับผู้ใช้ที่ล็อกอินแล้ว --> + <form action="/dashboard" method="GET" class="search-form d-flex"> + <div class="input-group"> + <input type="text" name="search" class="form-control" placeholder="Search Your Threds Here" + value="<%= typeof query !== 'undefined' ? query : '' %>"> + <div class="input-group-append"> + <button class="btn btn-outline-secondary" type="submit">Search</button> + <a href="/dashboard" class="btn btn-outline-secondary ml-2">Reset</a> + </div> + </div> + </form> + <% } %> + + <!-- เมนู Navbar --> + <ul class="navbar-nav ml-auto"> + <% if (typeof username !== 'undefined') { %> + <li class="nav-item"> + <a class="nav-link" href="#">Welcome, <%= username %></a> + </li> + <li class="nav-item"> + <a class="nav-link" href="/logout">Logout</a> + </li> + <% } else { %> + <li class="nav-item"> + <a class="nav-link" href="/login">Login</a> + </li> + <li class="nav-item"> + <a class="nav-link" href="/register">Register</a> + </li> + <% } %> + </ul> + </div> +</nav> + + +<div class="container mt-4"> diff --git a/views/register.ejs b/views/register.ejs new file mode 100644 index 0000000000000000000000000000000000000000..f3c0659110a4ff20efc1d53b3394c0cac1b00406 --- /dev/null +++ b/views/register.ejs @@ -0,0 +1,49 @@ +<!-- แสดง Header จาก partial/header.ejs --> +<%- include('partials/header') %> + +<!-- Container หลักของหน้าสมัครสมาชิก --> +<div class="container my-5" style="max-width: 450px;"> + <h2 class="text-center mb-4" style="color: #000000; font-weight: bold;">Register</h2> + + <!-- ถ้ามี error (จาก server) ให้แสดงข้อความแจ้งเตือนในกล่องสีแดง --> + <% if (error) { %> + <div class="alert alert-danger text-center" role="alert" style="font-size: 16px;"> + <%= error %> + </div> + <% } %> + + <!-- ฟอร์มสมัครสมาชิก ส่งข้อมูลผ่าน POST ไปยัง /register --> + <form action="/register" method="POST" class="shadow p-4 rounded-lg" style="background-color: #F4F7FA;"> + + <!-- ช่องกรอก Username --> + <div class="form-group"> + <label for="username" style="font-weight: bold;">Username:</label> + <input type="text" class="form-control" name="username" id="username" placeholder="Enter Username" required> + </div> + + <!-- ช่องกรอก Email --> + <div class="form-group"> + <label for="email" style="font-weight: bold;">Email:</label> + <input type="email" class="form-control" name="email" id="email" placeholder="Enter Email" required> + </div> + + <!-- ช่องกรอก Password --> + <div class="form-group"> + <label for="password" style="font-weight: bold;">Password:</label> + <input type="password" class="form-control" name="password" id="password" placeholder="Enter Password" required> + </div> + + <!-- ปุ่มกดสมัครสมาชิก --> + <button type="submit" class="btn btn-primary btn-block" style="border-radius: 30px; padding: 12px; background-color: #000000; border-color: #000000;"> + Register + </button> + </form> + + <!-- ลิงก์ไปหน้า Login หากผู้ใช้มีบัญชีอยู่แล้ว --> + <p class="text-center mt-3"> + Already have an account? <a href="/login" style="color: #000000; font-weight: bold;">Login here</a> + </p> +</div> + +<!-- แสดง Footer จาก partial/footer.ejs --> +<%- include('partials/footer') %> diff --git a/views/thread-create.ejs b/views/thread-create.ejs new file mode 100644 index 0000000000000000000000000000000000000000..9d385c0905a40814452c4c87116d75760383e85b --- /dev/null +++ b/views/thread-create.ejs @@ -0,0 +1,35 @@ +<!-- แสดง Header พร้อมส่งค่า username ไปด้วย --> +<%- include('partials/header', { username: username }) %> + +<!-- ส่วนฟอร์มสำหรับสร้างกระทู้ --> +<div class="container my-5" style="max-width: 600px;"> + <h2 class="text-center mb-4" style="color: #000000;">Create Thread</h2> + + <!-- Card ที่ห่อหุ้มฟอร์ม --> + <div class="card shadow-sm"> + <div class="card-body"> + <!-- ฟอร์มสร้างกระทู้ใหม่ --> + <form action="/thread/create" method="POST"> + <!-- ชื่อกระทู้ --> + <div class="form-group"> + <label for="thread_name" style="color: #333;">Thread Name:</label> + <input type="text" class="form-control" name="thread_name" id="thread_name" required> + </div> + + <!-- รายละเอียดกระทู้ --> + <div class="form-group"> + <label for="description" style="color: #333;">Description:</label> + <textarea class="form-control" name="description" id="description" rows="5"></textarea> + </div> + + <!-- ปุ่มสร้างกระทู้ --> + <button type="submit" class="btn btn-block" style="background-color: #000000; color: #fff; border: none;"> + Create + </button> + </form> + </div> + </div> +</div> + +<!-- แสดง Footer --> +<%- include('partials/footer') %> diff --git a/views/thread-edit.ejs b/views/thread-edit.ejs new file mode 100644 index 0000000000000000000000000000000000000000..02cd0548a1338d79d0a8b67905e147ab82819bbe --- /dev/null +++ b/views/thread-edit.ejs @@ -0,0 +1,40 @@ +<!-- แสดง Header และส่งตัวแปร username ไปยัง partial --> +<%- include('partials/header', { username: username }) %> + +<!-- Container หลักที่จำกัดความกว้างไว้ไม่เกิน 600px และมีระยะห่างด้านบน/ล่าง --> +<div class="container my-5" style="max-width: 600px;"> + + <!-- หัวข้อหน้าแก้ไขกระทู้ --> + <h2 class="text-center mb-4" style="color: #000000;">Edit Thread</h2> + + <!-- การ์ดสไตล์ Bootstrap พร้อมเงาเล็กน้อย --> + <div class="card shadow-sm"> + <div class="card-body"> + + <!-- ฟอร์มสำหรับแก้ไขกระทู้ ใช้ method POST และส่งไปยัง path ที่มี id ของกระทู้ --> + <form action="/thread/edit/<%= thread.id %>" method="POST"> + + <!-- ช่องกรอกชื่อกระทู้ พร้อมเติมค่าปัจจุบันจากตัวแปร thread --> + <div class="form-group"> + <label for="thread_name" style="color: #333;">Thread Name:</label> + <input type="text" class="form-control" name="thread_name" id="thread_name" value="<%= thread.thread_name %>" required> + </div> + + <!-- ช่องกรอกรายละเอียดกระทู้ พร้อมเติมค่าปัจจุบัน --> + <div class="form-group"> + <label for="description" style="color: #333;">Description:</label> + <textarea class="form-control" name="description" id="description" rows="5"><%= thread.description %></textarea> + </div> + + <!-- ปุ่มกดยืนยันการอัปเดตกระทู้ --> + <button type="submit" class="btn btn-block" style="background-color: #000000; color: #fff; border: none;"> + Update + </button> + </form> + + </div> + </div> +</div> + +<!-- แสดง Footer จาก partial --> +<%- include('partials/footer') %> diff --git a/views/thread-view.ejs b/views/thread-view.ejs new file mode 100644 index 0000000000000000000000000000000000000000..1157bad25192cc7e5358b04b918985e52ada8169 --- /dev/null +++ b/views/thread-view.ejs @@ -0,0 +1,94 @@ +<!-- แสดง Header และส่งตัวแปร username ไปยัง partial --> +<%- include('partials/header', { username: username }) %> + +<!-- Container หลักที่จำกัดความกว้างไว้ไม่เกิน 600px พร้อมระยะห่างด้านบน/ล่าง --> +<div class="container my-5" style="max-width: 600px;"> + + <!-- หัวข้อชื่อกระทู้ (แสดงชื่อกระทู้เป็นหัวข้อใหญ่) --> + <h2 class="text-center mb-4" style="color: #000000;"> <%= thread.thread_name %> </h2> + + <!-- การ์ดหลักที่แสดงรายละเอียดกระทู้ --> + <div class="card shadow-sm"> + <div class="card-body"> + + <!-- หัวข้อย่อยของการ์ด --> + <h5 class="card-title" style="color: #000000; font-size: 1.8rem;"> + Topic: <%= thread.thread_name %> + </h5> + + <!-- คำอธิบายของกระทู้ --> + <p class="card-text"> + <strong>Description:</strong> <%= thread.description %> + </p> + + <!-- เจ้าของกระทู้ --> + <p class="card-text"> + <strong>Owner:</strong> <%= thread.ownerName %> + </p> + + <!-- ปุ่มแก้ไขและลบกระทู้ (แสดงเฉพาะเมื่อ user เป็นเจ้าของกระทู้) --> + <% if (thread.user_id === userId) { %> + <a href="/thread/edit/<%= thread.id %>" class="btn btn-sm btn-outline-warning">Edit</a> + <a href="/thread/delete/<%= thread.id %>" class="btn btn-sm btn-outline-danger" + onclick="return confirm('Are you sure you want to delete this thread?');"> + Delete + </a> + <% } %> + + <!-- ปุ่มย้อนกลับไปหน้า Dashboard --> + <div class="d-flex justify-content-end mt-2"> + <a href="/dashboard" class="btn btn-sm btn-outline-info">Back to Dashboard</a> + </div> + </div> + </div> + + <!-- ส่วนแสดงคอมเมนต์ทั้งหมด --> + <div class="mt-4"> + <h4 style="color: #000000;">Comments</h4> + + <!-- ถ้ามีคอมเมนต์ --> + <% if (comments && comments.length > 0) { %> + <ul class="list-group"> + <% comments.forEach(c => { %> + <li class="list-group-item mb-2" style="border-radius: 5px;"> + <!-- แสดงชื่อผู้คอมเมนต์และข้อความ --> + <strong><%= c.commentUser %>:</strong> <%= c.comment %> + + <!-- แสดง Email --> + <span class="text-muted" style="font-size: 0.8em;"> + <b>(<%= c.email %>)</b> + </span> + + <!-- ปุ่มลบคอมเมนต์ (เฉพาะเจ้าของคอมเมนต์) --> + <% if (c.user_id === userId) { %> + <a href="/comment/delete/<%= c.id %>" + class="btn btn-sm btn-outline-danger float-right" + onclick="return confirm('Delete this comment?');"> + Delete + </a> + <% } %> + </li> + <% }) %> + </ul> + <% } else { %> + <!-- ถ้ายังไม่มีคอมเมนต์ --> + <p>No comments yet.</p> + <% } %> + </div> + + <!-- แบบฟอร์มเพิ่มคอมเมนต์ใหม่ --> + <div class="card mt-3"> + <div class="card-body"> + <form action="/thread/<%= thread.id %>/comment" method="POST"> + <div class="form-group"> + <label for="comment">Add Comment:</label> + <textarea class="form-control" name="comment" id="comment" rows="2" required></textarea> + </div> + <button type="submit" class="btn" style="background-color: #000000; color: #fff;">Comment</button> + </form> + </div> + </div> +</div> + +<!-- แสดง Footer --> +<%- include('partials/footer') %>