diff --git a/controller/pool/PoolController.js b/controller/pool/PoolController.js index 247e085..0621239 100644 --- a/controller/pool/PoolController.js +++ b/controller/pool/PoolController.js @@ -3,13 +3,14 @@ const { createPool, listPools, updatePoolState } = require('../../services/pool/ module.exports = { async create(req, res) { try { - const { name, description, state } = req.body || {}; + const { pool_name, description, price, pool_type, is_active } = req.body || {}; const actorUserId = req.user && req.user.userId; - if (!name) return res.status(400).json({ success: false, message: 'name is required' }); - if (state && !['active', 'inactive'].includes(state)) { - return res.status(400).json({ success: false, message: 'Invalid state. Allowed: active, inactive' }); + if (!pool_name) return res.status(400).json({ success: false, message: 'Pool name is required' }); + if (!price || price < 0) return res.status(400).json({ success: false, message: 'Valid price is required' }); + if (pool_type && !['coffee', 'other'].includes(pool_type)) { + return res.status(400).json({ success: false, message: 'Invalid pool_type. Allowed: coffee, other' }); } - const pool = await createPool({ name, description, state, actorUserId }); + const pool = await createPool({ pool_name, description, price, pool_type, is_active, created_by: actorUserId }); return res.status(201).json({ success: true, data: pool }); } catch (e) { if (e && (e.code === 'ER_DUP_ENTRY' || e.errno === 1062)) { @@ -34,23 +35,23 @@ module.exports = { } }, - // NEW: optional state update handler (route can be added later) - async updateState(req, res) { + // Update pool active status + async updateActive(req, res) { try { const { id } = req.params || {}; - const { state } = req.body || {}; + const { is_active } = req.body || {}; const actorUserId = req.user && req.user.userId; if (!id) return res.status(400).json({ success: false, message: 'id is required' }); - if (!['active', 'inactive', 'archived'].includes(state)) { - return res.status(400).json({ success: false, message: 'Invalid state. Allowed: active, inactive, archived' }); + if (typeof is_active !== 'boolean') { + return res.status(400).json({ success: false, message: 'is_active must be a boolean' }); } - const updated = await updatePoolState(id, state, actorUserId); + const updated = await updatePoolState(id, is_active, actorUserId); return res.status(200).json({ success: true, data: updated }); } catch (e) { if (e && e.status === 400) { return res.status(400).json({ success: false, message: e.message }); } - console.error('[PoolController.updateState]', e); + console.error('[PoolController.updateActive]', e); return res.status(500).json({ success: false, message: 'Internal server error' }); } } diff --git a/database/createDb.js b/database/createDb.js index 656cd92..0fc0b5b 100644 --- a/database/createDb.js +++ b/database/createDb.js @@ -560,6 +560,27 @@ async function createDatabase() { `); console.log('✅ Coffee table (simplified) created/verified'); + // --- Pools Table --- + await connection.query(` + CREATE TABLE IF NOT EXISTS pools ( + id INT AUTO_INCREMENT PRIMARY KEY, + pool_name VARCHAR(255) NOT NULL, + description TEXT, + price DECIMAL(10,2) NOT NULL DEFAULT 0.00, + pool_type ENUM('coffee', 'other') NOT NULL DEFAULT 'other', + is_active BOOLEAN DEFAULT TRUE, + created_by INT NULL, + updated_by INT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL ON UPDATE CASCADE, + FOREIGN KEY (updated_by) REFERENCES users(id) ON DELETE SET NULL ON UPDATE CASCADE, + INDEX idx_pool_type (pool_type), + INDEX idx_is_active (is_active) + ); + `); + console.log('✅ Pools table created/verified'); + // --- Matrix: Global 5-ary tree config and relations --- await connection.query(` CREATE TABLE IF NOT EXISTS matrix_config ( diff --git a/models/Pool.js b/models/Pool.js index 891fa98..dc58594 100644 --- a/models/Pool.js +++ b/models/Pool.js @@ -1,9 +1,11 @@ class Pool { - constructor({ id = null, name, description = null, state = 'active', created_by = null, updated_by = null, created_at = null, updated_at = null }) { + constructor({ id = null, pool_name, description = null, price = 0.00, pool_type = 'other', is_active = true, created_by = null, updated_by = null, created_at = null, updated_at = null }) { this.id = id; - this.name = name; + this.pool_name = pool_name; this.description = description; - this.state = state; + this.price = price; + this.pool_type = pool_type; + this.is_active = is_active; this.created_by = created_by; this.updated_by = updated_by; this.created_at = created_at; diff --git a/repositories/pool/poolRepository.js b/repositories/pool/poolRepository.js index cc28251..95f09fd 100644 --- a/repositories/pool/poolRepository.js +++ b/repositories/pool/poolRepository.js @@ -5,14 +5,14 @@ class PoolRepository { this.uow = uow; } - async create({ name, description = null, state = 'active', created_by }) { + async create({ pool_name, description = null, price = 0.00, pool_type = 'other', is_active = true, created_by = null }) { const conn = this.uow.connection; const [res] = await conn.execute( - `INSERT INTO pools (name, description, state, created_by, updated_by) - VALUES (?, ?, ?, ?, NULL)`, - [name, description, state, created_by] + `INSERT INTO pools (pool_name, description, price, pool_type, is_active, created_by) + VALUES (?, ?, ?, ?, ?, ?)`, + [pool_name, description, price, pool_type, is_active, created_by] ); - return new Pool({ id: res.insertId, name, description, state, created_by, updated_by: null }); + return new Pool({ id: res.insertId, pool_name, description, price, pool_type, is_active, created_by }); } async findAll() { @@ -20,7 +20,7 @@ class PoolRepository { try { console.debug('[PoolRepository.findAll] querying pools'); const [rows] = await conn.execute( - `SELECT id, name, description, state, created_at, updated_at + `SELECT id, pool_name, description, price, pool_type, is_active, created_by, updated_by, created_at, updated_at FROM pools ORDER BY created_at DESC` ); @@ -36,30 +36,24 @@ class PoolRepository { } } - // NEW: update state with enforced transitions (active <-> inactive -> archived also supported) - async updateState(id, nextState, updated_by) { + // Update is_active flag (replaces old state transitions) + async updateActive(id, is_active, updated_by = null) { const conn = this.uow.connection; - const [rows] = await conn.execute(`SELECT id, state FROM pools WHERE id = ?`, [id]); + const [rows] = await conn.execute(`SELECT id FROM pools WHERE id = ?`, [id]); if (!rows || rows.length === 0) { const err = new Error('Pool not found'); err.status = 404; throw err; } - const current = rows[0].state; - const allowed = ['active', 'inactive', 'archived']; - if (!allowed.includes(current) || !allowed.includes(nextState)) { - const err = new Error('Invalid state transition'); - err.status = 400; - throw err; - } - if (current === nextState) { - return new Pool({ id, state: current, updated_by }); // no-op - } await conn.execute( - `UPDATE pools SET state = ?, updated_by = ?, updated_at = NOW() WHERE id = ?`, - [nextState, updated_by, id] + `UPDATE pools SET is_active = ?, updated_by = ?, updated_at = NOW() WHERE id = ?`, + [is_active, updated_by, id] ); - return new Pool({ id, state: nextState, updated_by }); + const [updated] = await conn.execute( + `SELECT id, pool_name, description, price, pool_type, is_active, created_by, updated_by, created_at, updated_at FROM pools WHERE id = ?`, + [id] + ); + return new Pool(updated[0]); } } diff --git a/routes/patchRoutes.js b/routes/patchRoutes.js index 985c960..f69a047 100644 --- a/routes/patchRoutes.js +++ b/routes/patchRoutes.js @@ -38,8 +38,8 @@ router.patch('/admin/update-user-profile/:id', authMiddleware, adminOnly, AdminU router.patch('/admin/update-user-status/:id', authMiddleware, adminOnly, AdminUserController.updateUserStatus); // Admin: set state for coffee product router.patch('/admin/coffee/:id/state', authMiddleware, adminOnly, CoffeeController.setState); -// NEW: Admin pool state update -router.patch('/admin/pools/:id/state', authMiddleware, adminOnly, PoolController.updateState); +// NEW: Admin pool active status update +router.patch('/admin/pools/:id/active', authMiddleware, adminOnly, PoolController.updateActive); // NEW: deactivate a matrix instance (admin-only) router.patch('/admin/matrix/:id/deactivate', authMiddleware, adminOnly, MatrixController.deactivate); // NEW: activate a matrix instance (admin-only) diff --git a/services/pool/PoolService.js b/services/pool/PoolService.js index 1bf8a74..2dc35e4 100644 --- a/services/pool/PoolService.js +++ b/services/pool/PoolService.js @@ -1,22 +1,22 @@ const UnitOfWork = require('../../database/UnitOfWork'); const PoolRepository = require('../../repositories/pool/poolRepository'); -function isValidState(state) { - return state === undefined || state === null || state === 'active' || state === 'inactive' || state === 'archived'; +function isValidPoolType(pool_type) { + return pool_type === 'coffee' || pool_type === 'other'; } -async function createPool({ name, description = null, state = 'active', actorUserId }) { - if (!isValidState(state)) { - const err = new Error('Invalid state. Allowed: active, inactive, archived'); +async function createPool({ pool_name, description = null, price = 0.00, pool_type = 'other', is_active = true, created_by = null }) { + if (!isValidPoolType(pool_type)) { + const err = new Error('Invalid pool_type. Allowed: coffee, other'); err.status = 400; throw err; } const uow = new UnitOfWork(); try { - console.debug('[PoolService.createPool] start', { name, state }); + console.debug('[PoolService.createPool] start', { pool_name, pool_type }); await uow.start(); const repo = new PoolRepository(uow); - const pool = await repo.create({ name, description, state, created_by: actorUserId }); + const pool = await repo.create({ pool_name, description, price, pool_type, is_active, created_by }); await uow.commit(); console.debug('[PoolService.createPool] success', { id: pool.id }); return pool; @@ -45,20 +45,20 @@ async function listPools() { } } -async function updatePoolState(id, nextState, actorUserId) { - if (!isValidState(nextState) || !['active', 'inactive', 'archived'].includes(nextState)) { - const err = new Error('Invalid state. Allowed: active, inactive, archived'); +async function updatePoolState(id, is_active, actorUserId) { + if (typeof is_active !== 'boolean') { + const err = new Error('is_active must be a boolean'); err.status = 400; throw err; } const uow = new UnitOfWork(); try { - console.debug('[PoolService.updatePoolState] start', { id, nextState }); + console.debug('[PoolService.updatePoolState] start', { id, is_active }); await uow.start(); const repo = new PoolRepository(uow); - const updated = await repo.updateState(id, nextState, actorUserId); + const updated = await repo.updateActive(id, is_active, actorUserId); await uow.commit(); - console.debug('[PoolService.updatePoolState] success', { id, state: nextState }); + console.debug('[PoolService.updatePoolState] success', { id, is_active }); return updated; } catch (err) { console.error('[PoolService.updatePoolState] error', err);