Added delete VA and project route, fixed tracker api issues

This commit is contained in:
Aakash Mittal 2025-11-25 19:39:48 +05:30
parent 9c736dea02
commit 39010ee61c
8 changed files with 306 additions and 25 deletions

View File

@ -693,12 +693,12 @@ async function submitForm() {
let cardNumber = document.getElementById('card-number');
let expiryDate = document.getElementById('expiry-date');
let securityCode = document.getElementById('security-code');
let cardZip = document.getElementById('card-zip').value;
let bankName = document.getElementById('bank-name').value;
let accountNumber = document.getElementById('account-number').value;
let routingNumber = document.getElementById('routing-number').value;
let accountType = document.getElementById('account-type').value;
let accountHolderName = document.getElementById('account-holder-name').value;
let cardZip = document.getElementById('card-zip');
let bankName = document.getElementById('bank-name');
let accountNumber = document.getElementById('account-number');
let routingNumber = document.getElementById('routing-number');
let accountType = document.getElementById('account-type');
let accountHolderName = document.getElementById('account-holder-name');
if(cardNumber.value) {
formData.userDetails.cardNumber = cardNumber.value;

View File

@ -0,0 +1,97 @@
import { ProjectModel, VAModel, ClientModel } from "../../models/vaModel.js";
import { Types } from "mongoose";
/**
* Delete project and all related data (without transaction)
*/
async function delete_project_sf(req, res) {
try {
const { projectId } = req.params;
// Validate projectId
if (!projectId) {
return res.status(400).json({
success: false,
message: "Project ID is required"
});
}
let projectObjectId;
try {
projectObjectId = new Types.ObjectId(projectId);
} catch (error) {
return res.status(400).json({
success: false,
message: "Invalid Project ID format"
});
}
// Check if project exists
const existingProject = await ProjectModel.findById(projectObjectId);
if (!existingProject) {
return res.status(404).json({
success: false,
message: "Project not found"
});
}
const clientId = existingProject.clientId;
const vaAssigned = existingProject.vaAssigned || [];
const projectName = existingProject.name;
console.log(`Deleting project: ${projectName} (${projectId})`);
const deletionSummary = {};
// 1. Remove project from Client's projectIds array
if (clientId) {
await ClientModel.findByIdAndUpdate(
clientId,
{ $pull: { projectIds: projectObjectId } }
);
deletionSummary.clientUpdated = true;
console.log(`Removed project from client`);
}
// 2. Remove project from all assigned VAs' projectIds arrays
if (vaAssigned.length > 0) {
const vaResult = await VAModel.updateMany(
{ _id: { $in: vaAssigned } },
{ $pull: { projectIds: projectObjectId } }
);
deletionSummary.vasUpdated = vaResult.modifiedCount;
console.log(`Removed project from ${deletionSummary.vasUpdated} VAs`);
} else {
deletionSummary.vasUpdated = 0;
}
// 6. Finally delete the project
await ProjectModel.findByIdAndDelete(projectObjectId);
console.log(`✅ Successfully deleted project: ${projectName} (${projectId})`);
res.status(200).json({
success: true,
message: 'Project and all related data deleted successfully',
data: {
projectId: projectId,
projectName: projectName,
clientId: clientId,
deletedAt: new Date().toISOString()
}
});
} catch (error) {
console.error("delete_project_sf error:", error);
return res.status(500).json({
success: false,
message: "Failed to delete project",
error: process.env.NODE_ENV === 'production'
? 'Internal server error'
: error.message
});
}
}
export { delete_project_sf };

View File

@ -147,7 +147,7 @@ async function va_log_track_work_callback(req, res) {
const vaId = vaUser._id;
const payload = req.body || {};
const { projectId, logId, title, appName } = payload;
const { projectId, logId, title, appName, url = null } = payload;
const project = await ProjectModel.findById(projectId);
if (!project) return res.status(400).json({ success: false, message: "Project not found" });
@ -190,6 +190,7 @@ async function va_log_track_work_callback(req, res) {
logId,
title,
appName,
url,
from: Date.now(),
});

View File

@ -0,0 +1,92 @@
import { VAModel, ClientModel, ProjectModel, LoggedModel, WorkTrackModel } from "../../../models/vaModel.js";
// Delete VA and all related data
async function va_remove_function(req, res) {
try {
const vaId = req.params.id;
// Validate VA ID
if (!vaId) {
return res.status(400).json({
success: false,
message: "VA ID is required"
});
}
// Find the VA to get clientIds and projectIds before deletion
const vaUser = await VAModel.findById(vaId);
if (!vaUser) {
return res.status(404).json({
success: false,
message: "VA not found"
});
}
const clientIds = vaUser.clientIds || [];
const projectIds = vaUser.projectIds || [];
const vaEmail = vaUser.userDetails?.email;
console.log(`Deleting VA ${vaId} - Affected: ${clientIds.length} clients, ${projectIds.length} projects`);
// 1. Remove VA from all Clients
let clientsUpdated = 0;
if (clientIds.length > 0) {
const clientResult = await ClientModel.updateMany(
{ _id: { $in: clientIds } },
{ $pull: { vaIds: vaId } }
);
clientsUpdated = clientResult.modifiedCount;
console.log(`Removed VA from ${clientsUpdated} clients`);
}
// 2. Remove VA from all Projects
let projectsUpdated = 0;
if (projectIds.length > 0) {
const projectResult = await ProjectModel.updateMany(
{ _id: { $in: projectIds } },
{ $pull: { vaAssigned: vaId } }
);
projectsUpdated = projectResult.modifiedCount;
console.log(`Removed VA from ${projectsUpdated} projects`);
}
// 3. Delete all WorkTrack records for this VA
const workTrackResult = await WorkTrackModel.deleteMany({ vaId: vaId });
console.log(`Deleted ${workTrackResult.deletedCount} work track records`);
// 4. Delete all Logged records for this VA
const loggedResult = await LoggedModel.deleteMany({ vaId: vaId });
console.log(`Deleted ${loggedResult.deletedCount} log records`);
// 5. Finally delete the VA
await VAModel.findByIdAndDelete(vaId);
console.log(`Successfully deleted VA: ${vaId} and all related data`);
res.status(200).json({
success: true,
message: 'Virtual Assistant and all related data deleted successfully',
data: {
vaId: vaId,
vaName: `${vaUser.userDetails?.firstName || ''} ${vaUser.userDetails?.lastName || ''}`.trim() || 'Unknown VA',
vaEmail: vaEmail,
clientsUpdated: clientsUpdated,
projectsUpdated: projectsUpdated,
workTracksDeleted: workTrackResult.deletedCount,
logsDeleted: loggedResult.deletedCount,
deletedAt: new Date().toISOString()
}
});
} catch (error) {
console.error('Error deleting VA:', error);
res.status(500).json({
success: false,
message: 'Failed to delete Virtual Assistant',
error: 'Internal server error'
});
}
}
export { va_remove_function };

View File

@ -1,12 +1,12 @@
import { LoggedModel, WorkTrackModel, ProjectModel } from "../../../models/vaModel.js";
// Helper function to get start and end of day
// Helper function to get start and end of day in UTC
function getDayRange(date = new Date()) {
const start = new Date(date);
start.setHours(0, 0, 0, 0);
start.setUTCHours(0, 0, 0, 0);
const end = new Date(date);
end.setHours(23, 59, 59, 999);
end.setUTCHours(23, 59, 59, 999);
return { start, end };
}
@ -25,10 +25,53 @@ function formatHoursToReadable(hours) {
}
}
// function to close open work tracks and create logout
async function closePreviousSession(vaId, lastLog) {
// Helper function to parse ISO time string
function parseISOTimeString(timeString) {
if (!timeString) return new Date();
try {
const currentTime = new Date();
// Parse ISO string directly
const parsedDate = new Date(timeString);
// Check if the date is valid
if (isNaN(parsedDate.getTime())) {
throw new Error('Invalid date');
}
parsedDate.setMilliseconds(0);
console.log("pasdes date" + parsedDate)
return parsedDate;
} catch (error) {
console.error('Error parsing ISO time string:', error);
return new Date();
}
}
function setMillisecondsToZero(date) {
const newDate = new Date(date);
newDate.setMilliseconds(0);
return newDate;
}
// Helper function to check if time difference is less than threshold
function isTimeWithinThreshold(receivedTime, currentTime, thresholdSeconds = 10) {
const diffMs = Math.abs(currentTime - receivedTime);
const diffSeconds = diffMs / 1000;
console.log(`Time comparison - Received: ${receivedTime.toISOString()}, Current: ${currentTime.toISOString()}, Diff: ${diffSeconds}s`);
return diffSeconds <= thresholdSeconds;
}
// Helper function to get current UTC time
function getCurrentUTCTime() {
const now = new Date();
now.setMilliseconds(0);
return now;
}
// function to close open work tracks and create logout
async function closePreviousSession(vaId, lastLog, logTime = new Date()) {
try {
const currentTime = logTime;
// Find all open work tracks for this VA and project (where to is not set or totalTime is 0)
const openWorkTracks = await WorkTrackModel.find({
@ -120,18 +163,44 @@ async function closePreviousSession(vaId, lastLog) {
async function va_project_log_callback(req, res) {
try {
const payload = req.body || {};
const { vaId, vaEmail, projectId, type } = payload;
const { vaId, vaEmail, projectId, type, time: receivedTimeString } = payload;
console.log("Received payload:", payload);
if (!vaId, !vaEmail, !projectId, !type) return res.status(400).send("Missing Props");
if (!vaId || !vaEmail || !projectId || !type) {
return res.status(400).send("Missing required properties");
}
// Determine the log time based on received time (both in UTC)
const currentTime = getCurrentUTCTime(); // Current UTC time
let logTime = currentTime;
if (receivedTimeString) {
// Parse the received ISO time string
const receivedTime = parseISOTimeString(receivedTimeString);
// Check if the difference is less than 10 seconds
if (isTimeWithinThreshold(receivedTime, currentTime, 10)) {
logTime = receivedTime;
console.log(`✅ Using received ISO time: ${receivedTimeString}, difference is within 10 seconds`);
console.log(`✅ Parsed as: ${receivedTime.toISOString()}`);
} else {
logTime = currentTime;
console.log(`⚠️ Using current UTC time, received time difference exceeds 10 seconds.`);
console.log(`Received: ${receivedTimeString} (parsed: ${receivedTime.toISOString()})`);
console.log(`Current: ${currentTime.toISOString()}`);
}
} else {
console.log(`ℹ️ No time received, using current UTC time: ${currentTime.toISOString()}`);
}
const project = await ProjectModel.findById(projectId);
if (!project) {
return res.status(400).json({ success: false, message: "Project not found" });
}
// For LOGIN requests, check daily hours
// For LOGIN requests, check daily hours (using UTC day range)
if (type === "LOGIN") {
const { start, end } = getDayRange();
const { start, end } = getDayRange(); // UTC day range
// Get today's total hours
const todayWorkEntries = await WorkTrackModel.find({
vaId,
@ -163,7 +232,7 @@ async function va_project_log_callback(req, res) {
// check last workModel login time and make a logout request then create login
// console.log(`User already logged in. Closing previous session...`);
const closeResult = await closePreviousSession(vaId, lastLog);
const closeResult = await closePreviousSession(vaId, lastLog, logTime);
// console.log(`Previous session closed: ${closeResult.closedWorkTracks} work tracks, logout log: ${closeResult.logoutLogId}`);
@ -172,7 +241,8 @@ async function va_project_log_callback(req, res) {
loggedType: "LOGIN",
vaId,
vaEmail,
projectId
projectId,
time: logTime
});
return res.status(201).send({
@ -182,13 +252,21 @@ async function va_project_log_callback(req, res) {
previousSession: {
closedWorkTracks: closeResult.closedWorkTracks,
logoutLogId: closeResult.logoutLogId
}
},
timeUsed: receivedTimeString ? 'received' : 'current',
actualTime: createdLog.time
});
}
}
const createdLog = await LoggedModel.create({ loggedType: type, vaId, vaEmail, projectId })
// console.log(createdLog)
const createdLog = await LoggedModel.create({
loggedType: type,
vaId,
vaEmail,
projectId,
time: logTime
});
try {
const salesforceUrl = `${process.env.SF_DOCTRACKER_API_URL}services/apexrest/docva/api/va-project-log`;
const sfData = {
@ -218,7 +296,14 @@ async function va_project_log_callback(req, res) {
const result = await response.json();
// console.log("✅ Successfully synced VA data to Salesforce:", result);
res.status(201).json({ success: true, message: `${type} Successfully`, logId: createdLog._id, SyncSalesforce: result });
res.status(201).json({
success: true,
message: `${type} Successfully`,
logId: createdLog._id,
SyncSalesforce: result,
timeUsed: receivedTimeString ? 'received' : 'current',
actualTime: createdLog.time
});
} catch (error) {
console.error("Failed to sync with Salesforce:", error.message);
console.log(error)
@ -229,9 +314,8 @@ async function va_project_log_callback(req, res) {
throw salesforceError;
}
// return res.status(201).send({ success: true, message: `${type} Successfully`, logId: createdLog._id });
} catch (error) {
console.error("Error in va_project_log_callback:", error);
return res.status(500).send("Internal Server Error");
}
}

View File

@ -320,6 +320,9 @@ const vaWorkTrackSchema = new Schema({
appName: {
type: String,
},
url: {
type: String,
},
from: {
type: Date,
default: Date.now,

View File

@ -12,6 +12,7 @@ import { process_reset } from "../../controllers/passwordReset.controllers.js";
import { project_add_sf } from "../../controllers/project/projectAddSf.controller.js"
import { update_project_sf } from "../../controllers/project/projectUpdateSf.controller.js"
import { authValidator } from "../../middlewares/auth.middleware.js";
import { delete_project_sf } from "../../controllers/project/deleteProjectSf.controller.js"
const v1Routes = express.Router()
@ -29,6 +30,7 @@ v1Routes.use("/client", clientRoutes);
v1Routes.post("/project", authValidator, project_add_sf);
v1Routes.patch("/project/:projectId", authValidator, update_project_sf);
v1Routes.delete("/project/:projectId", authValidator, delete_project_sf);
v1Routes.post("/password/reset", process_reset);

View File

@ -26,6 +26,7 @@ import { va_verify_email } from "../../controllers/va/vaAuth/vaVerifyEmail.contr
import { va_leave_apply_function } from "../../controllers/va/vaLeaves/vaLeavesApply.controller.js";
import { va_leave_list_function } from "../../controllers/va/vaLeaves/vaLeaveList.controller.js"
import { va_leave_manage_function } from "../../controllers/va/vaLeaves/vaLeaveManage.controller.js";
import { va_remove_function } from "../../controllers/va/vaProfile/vaDelete.controller.js";
vaRoutes.get("/", va_callback); // VA Callback for getting active VA
@ -42,6 +43,7 @@ vaRoutes.patch("/profile", authValidator, va_profile_update_callback); // Update
vaRoutes.get("/:userName", va_username_callback); // VA Username Callback
vaRoutes.delete("/:id", va_remove_function); // VA Username Callback
// timesheet adjustment routes
vaRoutes.get("/timesheet/summary/:projectId/:date", authValidator, va_timesheet_summary_function); // Get Timesheet Adjustment for VA on specific project and date