add: CRUD tags

This commit is contained in:
Muhammad Afif
2025-10-10 20:06:56 +07:00
parent ee30308112
commit 425b1ed554
8 changed files with 358 additions and 122 deletions

View File

@@ -0,0 +1,71 @@
const TagsService = require('../services/tags.service');
const { setResponse, setResponsePaging, checkValidate } = require('../helpers/utils');
const { insertTagsSchema, updateTagsSchema } = require('../validate/tags.schema');
class TagsController {
// Get all devices
static async getAll(req, res) {
const queryParams = req.query;
const results = await TagsService.getAllTags(queryParams);
const response = await setResponsePaging(queryParams, results, 'Tags found')
res.status(response.statusCode).json(response);
}
// Get device by ID
static async getById(req, res) {
const { id } = req.params;
const results = await TagsService.getTagByID(id);
const response = await setResponse(results, 'Tags found')
res.status(response.statusCode).json(response);
}
// Create device
static async create(req, res) {
const { error, value } = await checkValidate(insertTagsSchema, req)
if (error) {
return res.status(400).json(setResponse(error, 'Validation failed', 400));
}
value.userId = req.user.user_id
const results = await TagsService.createTags(value);
const response = await setResponse(results, 'Tags created successfully')
return res.status(response.statusCode).json(response);
}
// Update device
static async update(req, res) {
const { id } = req.params;
const { error, value } = checkValidate(updateTagsSchema, req)
if (error) {
return res.status(400).json(setResponse(error, 'Validation failed', 400));
}
value.userId = req.user.user_id
const results = await TagsService.updateTags(id, value);
const response = await setResponse(results, 'Tags updated successfully')
res.status(response.statusCode).json(response);
}
// Soft delete device
static async delete(req, res) {
const { id } = req.params;
const results = await TagsService.deleteTags(id, req.user.user_id);
const response = await setResponse(results, 'Tags deleted successfully')
res.status(response.statusCode).json(response);
}
}
module.exports = TagsController;

View File

@@ -1,116 +0,0 @@
const { query, buildFilterQuery, buildDynamicUpdate } = require("../config");
// Get all tags
const getAllTagsDb = async (searchParams = {}) => {
const { whereConditions, queryParams } = buildFilterQuery([
{ column: "mt.tag_name", param: searchParams.name, type: "string" },
{ column: "mt.tag_code", param: searchParams.code, type: "string" },
{
column: "md.device_name",
param: searchParams.deviceName,
type: "string",
},
{
column: "pss.sub_section_name",
param: searchParams.subSectionName,
type: "string",
},
]);
const whereClause = whereConditions.length
? `AND ${whereConditions.join(" AND ")}`
: "";
const queryText = `
SELECT
mt.tag_id, mt.device_id, mt.tag_code, mt.tag_name, mt.tag_number,
mt.data_type, mt.unit, mt.is_active, mt.sub_section_id,
mt.created_at, mt.updated_at, mt.deleted_at,
md.device_name,md.ip_address,
pss.sub_section_code, pss.sub_section_name
FROM m_tags mt
INNER JOIN m_device md ON mt.device_id = md.device_id
INNER JOIN plant_sub_section pss ON mt.sub_section_id = pss.sub_section_id
WHERE mt.deleted_at IS NULL ${whereClause}
ORDER BY mt.tag_id ASC
`;
const result = await query(queryText, queryParams);
return result.recordset;
};
// Get tag by ID
const getTagByIdDb = async (id) => {
const queryText = `
SELECT
mt.tag_id, mt.device_id, mt.tag_code, mt.tag_name, mt.tag_number,
mt.data_type, mt.unit, mt.is_active, mt.sub_section_id,
mt.created_at, mt.updated_at, mt.deleted_at,
md.device_name,
pss.sub_section_code, pss.sub_section_name
FROM m_tags mt
LEFT JOIN m_device md ON mt.device_id = md.device_id
LEFT JOIN plant_sub_section pss ON mt.sub_section_id = pss.sub_section_id
WHERE mt.tag_id = $1 AND mt.deleted_at IS NULL
`;
const result = await query(queryText, [id]);
return result.recordset[0];
};
// Create tag
const createTagDb = async (data) => {
const queryText = `
INSERT INTO m_tags
(device_id, tag_code, tag_name, tag_number, data_type, unit, is_active, sub_section_id, created_by)
VALUES
($1,$2,$3,$4,$5,$6,$7,$8,$9);
SELECT SCOPE_IDENTITY() as tag_id;
`;
const values = [
data.device_id,
data.tag_code,
data.tag_name,
data.tag_number,
data.data_type,
data.unit,
data.is_active || 1, //default aktif
data.sub_section_id,
data.created_by,
];
const result = await query(queryText, values);
return result.recordset[0]?.tag_id;
};
const updateTagDb = async (tagId, data) => {
const { query: queryText, values } = buildDynamicUpdate("m_tags", data, {
tag_id: tagId,
updated_at: "GETDATE()",
});
const finalQuery = queryText.replace("WHERE", "WHERE deleted_at IS NULL AND");
await query(finalQuery, values);
return true;
};
const deleteTagDb = async (tagId, deletedBy) => {
const queryText = `
UPDATE m_tags
SET
deleted_at = GETDATE(),
deleted_by = $1,
is_active = 0
WHERE tag_id = $2
AND deleted_at IS NULL
`;
await query(queryText, [deletedBy, tagId]);
return true;
};
module.exports = {
getAllTagsDb,
getTagByIdDb,
createTagDb,
updateTagDb,
deleteTagDb,
};

