From a9ad9cccc7cabe74eff5409256d97d7c42f69037 Mon Sep 17 00:00:00 2001 From: Muhammad Afif Date: Tue, 21 Oct 2025 10:59:25 +0700 Subject: [PATCH] add: CRUD user schedule --- controllers/user_schedule.controller.js | 71 +++++++++++++++ db/user_schedule.db.js | 116 ++++++++++++++++++++++++ routes/index.js | 2 + routes/user_schedule.route.js | 17 ++++ services/user_schedule.service.js | 89 ++++++++++++++++++ validate/user_schedule.schema.js | 21 +++++ 6 files changed, 316 insertions(+) create mode 100644 controllers/user_schedule.controller.js create mode 100644 db/user_schedule.db.js create mode 100644 routes/user_schedule.route.js create mode 100644 services/user_schedule.service.js create mode 100644 validate/user_schedule.schema.js diff --git a/controllers/user_schedule.controller.js b/controllers/user_schedule.controller.js new file mode 100644 index 0000000..02f3425 --- /dev/null +++ b/controllers/user_schedule.controller.js @@ -0,0 +1,71 @@ +const UserScheduleService = require('../services/user_schedule.service'); +const { setResponse, setResponsePaging, checkValidate } = require('../helpers/utils'); +const { insertUserScheduleSchema, updateUserScheduleSchema } = require('../validate/user_schedule.schema'); + +class UserScheduleController { + // Get all User Schedule + static async getAll(req, res) { + const queryParams = req.query; + + const results = await UserScheduleService.getAllUserScheduleDb(queryParams); + const response = await setResponsePaging(queryParams, results, 'User Schedule found') + + res.status(response.statusCode).json(response); + } + + // Get User Schedule by ID + static async getById(req, res) { + const { id } = req.params; + + const results = await UserScheduleService.getUserScheduleByID(id); + const response = await setResponse(results, 'User Schedule found') + + res.status(response.statusCode).json(response); + } + + // Create User Schedule + static async create(req, res) { + const { error, value } = await checkValidate(insertUserScheduleSchema, req) + + if (error) { + return res.status(400).json(setResponse(error, 'Validation failed', 400)); + } + + value.userId = req.user.user_id + + const results = await UserScheduleService.createUserSchedules(value); + const response = await setResponse(results, 'User Schedule created successfully') + + return res.status(response.statusCode).json(response); + } + + // Update User Schedule + static async update(req, res) { + const { id } = req.params; + + const { error, value } = checkValidate(updateUserScheduleSchema, req) + + if (error) { + return res.status(400).json(setResponse(error, 'Validation failed', 400)); + } + + value.userId = req.user.user_id + + const results = await UserScheduleService.updateUserSchedules(id, value); + const response = await setResponse(results, 'User Schedule updated successfully') + + res.status(response.statusCode).json(response); + } + + // Soft delete User Schedule + static async delete(req, res) { + const { id } = req.params; + + const results = await UserScheduleService.deleteUserSchedules(id, req.user.user_id); + const response = await setResponse(results, 'User Schedule deleted successfully') + + res.status(response.statusCode).json(response); + } +} + +module.exports = UserScheduleController; diff --git a/db/user_schedule.db.js b/db/user_schedule.db.js new file mode 100644 index 0000000..68822ad --- /dev/null +++ b/db/user_schedule.db.js @@ -0,0 +1,116 @@ +const pool = require("../config"); + +const getAllUserScheduleDb = async (searchParams = {}) => { + let queryParams = []; + + // Handle pagination + if (searchParams.limit) { + const page = Number(searchParams.page ?? 1) - 1; + queryParams = [Number(searchParams.limit ?? 10), page]; + } + + const { whereOrConditions, whereParamOr } = pool.buildStringOrIlike( + ["a.user_id", "a.user_schedule_id", "a.schedule_id", "a.user_schedule_id"], + searchParams.criteria, + queryParams + ); + if (whereParamOr) queryParams = whereParamOr; + + const { whereConditions, whereParamAnd } = pool.buildFilterQuery( + [ + { column: "a.user_id", param: searchParams.user_id, type: "int" }, + { column: "a.user_schedule_id", param: searchParams.user_schedule_id, type: "int" }, + { column: "a.schedule_id", param: searchParams.schedule_id, type: "int" }, + { column: "a.user_schedule_id", param: searchParams.user_schedule_id, type: "int" }, + ], + queryParams + ); + if (whereParamAnd) queryParams = whereParamAnd; + + const queryText = ` + SELECT + COUNT(*) OVER() AS total_data, + a.*, + b.schedule_date, + c.shift_name, + c.start_time, + c.end_time, + d.user_fullname + FROM user_schedule a + LEFT JOIN schedule b ON a.user_schedule_id = b.schedule_id + LEFT JOIN m_shift c ON a.user_schedule_id = c.shift_id + LEFT JOIN m_users d ON a.user_schedule_id = d.user_id + WHERE a.deleted_at IS NULL + ${whereConditions.length > 0 ? ` AND ${whereConditions.join(" AND ")}` : ""} + ${whereOrConditions ? ` ${whereOrConditions}` : ""} + ORDER BY a.user_schedule_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 getUserScheduleByIdDb = async (id) => { + const queryText = ` + SELECT + a.*, + b.schedule_date, + c.shift_name, + c.start_time, + c.end_time, + d.user_fullname + FROM user_schedule a + LEFT JOIN schedule b ON a.user_schedule_id = b.schedule_id + LEFT JOIN m_shift c ON a.user_schedule_id = c.shift_id + LEFT JOIN m_users d ON a.user_schedule_id = d.user_id + WHERE a.user_schedule_id = $1 AND a.deleted_at IS NULL + `; + const result = await pool.query(queryText, [id]); + return result.recordset?.[0] || null; +}; + +const insertUserScheduleDb = async (store) => { + const { query: queryText, values } = pool.buildDynamicInsert("user_schedule", store); + const result = await pool.query(queryText, values); + const insertedId = result.recordset?.[0]?.inserted_id; + + return insertedId ? await getUserScheduleByIdDb(insertedId) : null; +}; + +const updateUserScheduleDb = async (id, data) => { + const store = { ...data }; + const whereData = { user_schedule_id: id }; + + const { query: queryText, values } = pool.buildDynamicUpdate( + "user_schedule", + store, + whereData + ); + + await pool.query(`${queryText} AND deleted_at IS NULL`, values); + return getUserScheduleByIdDb(id); +}; + +const deleteUserScheduleDb = async (id, deletedBy) => { + const queryText = ` + UPDATE user_schedule + SET deleted_at = CURRENT_TIMESTAMP, deleted_by = $1 + WHERE user_schedule_id = $2 AND deleted_at IS NULL + `; + await pool.query(queryText, [deletedBy, id]); + return true; +}; + +module.exports = { + getAllUserScheduleDb, + getUserScheduleByIdDb, + insertUserScheduleDb, + updateUserScheduleDb, + deleteUserScheduleDb, +}; diff --git a/routes/index.js b/routes/index.js index cc2c2c5..25881e1 100644 --- a/routes/index.js +++ b/routes/index.js @@ -9,6 +9,7 @@ const shift = require("./shift.route"); const schedule = require("./schedule.route"); const status = require("./status.route"); const unit = require("./unit.route") +const UserSchedule = require("./user_schedule.route") router.use("/auth", auth); router.use("/user", users); @@ -20,6 +21,7 @@ router.use("/shift", shift); router.use("/schedule", schedule); router.use("/status", status); router.use("/unit", unit); +router.use("/user-schedule", UserSchedule) module.exports = router; diff --git a/routes/user_schedule.route.js b/routes/user_schedule.route.js new file mode 100644 index 0000000..4273807 --- /dev/null +++ b/routes/user_schedule.route.js @@ -0,0 +1,17 @@ +const express = require('express'); +const UserSchedulesController = require('../controllers/user_schedule.controller'); +const verifyToken = require("../middleware/verifyToken") +const verifyAccess = require("../middleware/verifyAccess") + +const router = express.Router(); + +router.route("/") + .get(verifyToken.verifyAccessToken, UserSchedulesController.getAll) + .post(verifyToken.verifyAccessToken, verifyAccess(), UserSchedulesController.create); + +router.route("/:id") + .get(verifyToken.verifyAccessToken, UserSchedulesController.getById) + .put(verifyToken.verifyAccessToken, verifyAccess(), UserSchedulesController.update) + .delete(verifyToken.verifyAccessToken, verifyAccess(), UserSchedulesController.delete); + +module.exports = router; \ No newline at end of file diff --git a/services/user_schedule.service.js b/services/user_schedule.service.js new file mode 100644 index 0000000..6c07048 --- /dev/null +++ b/services/user_schedule.service.js @@ -0,0 +1,89 @@ +const { + getAllUserScheduleDb, + getUserScheduleByIdDb, + insertUserScheduleDb, + updateUserScheduleDb, + deleteUserScheduleDb + } = require('../db/user_schedule.db'); + const { ErrorHandler } = require('../helpers/error'); + + class UserScheduleService { + // Get all devices + static async getAllUserScheduleDb(param) { + try { + const results = await getAllUserScheduleDb(param); + + results.data.map(element => { + }); + + return results + } catch (error) { + throw new ErrorHandler(error.statusCode, error.message); + } + } + + // Get device by ID + static async getUserScheduleByID(id) { + try { + const result = await getUserScheduleByIdDb(id); + + if (result.length < 1) throw new ErrorHandler(404, 'User Schedule not found'); + + return result; + } catch (error) { + throw new ErrorHandler(error.statusCode, error.message); + } + } + + // Create device + static async createUserSchedules(data) { + try { + if (!data || typeof data !== 'object') data = {}; + + const result = await insertUserScheduleDb(data); + + return result; + } catch (error) { + throw new ErrorHandler(error.statusCode, error.message); + } + } + + // Update device + static async updateUserSchedules(id, data) { + try { + if (!data || typeof data !== 'object') data = {}; + + const dataExist = await getUserScheduleByIdDb(id); + + if (dataExist.length < 1) { + throw new ErrorHandler(404, 'UserSchedules not found'); + } + + const result = await updateUserScheduleDb(id, data); + + return result; + } catch (error) { + throw new ErrorHandler(error.statusCode, error.message); + } + } + + // Soft delete device + static async deleteUserSchedules(id, userId) { + try { + const dataExist = await getUserScheduleByIdDb(id); + + if (dataExist.length < 1) { + throw new ErrorHandler(404, 'UserSchedules not found'); + } + + const result = await deleteUserScheduleDb(id, userId); + + return result; + } catch (error) { + throw new ErrorHandler(error.statusCode, error.message); + } + } + } + + module.exports = UserScheduleService; + \ No newline at end of file diff --git a/validate/user_schedule.schema.js b/validate/user_schedule.schema.js new file mode 100644 index 0000000..7e8b6af --- /dev/null +++ b/validate/user_schedule.schema.js @@ -0,0 +1,21 @@ +const Joi = require("joi"); + +// ======================== +// User Schedule Validation +// ======================== +const insertUserScheduleSchema = Joi.object({ + user_id: Joi.number().integer().required(), + schedule_id: Joi.number().integer().required(), + shift_id: Joi.number().required(), +}); + +const updateUserScheduleSchema = Joi.object({ + user_id: Joi.number().integer().optional(), + schedule_id: Joi.number().integer().optional(), + shift_id: Joi.number().optional() +}).min(1); + +module.exports = { + insertUserScheduleSchema, + updateUserScheduleSchema +};