Compare commits

..

No commits in common. "c04264f054e3a117f0a4d35565cc8345ce6181c5" and "2ca51468460be4ffb8e124e65a9599657abc046e" have entirely different histories.

4 changed files with 106 additions and 107 deletions

View File

@ -6,7 +6,7 @@
"scripts": { "scripts": {
"start": "nodemon --inspect src/server.js", "start": "nodemon --inspect src/server.js",
"test": "echo \"Error: no test specified\" && exit 1", "test": "echo \"Error: no test specified\" && exit 1",
"deploy": "curl -X POST http://35.162.222.18:9000/hooks/deploy-va-middleware", "deploy": "curl -X POST http://16.170.168.132:9000/hooks/deploy-va-middleware",
"build": "npx webpack --mode production", "build": "npx webpack --mode production",
"dev": "npx webpack --mode development" "dev": "npx webpack --mode development"
}, },

View File

@ -5,7 +5,7 @@ import { VAModel, LeavesModel } from "../../../models/vaModel.js";
async function va_leave_apply_function(req, res) { async function va_leave_apply_function(req, res) {
try { try {
const accessToken = req.headers.token; const accessToken = req.headers.token;
if (!accessToken) { if (!accessToken) {
return res.status(401).json({ success: false, message: "Missing token" }); return res.status(401).json({ success: false, message: "Missing token" });
} }
@ -25,9 +25,9 @@ async function va_leave_apply_function(req, res) {
const { dateFrom, dateTo, leaveType, comment } = req.body; const { dateFrom, dateTo, leaveType, comment } = req.body;
if (!dateFrom || !dateTo || !leaveType || !comment) { if (!dateFrom || !dateTo || !leaveType || !comment) {
return res.status(400).json({ return res.status(400).json({
success: false, success: false,
message: "dateFrom, dateTo, leaveType, and comment are required" message: "dateFrom, dateTo, leaveType, and comment are required"
}); });
} }
@ -36,49 +36,49 @@ async function va_leave_apply_function(req, res) {
try { try {
startDate = new Date(dateFrom); startDate = new Date(dateFrom);
endDate = new Date(dateTo); endDate = new Date(dateTo);
// Set to start of day for accurate calculation // Set to start of day for accurate calculation
startDate.setHours(0, 0, 0, 0); startDate.setHours(0, 0, 0, 0);
endDate.setHours(0, 0, 0, 0); endDate.setHours(0, 0, 0, 0);
if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) { if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) {
return res.status(400).json({ return res.status(400).json({
success: false, success: false,
message: "Invalid date format. Use valid date format (YYYY-MM-DD)" message: "Invalid date format. Use valid date format (YYYY-MM-DD)"
}); });
} }
} catch (error) { } catch (error) {
return res.status(400).json({ return res.status(400).json({
success: false, success: false,
message: "Invalid date format" message: "Invalid date format"
}); });
} }
// Ensure start date is before or equal to end date // Ensure start date is before or equal to end date
if (startDate > endDate) { if (startDate > endDate) {
return res.status(400).json({ return res.status(400).json({
success: false, success: false,
message: "Start date must be before or equal to end date" message: "Start date must be before or equal to end date"
}); });
} }
// Ensure dates are not in the past // Ensure dates are not in the past
const today = new Date(); const today = new Date();
today.setHours(0, 0, 0, 0); today.setHours(0, 0, 0, 0);
if (startDate < today) { if (startDate < today) {
return res.status(400).json({ return res.status(400).json({
success: false, success: false,
message: "Cannot apply for leave for past dates" message: "Cannot apply for leave for past dates"
}); });
} }
// Validate leave type // Validate leave type
const validLeaveTypes = ["SICK", "PTO"]; const validLeaveTypes = ["SICK", "PTO"];
if (!validLeaveTypes.includes(leaveType)) { if (!validLeaveTypes.includes(leaveType)) {
return res.status(400).json({ return res.status(400).json({
success: false, success: false,
message: "Invalid leave type. Must be SICK or PTO" message: "Invalid leave type. Must be SICK or PTO"
}); });
} }
@ -87,9 +87,9 @@ async function va_leave_apply_function(req, res) {
const totalDays = Math.floor(timeDifference / (1000 * 60 * 60 * 24)) + 1; // +1 to include both start and end dates const totalDays = Math.floor(timeDifference / (1000 * 60 * 60 * 24)) + 1; // +1 to include both start and end dates
if (totalDays <= 0) { if (totalDays <= 0) {
return res.status(400).json({ return res.status(400).json({
success: false, success: false,
message: "Invalid date range" message: "Invalid date range"
}); });
} }
@ -106,9 +106,9 @@ async function va_leave_apply_function(req, res) {
}); });
if (existingLeave) { if (existingLeave) {
return res.status(400).json({ return res.status(400).json({
success: false, success: false,
message: "You already have a pending leave request that overlaps with these dates" message: "You already have a pending leave request that overlaps with these dates"
}); });
} }
@ -125,9 +125,9 @@ async function va_leave_apply_function(req, res) {
}); });
if (existingApprovedLeave) { if (existingApprovedLeave) {
return res.status(400).json({ return res.status(400).json({
success: false, success: false,
message: "You already have an approved leave that overlaps with these dates" message: "You already have an approved leave that overlaps with these dates"
}); });
} }
@ -147,95 +147,94 @@ async function va_leave_apply_function(req, res) {
await leaveRequest.save(); await leaveRequest.save();
try { try {
const salesforceUrl = `${process.env.SF_DOCTRACKER_API_URL}services/apexrest/docva/api/leave-request`; // const salesforceUrl = `${process.env.SF_DOCTRACKER_API_URL}services/apexrest/docva/api/timesheet-adjustment`;
const sfData = { // const sfData = {
"leaveId": leaveRequest._id.toString(), // "id": leaveRequest._id,
// "va": { // "va": {
// "id": vaId // "id": vaId
// }, // },
"va": vaId.toString(), // "dateFrom": startDate.toISOString().split('T')[0],
"dateFrom": startDate.toISOString().split('T')[0], // "dateTo": endDate.toISOString().split('T')[0],
"dateTo": endDate.toISOString().split('T')[0], // "totalDays": totalDays,
"totalDays": totalDays, // "leaveType": leaveType,
"leaveType": leaveType, // "comment": processedComment.plainText, // Send plain text to Salesforce
"comment": processedComment.plainText, // Send plain text to Salesforce // "attachments": processedComment.extractedImages, // Send extracted images info
"attachments": processedComment.extractedImages, // Send extracted images info // "status": "PENDING",
"status": "PENDING", // "rejectionReason": ""
"rejectionReason": "" // }
}
const response = await fetch(salesforceUrl, { // const response = await fetch(salesforceUrl, {
method: 'POST', // method: 'POST',
headers: { // headers: {
'Content-Type': 'application/json', // 'Content-Type': 'application/json',
// Add any required headers for Salesforce authentication // // Add any required headers for Salesforce authentication
// 'Authorization': 'Bearer ' + process.env.SF_ACCESS_TOKEN // // 'Authorization': 'Bearer ' + process.env.SF_ACCESS_TOKEN
}, // },
body: JSON.stringify({ "data": sfData }) // body: JSON.stringify({"data": sfData})
}); // });
if (!response.ok) { // if (!response.ok) {
const errorText = await response.text(); // const errorText = await response.text();
throw new Error(`Salesforce API responded with status ${response.status}: ${errorText}`); // throw new Error(`Salesforce API responded with status ${response.status}: ${errorText}`);
} // }
// const result = await response.json(); // const result = await response.json();
// console.log("✅ Successfully synced VA data to Salesforce:", result); // // console.log("✅ Successfully synced VA data to Salesforce:", result);
// return res.status(201).json({ return res.status(201).json({
// success: true, success: true,
// message: "Leave request submitted successfully", message: "Leave request submitted successfully",
// data: { data: {
// leaveId: leaveRequest._id, leaveId: leaveRequest._id,
// dateFrom: startDate.toISOString().split('T')[0], dateFrom: startDate.toISOString().split('T')[0],
// dateTo: endDate.toISOString().split('T')[0], dateTo: endDate.toISOString().split('T')[0],
// totalDays: totalDays, totalDays: totalDays,
// leaveType, leaveType,
// comment: processedComment.processedHTML, comment: processedComment.processedHTML,
// // attachments: processedComment.extractedImages, // attachments: processedComment.extractedImages,
// status: "PENDING" status: "PENDING"
// }, },
// // SyncSalesforce: result // SyncSalesforce: result
// }); });
} catch (error) { } catch (error) {
console.error("Failed to sync with Salesforce:", error.message); console.error("Failed to sync with Salesforce:", error.message);
console.log(error) console.log(error)
// Create a custom error to distinguish Salesforce errors // Create a custom error to distinguish Salesforce errors
const salesforceError = new Error(`Salesforce sync failed: ${error.message}`); const salesforceError = new Error(`Salesforce sync failed: ${error.message}`);
salesforceError.isSalesforceError = true; salesforceError.isSalesforceError = true;
throw salesforceError; throw salesforceError;
} }
return res.status(201).json({ // return res.status(201).json({
success: true, // success: true,
message: "Leave request submitted successfully", // message: "Leave request submitted successfully",
data: { // data: {
leaveId: leaveRequest._id, // leaveId: leaveRequest._id,
dateFrom: startDate.toISOString().split('T')[0], // dateFrom: startDate.toISOString().split('T')[0],
dateTo: endDate.toISOString().split('T')[0], // dateTo: endDate.toISOString().split('T')[0],
totalDays: totalDays, // totalDays: totalDays,
leaveType, // leaveType,
comment, // comment,
status: "PENDING" // status: "PENDING"
} // }
}); // });
} catch (error) { } catch (error) {
console.error("Leave apply error:", error); console.error("Leave apply error:", error);
if (error.name === 'JsonWebTokenError') { if (error.name === 'JsonWebTokenError') {
return res.status(401).json({ success: false, message: "Invalid token" }); return res.status(401).json({ success: false, message: "Invalid token" });
} }
if (error.name === 'TokenExpiredError') { if (error.name === 'TokenExpiredError') {
return res.status(401).json({ success: false, message: "Token expired" }); return res.status(401).json({ success: false, message: "Token expired" });
} }
return res.status(500).json({ return res.status(500).json({
success: false, success: false,
message: "Internal Server Error", message: "Internal Server Error",
error: error.message error: error.message
}); });
} }
} }
@ -278,7 +277,7 @@ function extractPlainTextFromHTML(html) {
.replace(/<[^>]*>/g, ' ') // Replace tags with spaces .replace(/<[^>]*>/g, ' ') // Replace tags with spaces
.replace(/\s+/g, ' ') // Collapse multiple spaces .replace(/\s+/g, ' ') // Collapse multiple spaces
.trim(); .trim();
// Decode common HTML entities // Decode common HTML entities
plainText = plainText plainText = plainText
.replace(/&nbsp;/g, ' ') .replace(/&nbsp;/g, ' ')
@ -305,7 +304,7 @@ function extractAndProcessBase64Images(html) {
try { try {
// Regex to find base64 images in HTML (various formats) // Regex to find base64 images in HTML (various formats)
const base64Regex = /<img[^>]+src="data:image\/([a-zA-Z0-9+]+);base64,([^"]+)"[^>]*>/gi; const base64Regex = /<img[^>]+src="data:image\/([a-zA-Z0-9+]+);base64,([^"]+)"[^>]*>/gi;
let match; let match;
let imageIndex = 0; let imageIndex = 0;
@ -317,7 +316,7 @@ function extractAndProcessBase64Images(html) {
// Create a unique filename // Create a unique filename
const timestamp = Date.now(); const timestamp = Date.now();
const filename = `leave_image_${timestamp}_${imageIndex}.${imageType}`; const filename = `leave_image_${timestamp}_${imageIndex}.${imageType}`;
// Calculate approximate file size // Calculate approximate file size
const size = Math.floor((base64Data.length * 3) / 4); // Base64 size calculation const size = Math.floor((base64Data.length * 3) / 4); // Base64 size calculation
@ -333,7 +332,7 @@ function extractAndProcessBase64Images(html) {
// Replace base64 with a reference in HTML // Replace base64 with a reference in HTML
const replacementHTML = `<img src="attachment://${filename}" alt="Leave evidence ${imageIndex + 1}" data-filename="${filename}" data-image-index="${imageIndex}" class="leave-image">`; const replacementHTML = `<img src="attachment://${filename}" alt="Leave evidence ${imageIndex + 1}" data-filename="${filename}" data-image-index="${imageIndex}" class="leave-image">`;
processedHTML = processedHTML.replace(fullMatch, replacementHTML); processedHTML = processedHTML.replace(fullMatch, replacementHTML);
imageIndex++; imageIndex++;

View File

@ -138,12 +138,12 @@ async function va_timesheet_adjustment_function(req, res) {
try { try {
const salesforceUrl = `${process.env.SF_DOCTRACKER_API_URL}services/apexrest/docva/api/timesheet-adjustment`; const salesforceUrl = `${process.env.SF_DOCTRACKER_API_URL}services/apexrest/docva/api/timesheet-adjustment`;
const sfData = { const sfData = {
"id": adjustmentRequest._id.toString(), "id": adjustmentRequest._id,
"va": { "va": {
"id": vaId.toString() "id": vaId
}, },
"project": { "project": {
"id": projectId.toString() "id": projectId
}, },
"date": adjustmentDate.toISOString().split('T')[0], "date": adjustmentDate.toISOString().split('T')[0],
"loginTime": loginDateTime.toISOString(), "loginTime": loginDateTime.toISOString(),

View File

@ -4,7 +4,7 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<title>DocTracker</title> <title>Download DocTracker</title>
<style> <style>
body { body {