144
db/tags.db.js Normal file
View File

@@ -0,0 +1,144 @@
const pool = require("../config");
// Get all tags
const getAllTagsDb = async (searchParams = {}) => {
let queryParams = [];
if (searchParams.limit) {
const page = Number(searchParams.page ?? 1) - 1;
queryParams = [Number(searchParams.limit ?? 10), page];
}
const { whereOrConditions, whereParamOr } = pool.buildStringOrIlike(
[
"a.tag_name",
"a.tag_code",
"a.tag_number",
"a.data_type",
"a.unit",
"b.device_name",
"c.sub_section_name",
],
searchParams.criteria,
queryParams
);
if (whereParamOr) queryParams = whereParamOr;
// Filter tambahan (AND conditions)
const { whereConditions, whereParamAnd } = pool.buildFilterQuery(
[
{ column: "a.tag_name", param: searchParams.name, type: "string" },
{ column: "a.tag_code", param: searchParams.code, type: "string" },
{ column: "a.data_type", param: searchParams.data, type: "string" },
{ column: "a.unit", param: searchParams.unit, type: "string" },
{ column: "b.device_name", param: searchParams.device, type: "string" },
{
column: "b.device_description",
param: searchParams.device,
type: "string",
},
{ column: "b.ip_address", param: searchParams.device, type: "string" },
{
column: "c.sub_section_name",
param: searchParams.subsection,
type: "string",
},
],
queryParams
);
if (whereParamAnd) queryParams = whereParamAnd;
const queryText = `
SELECT
COUNT(*) OVER() AS total_data,
a.*,
b.device_name,
b.device_location,
b.device_description,
c.sub_section_name
FROM m_tags a
LEFT JOIN m_device b ON a.device_id = b.device_id
LEFT JOIN plant_sub_section c ON a.sub_section_id = c.sub_section_id
WHERE a.deleted_at IS NULL
${whereConditions.length > 0 ? ` AND ${whereConditions.join(" AND ")}` : ""}
${whereOrConditions ? ` ${whereOrConditions}` : ""}
ORDER BY a.tag_id ASC
${searchParams.limit ? `OFFSET $2 ROWS FETCH NEXT $1 ROWS ONLY` : ""}
`;
const result = await pool.query(queryText, queryParams);
const total =
result?.recordset?.length > 0
? parseInt(result.recordset[0].total_data, 10)
: 0;
return { data: result.recordset, total };
};
const getTagsByIdDb = async (id) => {
const queryText = `
SELECT
a.*,
b.device_name,
b.device_location,
b.device_description,
c.sub_section_name
FROM m_tags a
LEFT JOIN m_device b ON a.device_id = b.device_id
LEFT JOIN plant_sub_section c ON a.sub_section_id = c.sub_section_id
WHERE a.tag_id = $1 AND a.deleted_at IS NULL
`;
const result = await pool.query(queryText, [id]);
return result.recordset;
};
const createTagsDb = async (data) => {
const newCode = await pool.generateKode("TAG", "m_tags", "tag_code");
const store = {
...data,
tag_code: newCode,
};
const { query: queryText, values } = pool.buildDynamicInsert("m_tags", store);
const result = await pool.query(queryText, values);
const insertedId = result.recordset[0]?.inserted_id;
return insertedId ? await getTagsByIdDb(insertedId) : null;
};
const updateTagsDb = async (id, data) => {
const store = { ...data };
const whereData = { tag_id: id };
const { query: queryText, values } = pool.buildDynamicUpdate(
"m_tags",
store,
whereData
);
await pool.query(`${queryText} AND deleted_at IS NULL`, values);
return getTagsByIdDb(id);
};
// Soft delete tag
const deleteTagsDb = async (id, deletedBy) => {
const queryText = `
UPDATE m_tags
SET deleted_at = CURRENT_TIMESTAMP, deleted_by = $1
WHERE tag_id = $2 AND deleted_at IS NULL
`;
await pool.query(queryText, [deletedBy, id]);
return true;
};
module.exports = {
getAllTagsDb,
getTagsByIdDb,
createTagsDb,
updateTagsDb,
deleteTagsDb,
};

View File

