diff --git a/public/css/style.css b/public/css/style.css index 1d022947435a981e733516a83d61e0082e9f7ea8..ac2e2a503ab96d2c0cb2e0ee6c90fe546503bae0 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -72,4 +72,177 @@ th, td { display: grid; grid-template-columns: 1fr 1fr 1fr auto; gap: 10px; -} \ No newline at end of file +} +.container { padding: 20px; } + + .form-row { + display: flex; + gap: 10px; + margin-bottom: 10px; + } + + .status { + padding: 5px 10px; + border-radius: 4px; + font-weight: bold; + } + + .status-out { + background: #dc3545; + color: white; + } + + .status-low { + background: #ffc107; + color: black; + } + + .status-normal { + background: #28a745; + color: white; + } + + button { + padding: 5px 10px; + border-radius: 4px; + cursor: pointer; + } + + .modal { + display: none; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0,0,0,0.5); + z-index: 1000; + } + + .modal-content { + background-color: white; + margin: 15% auto; + padding: 20px; + width: 80%; + max-width: 600px; + border-radius: 5px; + position: relative; + } + + .form-actions { + display: flex; + gap: 10px; + justify-content: flex-end; + margin-top: 20px; + } + + .form-actions button { + padding: 8px 16px; + } + + .form-actions button[type="submit"] { + background-color: #28a745; + color: white; + border: none; + } + + .form-actions button[type="button"] { + background-color: #dc3545; + color: white; + border: none; + } + + .search-section { + margin-bottom: 20px; + } + + .search-form { + display: flex; + gap: 10px; + align-items: center; + } + + .search-form input { + padding: 8px; + border: 1px solid #ddd; + border-radius: 4px; + width: 300px; + } + + .search-form button { + padding: 8px 16px; + background: #007bff; + color: white; + border: none; + border-radius: 4px; + cursor: pointer; + } + + .clear-search { + padding: 8px 16px; + background: #6c757d; + color: white; + text-decoration: none; + border-radius: 4px; + } + .modal { + display: none; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0,0,0,0.5); + } + + .modal-content { + background-color: white; + margin: 15% auto; + padding: 20px; + border-radius: 5px; + width: 50%; + max-width: 500px; + } + + .category-form { + margin: 20px 0; + display: flex; + gap: 10px; + } + + .category-form input { + flex: 1; + padding: 8px; + border: 1px solid #ddd; + border-radius: 4px; + } + + table { + width: 100%; + border-collapse: collapse; + margin-top: 20px; + } + + th, td { + padding: 12px; + text-align: left; + border-bottom: 1px solid #ddd; + } + + .edit-btn, .delete-btn { + padding: 5px 10px; + margin: 0 5px; + border: none; + border-radius: 4px; + cursor: pointer; + } + + .edit-btn { + background-color: #ffc107; + color: black; + } + + .delete-btn { + background-color: #dc3545; + color: white; + } \ No newline at end of file diff --git a/routes/categories.js b/routes/categories.js index 29d21a640dfa6d16bc546037c6ff63887981735d..6426487527c79f0fd425d0ef36acbac14fa198ba 100644 --- a/routes/categories.js +++ b/routes/categories.js @@ -2,7 +2,7 @@ const express = require('express'); const router = express.Router(); const db = require('../database/db'); -// Middleware to check if user is logged in + const isAuthenticated = (req, res, next) => { if (req.session.userId) { next(); @@ -11,7 +11,7 @@ const isAuthenticated = (req, res, next) => { } }; -// Get all categories + router.get('/', isAuthenticated, async (req, res) => { try { const [categories] = await db.query('SELECT * FROM categories'); @@ -50,7 +50,7 @@ router.put('/:id', isAuthenticated, async (req, res) => { // Delete category router.delete('/:id', isAuthenticated, async (req, res) => { try { - // Check if category has products + const [products] = await db.query( 'SELECT COUNT(*) as count FROM products WHERE category_id = ?', [req.params.id] diff --git a/routes/products.js b/routes/products.js index a11fa053efacdfe589fb41881fae900c48fa3bc8..fd5a6bc89635673ab0d8243b28cc8d05de84ecfd 100644 --- a/routes/products.js +++ b/routes/products.js @@ -2,7 +2,7 @@ const express = require('express'); const router = express.Router(); const db = require('../database/db'); -// Middleware to check if user is logged in + const isAuthenticated = (req, res, next) => { if (req.session.userId) { next(); @@ -16,7 +16,7 @@ router.get('/', isAuthenticated, async (req, res) => { try { const { search } = req.query; - // Build query with search condition + let sql = ` SELECT p.*, c.name as category_name FROM products p @@ -29,11 +29,11 @@ router.get('/', isAuthenticated, async (req, res) => { params = [`%${search}%`, `%${search}%`]; } - // Execute query + const [products] = await db.query(sql, params); const [categories] = await db.query('SELECT * FROM categories'); - // Add helper function to be available in template + const getStockStatus = (current, minimum) => { current = parseInt(current) || 0; minimum = parseInt(minimum) || 0; @@ -47,12 +47,12 @@ router.get('/', isAuthenticated, async (req, res) => { return { text: 'ปกติ', class: 'status-normal' }; }; - // Pass search value back to template + res.render('products', { products, categories, searchQuery: search || '', - getStockStatus // Pass the function to the template + getStockStatus }); } catch (err) { console.error(err); @@ -99,7 +99,7 @@ router.post('/', isAuthenticated, async (req, res) => { } }); -// Get edit form + router.get('/edit/:id', isAuthenticated, async (req, res) => { try { const [products] = await db.query( @@ -122,7 +122,7 @@ router.get('/edit/:id', isAuthenticated, async (req, res) => { } }); -// Get product by ID + router.get('/:id', isAuthenticated, async (req, res) => { try { const [products] = await db.query( @@ -141,7 +141,7 @@ router.get('/:id', isAuthenticated, async (req, res) => { } }); -// Update product + router.put('/:id', isAuthenticated, async (req, res) => { try { const { @@ -179,7 +179,7 @@ router.put('/:id', isAuthenticated, async (req, res) => { } }); -// Delete product + router.delete('/:id', isAuthenticated, async (req, res) => { try { await db.query('DELETE FROM products WHERE id = ?', [req.params.id]); @@ -197,13 +197,13 @@ router.post('/stock/add', isAuthenticated, async (req, res) => { await db.query('START TRANSACTION'); - // บันทึกการเพิ่มสต็อก + await db.query( 'INSERT INTO stock_movements (product_id, quantity, type, user_id, description) VALUES (?, ?, "in", ?, ?)', [product_id, quantity, req.session.userId, description] ); - // อัพเดตจำนวนสต็อก + await db.query( 'UPDATE products SET stock_quantity = stock_quantity + ? WHERE id = ?', [quantity, product_id] @@ -218,14 +218,14 @@ router.post('/stock/add', isAuthenticated, async (req, res) => { } }); -// ตัดสต็อก + router.post('/stock/remove', isAuthenticated, async (req, res) => { try { const { product_id, quantity, description } = req.body; await db.query('START TRANSACTION'); - // ตรวจสอบจำนวนสต็อก + const [product] = await db.query( 'SELECT stock_quantity FROM products WHERE id = ?', [product_id] @@ -236,13 +236,13 @@ router.post('/stock/remove', isAuthenticated, async (req, res) => { return res.status(400).json({ error: 'Insufficient stock' }); } - // บันทึกการตัดสต็อก + await db.query( 'INSERT INTO stock_movements (product_id, quantity, type, user_id, description) VALUES (?, ?, "out", ?, ?)', [product_id, quantity, req.session.userId, description] ); - // อัพเดตจำนวนสต็อก + await db.query( 'UPDATE products SET stock_quantity = stock_quantity - ? WHERE id = ?', [quantity, product_id] @@ -257,7 +257,7 @@ router.post('/stock/remove', isAuthenticated, async (req, res) => { } }); -// ดูประวัติการเคลื่อนไหวของสต็อก + router.get('/stock/history/:productId', isAuthenticated, async (req, res) => { try { const [movements] = await db.query(` diff --git a/routes/stock.js b/routes/stock.js index fe005903e36200bb5eca555699cd3128524aec3c..eba0e54d7253bf1b3a9f9b920d5e3c8e8f3a4fe9 100644 --- a/routes/stock.js +++ b/routes/stock.js @@ -9,13 +9,13 @@ router.post('/add', async (req, res) => { await db.query('START TRANSACTION'); - // บันทึกการเคลื่อนไหว + await db.query( 'INSERT INTO stock_movements (product_id, quantity, type, user_id, description) VALUES (?, ?, "in", ?, ?)', [product_id, quantity, req.session.userId, description] ); - // อัพเดตจำนวนสต็อก + await db.query( 'UPDATE products SET stock_quantity = stock_quantity + ?, last_restock_date = CURRENT_TIMESTAMP WHERE id = ?', [quantity, product_id] @@ -37,7 +37,7 @@ router.post('/remove', async (req, res) => { await db.query('START TRANSACTION'); - // ตรวจสอบสต็อกคงเหลือ + const [product] = await db.query( 'SELECT stock_quantity FROM products WHERE id = ?', [product_id] @@ -48,13 +48,13 @@ router.post('/remove', async (req, res) => { return res.status(400).json({ error: 'สินค้าในสต็อกไม่เพียงพอ' }); } - // บันทึกการเคลื่อนไหว + await db.query( 'INSERT INTO stock_movements (product_id, quantity, type, user_id, description) VALUES (?, ?, "out", ?, ?)', [product_id, quantity, req.session.userId, description] ); - // อัพเดตจำนวนสต็อก + await db.query( 'UPDATE products SET stock_quantity = stock_quantity - ? WHERE id = ?', [quantity, product_id] @@ -69,7 +69,7 @@ router.post('/remove', async (req, res) => { } }); -// ดูประวัติการเคลื่อนไหวของสินค้า + router.get('/history/:productId', async (req, res) => { try { const [movements] = await db.query(` diff --git a/views/categories.ejs b/views/categories.ejs index 7dad304a86a31c33903bdb56522751578f45e7e4..210a4df71a42d6aa8f0be86dac567ba46aad38d5 100644 --- a/views/categories.ejs +++ b/views/categories.ejs @@ -99,69 +99,5 @@ } }; </script> - - <style> - .modal { - display: none; - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: rgba(0,0,0,0.5); - } - - .modal-content { - background-color: white; - margin: 15% auto; - padding: 20px; - border-radius: 5px; - width: 50%; - max-width: 500px; - } - - .category-form { - margin: 20px 0; - display: flex; - gap: 10px; - } - - .category-form input { - flex: 1; - padding: 8px; - border: 1px solid #ddd; - border-radius: 4px; - } - - table { - width: 100%; - border-collapse: collapse; - margin-top: 20px; - } - - th, td { - padding: 12px; - text-align: left; - border-bottom: 1px solid #ddd; - } - - .edit-btn, .delete-btn { - padding: 5px 10px; - margin: 0 5px; - border: none; - border-radius: 4px; - cursor: pointer; - } - - .edit-btn { - background-color: #ffc107; - color: black; - } - - .delete-btn { - background-color: #dc3545; - color: white; - } - </style> </body> </html> \ No newline at end of file diff --git a/views/products.ejs b/views/products.ejs index fd50d82ec0800c7d06d5e195d1a8fdb2ac8e639c..f371d38e374d0d9c9a2e0e6793055c03e6b7f190 100644 --- a/views/products.ejs +++ b/views/products.ejs @@ -4,7 +4,6 @@ <title>จัดการสินค้า</title> <link rel="stylesheet" href="/css/style.css"> <script> - // Define getStockStatus function first function getStockStatus(current, minimum) { current = parseInt(current) || 0; minimum = parseInt(minimum) || 0; @@ -21,7 +20,7 @@ </head> <body> <div class="container"> - <!-- ส่วนหัว --> + <div class="header"> <h1>จัดการสต็อกสินค้า</h1> <nav> @@ -31,7 +30,7 @@ </div> - <!-- แก้ไขส่วนค้นหา --> + <div class="search-section"> <form action="/products" method="GET" class="search-form"> <input @@ -47,7 +46,7 @@ </form> </div> - <!-- เพิ่มสินค้า --> + <div class="add-product-section"> <form action="/products" method="POST"> <div class="form-row"> @@ -185,10 +184,8 @@ </div> <script> - // ฟังก์ชันจัดการสินค้า const editModal = document.getElementById('editModal'); let currentProductId = null; - function editProduct(id) { currentProductId = id; fetch(`/products/${id}`) @@ -296,133 +293,17 @@ <td>${m.user_name}</td> <td>${m.description || '-'}</td> </tr> - `).join(''); - + `).join(''); document.getElementById('historyModal').style.display = 'block'; } catch (err) { console.error(err); alert('เกิดข้อผิดพลาด'); } } - function closeHistoryModal() { document.getElementById('historyModal').style.display = 'none'; } </script> - <style> - .container { padding: 20px; } - - .form-row { - display: flex; - gap: 10px; - margin-bottom: 10px; - } - - .status { - padding: 5px 10px; - border-radius: 4px; - font-weight: bold; - } - - .status-out { - background: #dc3545; - color: white; - } - - .status-low { - background: #ffc107; - color: black; - } - - .status-normal { - background: #28a745; - color: white; - } - - button { - padding: 5px 10px; - border-radius: 4px; - cursor: pointer; - } - - .modal { - display: none; - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: rgba(0,0,0,0.5); - z-index: 1000; - } - - .modal-content { - background-color: white; - margin: 15% auto; - padding: 20px; - width: 80%; - max-width: 600px; - border-radius: 5px; - position: relative; - } - - .form-actions { - display: flex; - gap: 10px; - justify-content: flex-end; - margin-top: 20px; - } - - .form-actions button { - padding: 8px 16px; - } - - .form-actions button[type="submit"] { - background-color: #28a745; - color: white; - border: none; - } - - .form-actions button[type="button"] { - background-color: #dc3545; - color: white; - border: none; - } - - .search-section { - margin-bottom: 20px; - } - - .search-form { - display: flex; - gap: 10px; - align-items: center; - } - - .search-form input { - padding: 8px; - border: 1px solid #ddd; - border-radius: 4px; - width: 300px; - } - - .search-form button { - padding: 8px 16px; - background: #007bff; - color: white; - border: none; - border-radius: 4px; - cursor: pointer; - } - - .clear-search { - padding: 8px 16px; - background: #6c757d; - color: white; - text-decoration: none; - border-radius: 4px; - } - </style> </body> </html> \ No newline at end of file