feat: enhance pool management with new properties and active status updates
This commit is contained in:
parent
8e450cd9c5
commit
4baafedc79
@ -3,13 +3,14 @@ const { createPool, listPools, updatePoolState } = require('../../services/pool/
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
async create(req, res) {
|
async create(req, res) {
|
||||||
try {
|
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;
|
const actorUserId = req.user && req.user.userId;
|
||||||
if (!name) return res.status(400).json({ success: false, message: 'name is required' });
|
if (!pool_name) return res.status(400).json({ success: false, message: 'Pool name is required' });
|
||||||
if (state && !['active', 'inactive'].includes(state)) {
|
if (!price || price < 0) return res.status(400).json({ success: false, message: 'Valid price is required' });
|
||||||
return res.status(400).json({ success: false, message: 'Invalid state. Allowed: active, inactive' });
|
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 });
|
return res.status(201).json({ success: true, data: pool });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e && (e.code === 'ER_DUP_ENTRY' || e.errno === 1062)) {
|
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)
|
// Update pool active status
|
||||||
async updateState(req, res) {
|
async updateActive(req, res) {
|
||||||
try {
|
try {
|
||||||
const { id } = req.params || {};
|
const { id } = req.params || {};
|
||||||
const { state } = req.body || {};
|
const { is_active } = req.body || {};
|
||||||
const actorUserId = req.user && req.user.userId;
|
const actorUserId = req.user && req.user.userId;
|
||||||
if (!id) return res.status(400).json({ success: false, message: 'id is required' });
|
if (!id) return res.status(400).json({ success: false, message: 'id is required' });
|
||||||
if (!['active', 'inactive', 'archived'].includes(state)) {
|
if (typeof is_active !== 'boolean') {
|
||||||
return res.status(400).json({ success: false, message: 'Invalid state. Allowed: active, inactive, archived' });
|
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 });
|
return res.status(200).json({ success: true, data: updated });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e && e.status === 400) {
|
if (e && e.status === 400) {
|
||||||
return res.status(400).json({ success: false, message: e.message });
|
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' });
|
return res.status(500).json({ success: false, message: 'Internal server error' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -560,6 +560,27 @@ async function createDatabase() {
|
|||||||
`);
|
`);
|
||||||
console.log('✅ Coffee table (simplified) created/verified');
|
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 ---
|
// --- Matrix: Global 5-ary tree config and relations ---
|
||||||
await connection.query(`
|
await connection.query(`
|
||||||
CREATE TABLE IF NOT EXISTS matrix_config (
|
CREATE TABLE IF NOT EXISTS matrix_config (
|
||||||
|
|||||||
@ -1,9 +1,11 @@
|
|||||||
class Pool {
|
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.id = id;
|
||||||
this.name = name;
|
this.pool_name = pool_name;
|
||||||
this.description = description;
|
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.created_by = created_by;
|
||||||
this.updated_by = updated_by;
|
this.updated_by = updated_by;
|
||||||
this.created_at = created_at;
|
this.created_at = created_at;
|
||||||
|
|||||||
@ -5,14 +5,14 @@ class PoolRepository {
|
|||||||
this.uow = uow;
|
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 conn = this.uow.connection;
|
||||||
const [res] = await conn.execute(
|
const [res] = await conn.execute(
|
||||||
`INSERT INTO pools (name, description, state, created_by, updated_by)
|
`INSERT INTO pools (pool_name, description, price, pool_type, is_active, created_by)
|
||||||
VALUES (?, ?, ?, ?, NULL)`,
|
VALUES (?, ?, ?, ?, ?, ?)`,
|
||||||
[name, description, state, created_by]
|
[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() {
|
async findAll() {
|
||||||
@ -20,7 +20,7 @@ class PoolRepository {
|
|||||||
try {
|
try {
|
||||||
console.debug('[PoolRepository.findAll] querying pools');
|
console.debug('[PoolRepository.findAll] querying pools');
|
||||||
const [rows] = await conn.execute(
|
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
|
FROM pools
|
||||||
ORDER BY created_at DESC`
|
ORDER BY created_at DESC`
|
||||||
);
|
);
|
||||||
@ -36,30 +36,24 @@ class PoolRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NEW: update state with enforced transitions (active <-> inactive -> archived also supported)
|
// Update is_active flag (replaces old state transitions)
|
||||||
async updateState(id, nextState, updated_by) {
|
async updateActive(id, is_active, updated_by = null) {
|
||||||
const conn = this.uow.connection;
|
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) {
|
if (!rows || rows.length === 0) {
|
||||||
const err = new Error('Pool not found');
|
const err = new Error('Pool not found');
|
||||||
err.status = 404;
|
err.status = 404;
|
||||||
throw err;
|
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(
|
await conn.execute(
|
||||||
`UPDATE pools SET state = ?, updated_by = ?, updated_at = NOW() WHERE id = ?`,
|
`UPDATE pools SET is_active = ?, updated_by = ?, updated_at = NOW() WHERE id = ?`,
|
||||||
[nextState, updated_by, 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]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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);
|
router.patch('/admin/update-user-status/:id', authMiddleware, adminOnly, AdminUserController.updateUserStatus);
|
||||||
// Admin: set state for coffee product
|
// Admin: set state for coffee product
|
||||||
router.patch('/admin/coffee/:id/state', authMiddleware, adminOnly, CoffeeController.setState);
|
router.patch('/admin/coffee/:id/state', authMiddleware, adminOnly, CoffeeController.setState);
|
||||||
// NEW: Admin pool state update
|
// NEW: Admin pool active status update
|
||||||
router.patch('/admin/pools/:id/state', authMiddleware, adminOnly, PoolController.updateState);
|
router.patch('/admin/pools/:id/active', authMiddleware, adminOnly, PoolController.updateActive);
|
||||||
// NEW: deactivate a matrix instance (admin-only)
|
// NEW: deactivate a matrix instance (admin-only)
|
||||||
router.patch('/admin/matrix/:id/deactivate', authMiddleware, adminOnly, MatrixController.deactivate);
|
router.patch('/admin/matrix/:id/deactivate', authMiddleware, adminOnly, MatrixController.deactivate);
|
||||||
// NEW: activate a matrix instance (admin-only)
|
// NEW: activate a matrix instance (admin-only)
|
||||||
|
|||||||
@ -1,22 +1,22 @@
|
|||||||
const UnitOfWork = require('../../database/UnitOfWork');
|
const UnitOfWork = require('../../database/UnitOfWork');
|
||||||
const PoolRepository = require('../../repositories/pool/poolRepository');
|
const PoolRepository = require('../../repositories/pool/poolRepository');
|
||||||
|
|
||||||
function isValidState(state) {
|
function isValidPoolType(pool_type) {
|
||||||
return state === undefined || state === null || state === 'active' || state === 'inactive' || state === 'archived';
|
return pool_type === 'coffee' || pool_type === 'other';
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createPool({ name, description = null, state = 'active', actorUserId }) {
|
async function createPool({ pool_name, description = null, price = 0.00, pool_type = 'other', is_active = true, created_by = null }) {
|
||||||
if (!isValidState(state)) {
|
if (!isValidPoolType(pool_type)) {
|
||||||
const err = new Error('Invalid state. Allowed: active, inactive, archived');
|
const err = new Error('Invalid pool_type. Allowed: coffee, other');
|
||||||
err.status = 400;
|
err.status = 400;
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
const uow = new UnitOfWork();
|
const uow = new UnitOfWork();
|
||||||
try {
|
try {
|
||||||
console.debug('[PoolService.createPool] start', { name, state });
|
console.debug('[PoolService.createPool] start', { pool_name, pool_type });
|
||||||
await uow.start();
|
await uow.start();
|
||||||
const repo = new PoolRepository(uow);
|
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();
|
await uow.commit();
|
||||||
console.debug('[PoolService.createPool] success', { id: pool.id });
|
console.debug('[PoolService.createPool] success', { id: pool.id });
|
||||||
return pool;
|
return pool;
|
||||||
@ -45,20 +45,20 @@ async function listPools() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updatePoolState(id, nextState, actorUserId) {
|
async function updatePoolState(id, is_active, actorUserId) {
|
||||||
if (!isValidState(nextState) || !['active', 'inactive', 'archived'].includes(nextState)) {
|
if (typeof is_active !== 'boolean') {
|
||||||
const err = new Error('Invalid state. Allowed: active, inactive, archived');
|
const err = new Error('is_active must be a boolean');
|
||||||
err.status = 400;
|
err.status = 400;
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
const uow = new UnitOfWork();
|
const uow = new UnitOfWork();
|
||||||
try {
|
try {
|
||||||
console.debug('[PoolService.updatePoolState] start', { id, nextState });
|
console.debug('[PoolService.updatePoolState] start', { id, is_active });
|
||||||
await uow.start();
|
await uow.start();
|
||||||
const repo = new PoolRepository(uow);
|
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();
|
await uow.commit();
|
||||||
console.debug('[PoolService.updatePoolState] success', { id, state: nextState });
|
console.debug('[PoolService.updatePoolState] success', { id, is_active });
|
||||||
return updated;
|
return updated;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[PoolService.updatePoolState] error', err);
|
console.error('[PoolService.updatePoolState] error', err);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user