diff --git a/Tombolo/client-reactjs/src/components/application/jobMonitoring/JobMonitoringActionButton.jsx b/Tombolo/client-reactjs/src/components/application/jobMonitoring/JobMonitoringActionButton.jsx
index c16c7ad91..9d894746a 100644
--- a/Tombolo/client-reactjs/src/components/application/jobMonitoring/JobMonitoringActionButton.jsx
+++ b/Tombolo/client-reactjs/src/components/application/jobMonitoring/JobMonitoringActionButton.jsx
@@ -1,9 +1,10 @@
import React from 'react';
-import { Menu, Dropdown, Button, message, Popconfirm } from 'antd';
+import { Menu, Dropdown, Button, message, Popconfirm, Popover, Form, Select, Card, Badge } from 'antd';
import { DownOutlined } from '@ant-design/icons';
-import { handleBulkDeleteJobMonitorings } from './jobMonitoringUtils';
+import { handleBulkDeleteJobMonitorings, toggleJobMonitoringStatus } from './jobMonitoringUtils';
+const { Option } = Select;
const JobMonitoringActionButton = ({
handleAddJobMonitoringButtonClick,
selectedRows,
@@ -13,6 +14,9 @@ const JobMonitoringActionButton = ({
setFiltersVisible,
filtersVisible,
}) => {
+ const [bulkStartPauseForm] = Form.useForm(); // Form Instance
+
+ // Handle bulk delete
const deleteSelected = async () => {
try {
const selectedRowIds = selectedRows.map((row) => row.id);
@@ -24,6 +28,22 @@ const JobMonitoringActionButton = ({
}
};
+ // Bulk start/pause job monitorings
+ const bulkStartPauseJobMonitorings = async () => {
+ try {
+ const action = bulkStartPauseForm.getFieldValue('action'); // Ensure correct usage of bulkStartPauseForm
+ const selectedRowIds = selectedRows.map((row) => row.id);
+ const updatedMonitorings = await toggleJobMonitoringStatus({ ids: selectedRowIds, action });
+ setJobMonitorings((prev) =>
+ prev.map((monitoring) => updatedMonitorings.find((updated) => updated.id === monitoring.id) || monitoring)
+ );
+ message.success(`Selected ${action === 'start' ? 'Job Monitorings started' : 'Job Monitorings paused'}`);
+ } catch (err) {
+ message.error('Unable to start/pause selected job monitorings');
+ }
+ };
+
+ // Handle menu selection
const handleMenuSelection = (key) => {
if (key === '1') {
handleAddJobMonitoringButtonClick();
@@ -49,6 +69,36 @@ const JobMonitoringActionButton = ({
Bulk Edit
+
+
+
+
+
+
+
+
+
+
+ }
+ trigger="hover">
+ Bulk start/pause
+
+
prev.map((monitoring) => (monitoring.id === record.id ? updatedData : monitoring)));
+
+ const updatedData = await toggleJobMonitoringStatus({ ids: [record.id] });
+ const updatedMonitoringIds = updatedData.map((monitoring) => monitoring.id);
+
+ setJobMonitorings((prev) =>
+ prev.map((monitoring) =>
+ updatedMonitoringIds.includes(monitoring.id)
+ ? updatedData.find((updated) => updated.id === monitoring.id)
+ : monitoring
+ )
+ );
} catch (err) {
message.error('Failed to toggle monitoring status');
}
@@ -283,6 +293,7 @@ const JobMonitoringTable = ({
loading={filteringJobs}
columns={columns}
rowKey="id"
+ rowSelectedBgColor="var(--danger)"
size="small"
rowSelection={{
type: 'checkbox',
@@ -291,9 +302,16 @@ const JobMonitoringTable = ({
},
}}
pagination={{ pageSize: 20 }}
- rowClassName={(record) =>
- record?.isActive ? 'jobMonitoringTable__active-monitoring' : 'jobMonitoringTable__inactive-monitoring'
- }
+ rowClassName={(record) => {
+ let className = record?.isActive
+ ? 'jobMonitoringTable__active-monitoring'
+ : 'jobMonitoringTable__inactive-monitoring';
+ const idsOfSelectedRows = selectedRows.map((row) => row.id);
+ if (idsOfSelectedRows.includes(record.id)) {
+ className += ' jobMonitoringTable__selected-row';
+ }
+ return className;
+ }}
/>
);
};
diff --git a/Tombolo/client-reactjs/src/components/application/jobMonitoring/index.jsx b/Tombolo/client-reactjs/src/components/application/jobMonitoring/index.jsx
index dab1eb75c..331bf3165 100644
--- a/Tombolo/client-reactjs/src/components/application/jobMonitoring/index.jsx
+++ b/Tombolo/client-reactjs/src/components/application/jobMonitoring/index.jsx
@@ -650,6 +650,7 @@ function JobMonitoring() {
setDisplayAddRejectModal={setDisplayAddRejectModal}
applicationId={applicationId}
setSelectedRows={setSelectedRows}
+ selectedRows={selectedRows}
domains={domains}
productCategories={productCategories}
allProductCategories={allProductCategories}
diff --git a/Tombolo/client-reactjs/src/components/application/jobMonitoring/jobMonitoring.css b/Tombolo/client-reactjs/src/components/application/jobMonitoring/jobMonitoring.css
index fd1d287b8..553da9531 100644
--- a/Tombolo/client-reactjs/src/components/application/jobMonitoring/jobMonitoring.css
+++ b/Tombolo/client-reactjs/src/components/application/jobMonitoring/jobMonitoring.css
@@ -111,6 +111,9 @@
background-color: var(--secondary-light);
}
+.jobMonitoringTable__selected-row > .ant-table-cell {
+ background-color: rgba(255, 255, 0, 0.1) !important;
+}
/* Filters ------------------------------------------------------------------ */
.notifications__filter-label {
diff --git a/Tombolo/client-reactjs/src/components/application/jobMonitoring/jobMonitoringUtils.js b/Tombolo/client-reactjs/src/components/application/jobMonitoring/jobMonitoringUtils.js
index 7eeae5583..578f29139 100644
--- a/Tombolo/client-reactjs/src/components/application/jobMonitoring/jobMonitoringUtils.js
+++ b/Tombolo/client-reactjs/src/components/application/jobMonitoring/jobMonitoringUtils.js
@@ -127,11 +127,11 @@ export const identifyErroneousTabs = ({ erroneousFields }) => {
};
//Toggle job monitoring status, just post the id of the job monitoring to /toggle in the req body
-export const toggleJobMonitoringStatus = async ({ id }) => {
+export const toggleJobMonitoringStatus = async ({ ids, action }) => {
const payload = {
method: 'PATCH',
headers: authHeader(),
- body: JSON.stringify({ id }),
+ body: JSON.stringify({ ids, action }),
};
const response = await fetch(`/api/jobmonitoring/toggleIsActive`, payload);
@@ -140,7 +140,8 @@ export const toggleJobMonitoringStatus = async ({ id }) => {
}
const data = await response.json();
- return data;
+
+ return data.updatedJobMonitorings;
};
// Bulk delete job monitorings
diff --git a/Tombolo/server/routes/jobmonitoring/read.js b/Tombolo/server/routes/jobmonitoring/read.js
index 192548f36..0c310404b 100644
--- a/Tombolo/server/routes/jobmonitoring/read.js
+++ b/Tombolo/server/routes/jobmonitoring/read.js
@@ -1,5 +1,6 @@
const express = require("express");
const router = express.Router();
+const Sequelize = require("sequelize");
const { body, check, param } = require("express-validator");
//Local imports
@@ -9,6 +10,7 @@ const { validationResult } = require("express-validator");
//Constants
const JobMonitoring = models.jobMonitoring;
+const Op = Sequelize.Op;
// Create new job monitoring
router.post(
@@ -253,40 +255,91 @@ router.delete(
router.patch(
"/toggleIsActive",
[
- body("id").isUUID().withMessage("ID must be a valid UUID"),
+ body("ids").isArray().withMessage("Invalid ids"), // Ensure ids is an array
+ body("ids.*").isUUID().withMessage("Invalid id"), // Ensure each id is a valid UUID
+ // make action optional and when provided must be either start or pause
+ body("action")
+ .optional()
+ .isIn(["start", "pause"])
+ .withMessage("Action must be either start or pause"),
],
async (req, res) => {
-
// Handle the PATCH request here
const errors = validationResult(req);
if (!errors.isEmpty()) {
- return res.status(503).send("Failed to toggle");
+ return res.status(400).send("Failed to toggle"); // Use a valid status code
}
+ let transaction;
+
try {
- const { id } = req.body;
- // Get and toggle
- const jobMonitoring = await JobMonitoring.findByPk(id);
- if (!jobMonitoring) {
- logger.error("Toggle Job monitoring - Job monitoring not found");
- return res.status(404).send("Job monitoring not found");
+ transaction = await JobMonitoring.sequelize.transaction();
+ const { ids, action } = req.body; // Expecting an array of IDs
+
+ // Find all job monitorings with the given IDs
+ const jobMonitorings = await JobMonitoring.findAll({
+ where: { id: { [Op.in]: ids } },
+ });
+
+ if (jobMonitorings.length === 0) {
+ logger.error("Toggle Job monitoring - Job monitorings not found");
+ return res.status(404).send("Job monitorings not found");
}
- const isApproved = jobMonitoring.approvalStatus === "Approved";
- if (!isApproved) {
- logger.error("Toggle Job monitoring - Job monitoring not approved");
- return res.status(503).send("Can't toggle job monitoring that is not in approved state");
+
+ // Filter out the job monitorings that are not approved
+ const approvedJobMonitorings = jobMonitorings.filter(
+ (jobMonitoring) => jobMonitoring.approvalStatus === "Approved"
+ );
+
+ if (approvedJobMonitorings.length === 0) {
+ logger.error(
+ "Toggle Job monitoring - No approved job monitorings found"
+ );
+ return res.status(400).send("No approved job monitorings to toggle"); // Use a valid status code
}
- const currentStatus = jobMonitoring.isActive;
- const data = await jobMonitoring.update({ isActive: !currentStatus });
- res.status(200).send(data);
+ // Get the IDs of the approved job monitorings
+ const approvedIds = approvedJobMonitorings.map(
+ (jobMonitoring) => jobMonitoring.id
+ );
+
+ if(action){
+ // If action is start or pause change isActive to true or false respectively
+ await JobMonitoring.update(
+ { isActive: action === "start" },
+ {
+ where: { id: { [Op.in]: approvedIds } },
+ transaction,
+ }
+ );
+ }else{
+ // Toggle the isActive status for all approved job monitorings
+ await JobMonitoring.update(
+ { isActive: Sequelize.literal("NOT isActive") },
+ {
+ where: { id: { [Op.in]: approvedIds } },
+ transaction,
+ }
+ );
+ }
+
+ await transaction.commit();
+
+ // Get all updated job monitorings
+ const updatedJobMonitorings = await JobMonitoring.findAll({
+ where: { id: { [Op.in]: approvedIds } },
+ });
+
+ res.status(200).send({ success: true, message: "Toggled successfully", updatedJobMonitorings }); // Send the updated job monitorings
} catch (err) {
- logger.error(err);
+ await transaction.rollback();
+ logger.error(err.message);
res.status(500).send("Failed to toggle job monitoring");
}
}
);
+module.exports = router;
// Bulk update - only primary, secondary and notify contact are part of bulk update for now
router.patch(
"/bulkUpdate",