@@ -3,10 +3,12 @@ const auth = require("./auth.route");
const users = require("./users.route");
const device = require('./device.route');
const roles = require('./roles.route')
const tags = require("./tags.route")
router.use("/auth", auth);
router.use("/user", users);
router.use("/device", device);
router.use("/roles", roles);
router.use("/tags", tags)
module.exports = router;

View File

@@ -1,17 +1,17 @@
const express = require('express');
const Rolesontroller = require('../controllers/roles.controller');
const RolesController = require('../controllers/roles.controller');
const verifyToken = require("../middleware/verifyToken")
const verifyAccess = require("../middleware/verifyAccess")
const router = express.Router();
router.route("/")
.get(verifyToken.verifyAccessToken, Rolesontroller.getAll)
.post(verifyToken.verifyAccessToken, verifyAccess(), Rolesontroller.create);
.get(verifyToken.verifyAccessToken, RolesController.getAll)
.post(verifyToken.verifyAccessToken, verifyAccess(), RolesController.create);
router.route("/:id")
.get(verifyToken.verifyAccessToken, Rolesontroller.getById)
.put(verifyToken.verifyAccessToken, verifyAccess(), Rolesontroller.update)
.delete(verifyToken.verifyAccessToken, verifyAccess(), Rolesontroller.delete);
.get(verifyToken.verifyAccessToken, RolesController.getById)
.put(verifyToken.verifyAccessToken, verifyAccess(), RolesController.update)
.delete(verifyToken.verifyAccessToken, verifyAccess(), RolesController.delete);
module.exports = router;

17
routes/tags.route.js Normal file
View File

@@ -0,0 +1,17 @@
const express = require('express');
const TagsController = require('../controllers/tags.controller');
const verifyToken = require("../middleware/verifyToken")
const verifyAccess = require("../middleware/verifyAccess")
const router = express.Router();
router.route("/")
.get(verifyToken.verifyAccessToken, TagsController.getAll)
.post(verifyToken.verifyAccessToken, verifyAccess(), TagsController.create);
router.route("/:id")
.get(verifyToken.verifyAccessToken, TagsController.getById)
.put(verifyToken.verifyAccessToken, verifyAccess(), TagsController.update)
.delete(verifyToken.verifyAccessToken, verifyAccess(), TagsController.delete);
module.exports = router;

89
services/tags.service.js Normal file
View File

@@ -0,0 +1,89 @@
const {
getAllTagsDb,
getTagsByIdDb,
createTagsDb,
updateTagsDb,
deleteTagsDb
} = require('../db/tags.db');
const { ErrorHandler } = require('../helpers/error');
class TagsService {
// Get all devices
static async getAllTags(param) {
try {
const results = await getAllTagsDb(param);
results.data.map(element => {
});
return results
} catch (error) {
throw new ErrorHandler(error.statusCode, error.message);
}
}
// Get device by ID
static async getTagByID(id) {
try {
const result = await getTagsByIdDb(id);
if (result.length < 1) throw new ErrorHandler(404, 'Tags not found');
return result;
} catch (error) {
throw new ErrorHandler(error.statusCode, error.message);
}
}
// Create device
static async createTags(data) {
try {
if (!data || typeof data !== 'object') data = {};
const result = await createTagsDb(data);
return result;
} catch (error) {
throw new ErrorHandler(error.statusCode, error.message);
}
}
// Update device
static async updateTags(id, data) {
try {
if (!data || typeof data !== 'object') data = {};
const dataExist = await getTagsByIdDb(id);
if (dataExist.length < 1) {
throw new ErrorHandler(404, 'Tags not found');
}
const result = await updateTagsDb(id, data);
return result;
} catch (error) {
throw new ErrorHandler(error.statusCode, error.message);
}
}
// Soft delete device
static async deleteTags(id, userId) {
try {
const dataExist = await getTagsByIdDb(id);
if (dataExist.length < 1) {
throw new ErrorHandler(404, 'Tags not found');
}
const result = await deleteTagsDb(id, userId);
return result;
} catch (error) {
throw new ErrorHandler(error.statusCode, error.message);
}
}
}
module.exports = TagsService;

29
validate/tags.schema.js Normal file
View File

@@ -0,0 +1,29 @@
// ========================
// Device Validation
const Joi = require("joi");
// ========================
const insertTagsSchema = Joi.object({
device_id: Joi.number().required(),
tag_name: Joi.string().max(200).required(),
tag_number: Joi.number().required(),
is_active: Joi.boolean().required(),
data_type: Joi.string().max(50).required(),
unit: Joi.string().max(50).required(),
});
const updateTagsSchema = Joi.object({
device_id: Joi.number(),
tag_name: Joi.string().max(200),
tag_number: Joi.number(),
is_active: Joi.boolean(),
data_type: Joi.string().max(50),
unit: Joi.string().max(50),
}).min(1);
// ✅ Export dengan CommonJS
module.exports = {
insertTagsSchema,
updateTagsSchema,
};