From 662038d9532a9874cb4d82d1dc47c73d7bdd8209 Mon Sep 17 00:00:00 2001 From: Muhammad Afif Date: Mon, 13 Oct 2025 10:54:39 +0700 Subject: [PATCH] add: CRUD shift --- controllers/shift.controller.js | 71 ++++++++++++++++++++ db/schedule.db.js | 8 ++- db/shift.db.js | 112 ++++++++++++++++++++++++++++++++ routes/index.js | 3 + routes/shift.route.js | 17 +++++ services/shift.service.js | 88 +++++++++++++++++++++++++ utils/time.js | 11 ++++ validate/shift.schema.js | 42 ++++++++++++ 8 files changed, 350 insertions(+), 2 deletions(-) create mode 100644 utils/time.js diff --git a/controllers/shift.controller.js b/controllers/shift.controller.js index e69de29..ce5ae5c 100644 --- a/controllers/shift.controller.js +++ b/controllers/shift.controller.js @@ -0,0 +1,71 @@ +const ShiftService = require('../services/shift.service'); +const { setResponse, setResponsePaging, checkValidate } = require('../helpers/utils'); +const { updateShiftSchema, insertShiftSchema } = require('../validate/shift.schema'); + +class ShiftController { + // Get all Shift + static async getAll(req, res) { + const queryParams = req.query; + + const results = await ShiftService.getAllShift(queryParams); + const response = await setResponsePaging(queryParams, results, 'Shift found') + + res.status(response.statusCode).json(response); + } + + // Get Shift by ID + static async getById(req, res) { + const { id } = req.params; + + const results = await ShiftService.getShiftById(id); + const response = await setResponse(results, 'Shift found') + + res.status(response.statusCode).json(response); + } + + // Create Shift + static async create(req, res) { + const { error, value } = await checkValidate(insertShiftSchema, req) + + if (error) { + return res.status(400).json(setResponse(error, 'Validation failed', 400)); + } + + value.userId = req.user.user_id + + const results = await ShiftService.createShift(value); + const response = await setResponse(results, 'Shift created successfully') + + return res.status(response.statusCode).json(response); + } + + // Update Shift + static async update(req, res) { + const { id } = req.params; + + const { error, value } = checkValidate(updateShiftSchema, req) + + if (error) { + return res.status(400).json(setResponse(error, 'Validation failed', 400)); + } + + value.userId = req.user.user_id + + const results = await ShiftService.updateShift(id, value); + const response = await setResponse(results, 'Shift updated successfully') + + res.status(response.statusCode).json(response); + } + + // Soft delete Shift + static async delete(req, res) { + const { id } = req.params; + + const results = await ShiftService.deleteShift(id, req.user.user_id); + const response = await setResponse(results, 'Shift deleted successfully') + + res.status(response.statusCode).json(response); + } +} + +module.exports = ShiftController; diff --git a/db/schedule.db.js b/db/schedule.db.js index 294d155..8c0df04 100644 --- a/db/schedule.db.js +++ b/db/schedule.db.js @@ -27,8 +27,12 @@ const getAllScheduleDb = async (searchParams = {}) => { SELECT COUNT(*) OVER() AS total_data, a.*, - b.user_id, - c.shift_id + b.shift_id, + b.shift_name, + b.start_time, + b.end_time, + c.user_schedule_id, + c.user_id FROM schedule a LEFT JOIN m_shift b ON a.shift_id = b.shift_id LEFT JOIN user_schedule c ON a.schedule_id = c.user_schedule_id diff --git a/db/shift.db.js b/db/shift.db.js index e69de29..fd62f5c 100644 --- a/db/shift.db.js +++ b/db/shift.db.js @@ -0,0 +1,112 @@ +const pool = require("../config"); + +// Get all roles +const getAllShiftDb = async (searchParams = {}) => { + let queryParams = []; + + // Pagination + if (searchParams.limit) { + const page = Number(searchParams.page ?? 1) - 1; + queryParams = [Number(searchParams.limit ?? 10), page]; + } + + // Filtering + const { whereConditions, whereParamAnd } = pool.buildFilterQuery( + [ + { column: "a.shift_name", param: searchParams.shift_name, type: "string" }, + { + column: "a.start_time", + param: searchParams.start_time, + type: "time", + }, + { + column: "a.end_time", + param: searchParams.end_time, + type: "time", + }, + ], + queryParams + ); + + queryParams = whereParamAnd ? whereParamAnd : queryParams; + + const queryText = ` + SELECT + COUNT(*) OVER() AS total_data, + a.* + FROM m_shift a + WHERE a.deleted_at IS NULL + ${whereConditions.length > 0 ? `AND ${whereConditions.join(" AND ")}` : ""} + ORDER BY a.shift_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 }; +}; + +// Get role by ID +const getShiftByIdDb = async (id) => { + const queryText = ` + SELECT + a.* + FROM m_shift a + WHERE a.shift_id = $1 AND a.deleted_at IS NULL + `; + const result = await pool.query(queryText, [id]); + return result.recordset; +}; + +// Create role +const createShiftDB = async (data) => { + const store = { ...data }; + + const { query: queryText, values } = pool.buildDynamicInsert( + "m_shift", + store + ); + const result = await pool.query(queryText, values); + + const insertedId = result.recordset[0]?.inserted_id; + return insertedId ? await getShiftByIdDb(insertedId) : null; +}; + +// Update role +const updateShiftDb = async (id, data) => { + const store = { ...data }; + const whereData = { shift_id: id }; + + const { query: queryText, values } = pool.buildDynamicUpdate( + "m_shift", + store, + whereData + ); + await pool.query(`${queryText} AND deleted_at IS NULL`, values); + + return getShiftByIdDb(id); +}; + +const deleteShiftDb = async (id, deletedBy) => { + const queryText = ` + UPDATE m_shift + SET deleted_at = CURRENT_TIMESTAMP, + deleted_by = $1 + WHERE shift_id = $2 AND deleted_at IS NULL + `; + await pool.query(queryText, [deletedBy, id]); + return true; +}; + +module.exports = { + getAllShiftDb, + getShiftByIdDb, + createShiftDB, + updateShiftDb, + deleteShiftDb, +}; diff --git a/routes/index.js b/routes/index.js index f3480a7..7e351bb 100644 --- a/routes/index.js +++ b/routes/index.js @@ -5,6 +5,7 @@ const device = require('./device.route'); const roles = require('./roles.route') const tags = require("./tags.route") const subSection = require("./sub_section.route") +const shift = require("./shift.route") router.use("/auth", auth); router.use("/user", users); @@ -12,5 +13,7 @@ router.use("/device", device); router.use("/roles", roles); router.use("/tags", tags); router.use("/plant-sub-section", subSection); +router.use("/shift", shift) + module.exports = router; diff --git a/routes/shift.route.js b/routes/shift.route.js index e69de29..322bb81 100644 --- a/routes/shift.route.js +++ b/routes/shift.route.js @@ -0,0 +1,17 @@ +const express = require('express'); +const ShiftController = require('../controllers/shift.controller'); +const verifyToken = require("../middleware/verifyToken") +const verifyAccess = require("../middleware/verifyAccess") + +const router = express.Router(); + +router.route("/") + .get(verifyToken.verifyAccessToken, ShiftController.getAll) + .post(verifyToken.verifyAccessToken, verifyAccess(), ShiftController.create); + +router.route("/:id") + .get(verifyToken.verifyAccessToken, ShiftController.getById) + .put(verifyToken.verifyAccessToken, verifyAccess(), ShiftController.update) + .delete(verifyToken.verifyAccessToken, verifyAccess(), ShiftController.delete); + +module.exports = router; \ No newline at end of file diff --git a/services/shift.service.js b/services/shift.service.js index e69de29..93439d2 100644 --- a/services/shift.service.js +++ b/services/shift.service.js @@ -0,0 +1,88 @@ +const { + getAllShiftDb, + getShiftByIdDb, + createShiftDB, + updateShiftDb, + deleteShiftDb +} = require('../db/shift.db'); +const { ErrorHandler } = require('../helpers/error'); + +class ShiftService { + // Get all Shift + static async getAllShift(param) { + try { + const results = await getAllShiftDb(param); + + results.data.map(element => { + }); + + return results + } catch (error) { + throw new ErrorHandler(error.statusCode, error.message); + } + } + + // Get Shift by ID + static async getShiftById(id) { + try { + const result = await getShiftByIdDb(id); + + if (result.length < 1) throw new ErrorHandler(404, 'Shift not found'); + + return result; + } catch (error) { + throw new ErrorHandler(error.statusCode, error.message); + } + } + + // Create Shift + static async createShift(data) { + try { + if (!data || typeof data !== 'object') data = {}; + + const result = await createShiftDB(data); + + return result; + } catch (error) { + throw new ErrorHandler(error.statusCode, error.message); + } + } + + // Update Shift + static async updateShift(id, data) { + try { + if (!data || typeof data !== 'object') data = {}; + + const dataExist = await getShiftByIdDb(id); + + if (dataExist.length < 1) { + throw new ErrorHandler(404, 'Shift not found'); + } + + const result = await updateShiftDb(id, data); + + return result; + } catch (error) { + throw new ErrorHandler(error.statusCode, error.message); + } + } + + // Soft delete Shift + static async deleteShift(id, userId) { + try { + const dataExist = await getShiftByIdDb(id); + + if (dataExist.length < 1) { + throw new ErrorHandler(404, 'Shift not found'); + } + + const result = await deleteShiftDb(id, userId); + + return result; + } catch (error) { + throw new ErrorHandler(error.statusCode, error.message); + } + } +} + +module.exports = ShiftService; diff --git a/utils/time.js b/utils/time.js new file mode 100644 index 0000000..9a982d7 --- /dev/null +++ b/utils/time.js @@ -0,0 +1,11 @@ +module.exports = { + formattedTime: (timestamp) => { + let date = new Date(timestamp); + let hours = date.getHours().toString().padStart(2, "0"); + let minutes = date.getMinutes().toString().padStart(2, "0"); + let seconds = date.getSeconds().toString().padStart(2, "0"); + let formattedTime = `${hours}:${minutes}:${seconds}`; + return formattedTime; + }, + }; + \ No newline at end of file diff --git a/validate/shift.schema.js b/validate/shift.schema.js index e69de29..49c6981 100644 --- a/validate/shift.schema.js +++ b/validate/shift.schema.js @@ -0,0 +1,42 @@ +// ======================== +// Device Validation + +const Joi = require("joi"); + +// ======================== +const timePattern = /^([01]\d|2[0-3]):([0-5]\d)(:[0-5]\d)?$/; + +const insertShiftSchema = Joi.object({ + shift_name: Joi.string().max(100).required(), + start_time: Joi.string() + .pattern(timePattern) + .required() + .messages({ + "string.pattern.base": "start_time harus dalam format HH:mm atau HH:mm:ss", + }), + end_time: Joi.string() + .pattern(timePattern) + .required() + .messages({ + "string.pattern.base": "end_time harus dalam format HH:mm atau HH:mm:ss", + }), +}); + +const updateShiftSchema = Joi.object({ + shift_name: Joi.string().max(100), + start_time: Joi.string() + .pattern(timePattern) + .messages({ + "string.pattern.base": "start_time harus dalam format HH:mm atau HH:mm:ss", + }), + end_time: Joi.string() + .pattern(timePattern) + .messages({ + "string.pattern.base": "end_time harus dalam format HH:mm atau HH:mm:ss", + }), +}).min(1); + +module.exports = { + insertShiftSchema, + updateShiftSchema, +};