From 38f97d197f36423b57bd7aaac233e91973182666 Mon Sep 17 00:00:00 2001 From: Gowtham M Date: Wed, 29 Oct 2025 11:38:32 +0530 Subject: [PATCH] GWM --- app/controllers/dashboard.controller.js | 112 ++++++++++++--- app/controllers/establishment.controller.js | 4 +- app/controllers/submission.controller.js | 151 +++++++++++++++++--- app/routes/routes.js | 68 ++++++++- 4 files changed, 292 insertions(+), 43 deletions(-) diff --git a/app/controllers/dashboard.controller.js b/app/controllers/dashboard.controller.js index 9b74721..d84262a 100644 --- a/app/controllers/dashboard.controller.js +++ b/app/controllers/dashboard.controller.js @@ -1,9 +1,6 @@ -const db = require("../models"); -const { Op } = require("sequelize"); -const { Sequelize } = require("sequelize"); -const Submission = db.Submission; -const SubmissionProduct = db.SubmissionProduct; -const SubmissionDeadline = db.SubmissionDeadline; + +const { Sequelize, Op } = require("sequelize"); +const { Establishment, Submission, Emirate, EstablishmentUser, SubmissionProduct, SubmissionDeadline } = require("../models"); function getQuarter(date) { const month = date.getMonth() + 1; @@ -76,12 +73,7 @@ exports.getEstablishmentDashboard = async (req, res) => { const today = new Date(); const quarters = getPrevCurrNextQuarter(today); - // Last 5 submissions - // const submission_history = await Submission.findAll({ - // where: { establishment_id }, - // order: [["created_at", "DESC"]], - // limit: 5, - // }); + const submission_history = await Submission.findAll({ where: { establishment_id }, @@ -99,12 +91,7 @@ exports.getEstablishmentDashboard = async (req, res) => { ], ], }, - // include: [ - // { - // model: SubmissionProduct, - // as: "products", // ✅ use your actual alias here - // }, - // ], + }); @@ -164,3 +151,92 @@ exports.getEstablishmentDashboard = async (req, res) => { res.status(500).json({ status: "failed", message: err.message }); } }; + + + + +exports.adminDashboard = async (req, res) => { + try { + const { quarter, year } = req.query; // e.g., Q1, 2025 + + // --- 1️⃣ Summary Counts --- + const [ + totalEstablishments, + submittedCount, + approvedCount, + rejectedCount, + pendingCount + ] = await Promise.all([ + Establishment.count(), + Submission.count(), + Submission.count({ where: { status: "Approved" } }), + Submission.count({ where: { status: "Rejected" } }), + Submission.count({ where: { status: "Pending" } }), + ]); + + // Not Started = total establishments - those that have any submission + const startedEstIds = await Submission.findAll({ + attributes: [[Sequelize.fn("DISTINCT", Sequelize.col("establishment_id")), "id"]], + raw: true, + }); + const startedIds = startedEstIds.map(e => e.id); + const notStartedCount = await Establishment.count({ + where: { id: { [Op.notIn]: startedIds } }, + }); + + // --- 2️⃣ Recent 10 submissions for given quarter & year --- + const whereCond = {}; + if (quarter) whereCond.quarter = quarter; + if (year) whereCond.year = year; + + const recentSubmissions = await Submission.findAll({ + where: whereCond, + include: [ + { + model: Establishment, + as: "establishment", + attributes: ["factory_name"], + include: [ + { model: Emirate, as: "establishment_emirate", attributes: ["name"] }, + ], + }, + { + model: EstablishmentUser, + as: "created_user", + attributes: ["name"], + }, + ], + attributes: { + include: [ + [ + Sequelize.literal(`( + SELECT COUNT(*) + FROM submission_products AS sp + WHERE sp.submission_id = submission.id + )`), + "product_count", + ], + ], + }, + order: [["created_at", "DESC"]], + limit: 10, + }); + + // --- 3️⃣ Send Response --- + return res.status(200).json({ + status: "success", + summary: { + total_establishments: totalEstablishments, + submitted: submittedCount, + approved: approvedCount, + rejected: rejectedCount, + pending: pendingCount, + not_started: notStartedCount, + }, + recent_submissions: recentSubmissions, + }); + } catch (err) { + console.error(err); + return res.status(500).json({ status: "failed", message: err.message }); + } +}; \ No newline at end of file diff --git a/app/controllers/establishment.controller.js b/app/controllers/establishment.controller.js index 8537ae0..59b9b26 100644 --- a/app/controllers/establishment.controller.js +++ b/app/controllers/establishment.controller.js @@ -241,7 +241,7 @@ exports.getAllEstablishments = async (req, res) => { ...(exportType ? {} : { limit, offset }), // pagination only when not exporting }); - // 📦 If export = excel → generate file + // If export = excel → generate file if (exportType && exportType.toLowerCase() === "excel") { const workbook = new ExcelJS.Workbook(); const worksheet = workbook.addWorksheet("Establishments"); @@ -302,7 +302,7 @@ exports.getAllEstablishments = async (req, res) => { return res.end(); } - // ✅ Otherwise return JSON (paginated) + // Otherwise return JSON (paginated) const totalCount = await Establishment.count({ where: whereClause }); return res.status(200).json({ status: "success", diff --git a/app/controllers/submission.controller.js b/app/controllers/submission.controller.js index 9ae91c0..200917b 100644 --- a/app/controllers/submission.controller.js +++ b/app/controllers/submission.controller.js @@ -7,6 +7,9 @@ const UnitMaster = db.UnitMaster; const VariationReasonMaster = db.VariationReasonMaster; const ZeroTargetReasonMaster = db.ZeroTargetReasonMaster; const Product = db.Product; +const CityTown = db.CityTown; +const Emirate = db.Emirate; +const ExcelJS = require("exceljs"); const { Op } = require("sequelize"); const { Sequelize } = require("sequelize"); const { sendEmail } = require("../services/email.service"); @@ -204,41 +207,147 @@ exports.submissionHistory = async (req, res) => { }; +// exports.submissionList = async (req, res) => { +// try { + +// const data = await Submission.findAll({ +// include: [ +// { +// model: Establishment, +// as: "establishment" , +// attributes: ["factory_name","establishment_code"], +// include: [ +// { model: Emirate, as: "establishment_emirate", attributes: ["name"] } +// ] +// }, +// { +// model: EstablishmentUser, +// as: "created_user", +// attributes: ["name"], +// }, +// ], +// attributes: { +// include: [ +// [ +// Sequelize.literal(`(SELECT COUNT(*) FROM submission_products AS sp WHERE sp.submission_id = submission.id )`), +// "product_count", +// ], +// ], +// }, +// order: [["id", "DESC"]], +// }); + +// res.status(200).json({ status: "success", data }); + +// } catch (err) { +// res.status(500).json({ status: "failed", message: err.message }); +// } +// }; + + exports.submissionList = async (req, res) => { try { + const { + page = 1, + limit = 10, + search = "", + quarter, + year, + status, + emirate_id, + export_excel = false, + } = req.query; - const data = await Submission.findAll({ + const offset = (page - 1) * limit; + const where = {}; + + // 🔹 Filters + if (year) where.year = year; + if (quarter) where.quarter = quarter; + if (status) where.status = status; + + // 🔹 Search — establishment name or code or submitted user + const establishmentWhere = {}; + if (search) { + establishmentWhere[Op.or] = [ + { factory_name: { [Op.like]: `%${search}%` } }, + { establishment_code: { [Op.like]: `%${search}%` } }, + ]; + } + + // 🔹 Emirate filter + if (emirate_id) establishmentWhere.establishment_emirate_id = emirate_id; + + // 🔹 Query + const { rows, count } = await Submission.findAndCountAll({ + where, include: [ - { - model: Establishment, - as: "establishment" , - attributes: ["factory_name","establishment_code","emirate"], - }, - { - model: EstablishmentUser, - as: "created_user", - attributes: ["name", "email"], - }, - ], + { + model: Establishment, + as: "establishment", + attributes: ["factory_name", "establishment_code"], + where: establishmentWhere, + include: [ + { model: Emirate, as: "establishment_emirate", attributes: ["name"] }, + ], + }, + { + model: EstablishmentUser, + as: "created_user", + attributes: ["name"], + }, + ], attributes: { - include: [ - [ - Sequelize.literal(`(SELECT COUNT(*) FROM submission_products AS sp WHERE sp.submission_id = submission.id )`), - "product_count", - ], - ], - }, + include: [ + [ + Sequelize.literal(`( + SELECT COUNT(*) FROM submission_products AS sp + WHERE sp.submission_id = submission.id + )`), + "product_count", + ], + ], + }, order: [["id", "DESC"]], + offset: parseInt(offset), + limit: parseInt(limit), }); - res.status(200).json({ status: "success", data }); + // 🔹 Export to Excel + if (export_excel && export_excel === "true") { + const excelData = rows.map((s) => ({ + Establishment: s.establishment?.factory_name, + Emirate: s.establishment?.establishment_emirate?.name, + Year: s.year, + Quarter: s.quarter, + Products: s.dataValues.product_count, + Status: s.status, + Submitted_By: s.created_user?.name, + Submission_Date: s.created_at, + })); + const filePath = await exportToExcel("Submission_List", excelData); + return res.download(filePath); + } + + // 🔹 Paginated Response + return res.status(200).json({ + status: "success", + message: "Fetched successfully", + pagination: { + total: count, + page: Number(page), + limit: Number(limit), + totalPages: Math.ceil(count / limit), + }, + data: rows, + }); } catch (err) { + console.error(err); res.status(500).json({ status: "failed", message: err.message }); } }; - exports.viewSubmissionDetails = async (req, res) => { try { diff --git a/app/routes/routes.js b/app/routes/routes.js index 0687153..c441da3 100644 --- a/app/routes/routes.js +++ b/app/routes/routes.js @@ -1767,14 +1767,45 @@ router.get("/submissions/history/:establishment_id",[verifySignature, verifyToke * @swagger * /api/submissions: * get: - * summary: Get all submissions + * summary: Get submissions list with pagination, filters, search, and export * tags: [Submissions] * security: * - appSignature: [] * - bearerAuth: [] + * parameters: + * - in: query + * name: page + * schema: { type: integer, default: 1 } + * description: Page number + * - in: query + * name: limit + * schema: { type: integer, default: 10 } + * description: Items per page + * - in: query + * name: search + * schema: { type: string } + * description: Search by establishment name or code + * - in: query + * name: quarter + * schema: { type: string } + * - in: query + * name: year + * schema: { type: integer } + * - in: query + * name: status + * schema: { type: string } + * - in: query + * name: emirate_id + * schema: { type: integer } + * - in: query + * name: export_excel + * schema: { type: boolean } + * description: If true, exports data to Excel file * responses: * 200: - * description: All submissions + * description: Submission list retrieved successfully + * 500: + * description: Server error */ router.get("/submissions",[verifySignature, verifyToken], submissionController.submissionList); @@ -2011,6 +2042,39 @@ router.post("/submission_deadlines",[verifySignature, verifyToken], ConfigContro router.get("/establishment_dashboard",[verifySignature, verifyToken],dashboardController.getEstablishmentDashboard); +/** + * @swagger + * /api/admin_dashboard: + * get: + * summary: Get dashboard data for admin user + * tags: [Dashboard] + * security: + * - appSignature: [] + * - bearerAuth: [] + * parameters: + * - in: query + * name: quarter + * required: true + * schema: + * type: string + * - in: query + * name: year + * required: true + * schema: + * type: integer + * responses: + * 200: + * description: Dashboard data fetched successfully + * 400: + * description: Bad request + * 403: + * description: Invalid signature + * 401: + * description: Unauthorized + * 500: + * description: Server error + */ +router.get("/admin_dashboard",[verifySignature, verifyToken], dashboardController.adminDashboard);