// ENVIRONMENT require('dotenv').config(); // DATABASE const { PrismaClient: CMSClient } = require("../../prisma/clients/cms"); const prisma = new CMSClient(); // CONSTANTS const { badRequestResponse, successResponse, notFoundResponse } = require("../res/responses.js"); // SERVICES const fileServices = require('../services/file.services.js'); const logger = require('../services/logger.services.js'); const { localTime } = require("../services/time.services.js"); const { default: prefixes } = require('../static/prefix.js'); exports.getMenus = async (req, res) => { try { const { isActive } = req.query; const filter = {}; if (isActive !== undefined) { filter.IsActive_AM = isActive === 'true'; } const menus = await prisma.appMenu.findMany({ where: filter, orderBy: { Order_AM: 'asc' } }); const formattedMenus = menus.map(menu => ({ id: menu.UUID_AM, name: menu.Name_AM, route: menu.Route_AM, icon: menu.Icon_AM, isActive: menu.IsActive_AM, badge: menu.Badge_AM, order: menu.Order_AM, type: menu.Type_AM, createdAt: menu.CreatedAt_AM, updatedAt: menu.UpdatedAt_AM })); logger.info(`Retrieved ${formattedMenus.length} menus`); return successResponse(res, "Menus retrieved successfully", formattedMenus); } catch (error) { logger.error(`Error getting menus: ${error.message}`); return badRequestResponse(res, "Failed to retrieve menus", error.message); } }; exports.getMenuById = async (req, res) => { try { const { id } = req.params; const menu = await prisma.appMenu.findUnique({ where: { UUID_AM: id } }); if (!menu) { logger.warn(`Menu not found with id: ${id}`); return notFoundResponse(res, "Menu not found", null); } const formattedMenu = { id: menu.UUID_AM, name: menu.Name_AM, route: menu.Route_AM, icon: menu.Icon_AM, isActive: menu.IsActive_AM, badge: menu.Badge_AM, order: menu.Order_AM, createdAt: menu.CreatedAt_AM, updatedAt: menu.UpdatedAt_AM }; logger.info(`Retrieved menu: ${menu.Name_AM}`); return successResponse(res, "Menu retrieved successfully", formattedMenu); } catch (error) { logger.error(`Error getting menu by id: ${error.message}`); return badRequestResponse(res, "Failed to retrieve menu", error.message); } }; exports.createMenu = async (req, res) => { try { const { name, route, isActive, badge, order, type } = req.body; const iconFile = req.files; if (!name || !route) { return badRequestResponse(res, "Name and route are required", null); } if (type && !['IN_APP_ROUTE', 'WEB_OPEN'].includes(type)) { return badRequestResponse(res, "Type must be either IN_APP_ROUTE or WEB_OPEN", null); } const parsedIsActive = isActive === 'true' || isActive === true; const parsedOrder = order !== undefined ? parseInt(order, 10) : 0; if (!iconFile || iconFile.length === 0) { return badRequestResponse(res, "Icon file is required", null); } const existingMenu = await prisma.appMenu.findFirst({ where: { Route_AM: route } }); if (existingMenu) { return badRequestResponse(res, "Menu with this route already exists", null); } const [iconUrl, fileName] = await fileServices.upload( prefixes.bucketName, 'menu-icons', iconFile[0].mimetype, iconFile ); const menu = await prisma.appMenu.create({ data: { Name_AM: name, Route_AM: route, Icon_AM: iconUrl, IsActive_AM: parsedIsActive, Badge_AM: badge || null, Order_AM: parsedOrder, Type_AM: type || 'IN_APP_ROUTE', CreatedAt_AM: localTime(new Date()), UpdatedAt_AM: localTime(new Date()) } }); const formattedMenu = { id: menu.UUID_AM, name: menu.Name_AM, route: menu.Route_AM, icon: menu.Icon_AM, isActive: menu.IsActive_AM, badge: menu.Badge_AM, order: menu.Order_AM, type: menu.Type_AM, createdAt: menu.CreatedAt_AM, updatedAt: menu.UpdatedAt_AM }; logger.info(`Menu created: ${menu.Name_AM} with icon: ${fileName}`); return successResponse(res, "Menu created successfully", formattedMenu); } catch (error) { logger.error(`Error creating menu: ${error.message}`); return badRequestResponse(res, "Failed to create menu", error.message); } }; exports.updateMenu = async (req, res) => { try { const { id } = req.params; const { name, route, isActive, badge, order, type } = req.body; const iconFile = req.files; // Parse FormData values to correct types const parsedIsActive = isActive !== undefined ? (isActive === 'true' || isActive === true) : undefined; const parsedOrder = order !== undefined ? parseInt(order, 10) : undefined; if (type && !['IN_APP_ROUTE', 'WEB_OPEN'].includes(type)) { return badRequestResponse(res, "Type must be either IN_APP_ROUTE or WEB_OPEN", null); } if (type && !['IN_APP_ROUTE', 'WEB_OPEN'].includes(type)) { return badRequestResponse(res, "Type must be either IN_APP_ROUTE or WEB_OPEN", null); } const existingMenu = await prisma.appMenu.findUnique({ where: { UUID_AM: id } }); if (!existingMenu) { logger.warn(`Menu not found with id: ${id}`); return notFoundResponse(res, "Menu not found", null); } if (route && route !== existingMenu.Route_AM) { const duplicateRoute = await prisma.appMenu.findFirst({ where: { Route_AM: route, UUID_AM: { not: id } } }); if (duplicateRoute) { return badRequestResponse(res, "Menu with this route already exists", null); } } const updateData = {}; if (name !== undefined) updateData.Name_AM = name; if (route !== undefined) updateData.Route_AM = route; if (parsedIsActive !== undefined) updateData.IsActive_AM = parsedIsActive; if (badge !== undefined) updateData.Badge_AM = badge; if (parsedOrder !== undefined) updateData.Order_AM = parsedOrder; if (type !== undefined) updateData.Type_AM = type; updateData.UpdatedAt_AM = localTime(new Date()); if (iconFile && iconFile.length > 0) { const [iconUrl, fileName] = await fileServices.upload( prefixes.bucketName, 'menu-icons', iconFile[0].mimetype, iconFile ); updateData.Icon_AM = iconUrl; logger.info(`New icon uploaded: ${fileName}`); } const menu = await prisma.appMenu.update({ where: { UUID_AM: id }, data: updateData }); const formattedMenu = { id: menu.UUID_AM, name: menu.Name_AM, route: menu.Route_AM, icon: menu.Icon_AM, isActive: menu.IsActive_AM, badge: menu.Badge_AM, order: menu.Order_AM, type: menu.Type_AM, createdAt: menu.CreatedAt_AM, updatedAt: menu.UpdatedAt_AM }; logger.info(`Menu updated: ${menu.Name_AM}`); return successResponse(res, "Menu updated successfully", formattedMenu); } catch (error) { logger.error(`Error updating menu: ${error.message}`); return badRequestResponse(res, "Failed to update menu", error.message); } }; exports.deleteMenu = async (req, res) => { try { const { id } = req.params; const existingMenu = await prisma.appMenu.findUnique({ where: { UUID_AM: id } }); if (!existingMenu) { logger.warn(`Menu not found with id: ${id}`); return notFoundResponse(res, "Menu not found", null); } await prisma.appMenu.delete({ where: { UUID_AM: id } }); logger.info(`Menu deleted: ${existingMenu.Name_AM}`); return successResponse(res, "Menu deleted successfully", null); } catch (error) { logger.error(`Error deleting menu: ${error.message}`); return badRequestResponse(res, "Failed to delete menu", error.message); } }; exports.reorderMenus = async (req, res) => { try { const { menuOrders } = req.body; if (!Array.isArray(menuOrders) || menuOrders.length === 0) { return badRequestResponse(res, "menuOrders array is required", null); } const updatePromises = menuOrders.map(({ id, order }) => prisma.appMenu.update({ where: { UUID_AM: id }, data: { Order_AM: order } }) ); await Promise.all(updatePromises); logger.info(`Reordered ${menuOrders.length} menus`); return successResponse(res, "Menus reordered successfully", null); } catch (error) { logger.error(`Error reordering menus: ${error.message}`); return badRequestResponse(res, "Failed to reorder menus", error.message); } };