Compare commits
No commits in common. "a5f8e7bc7f8ef5a9755d7a02a6f202936d802cb6" and "b9f2eb10b4ef14646046e8978a6274b7bfc84532" have entirely different histories.
a5f8e7bc7f
...
b9f2eb10b4
|
|
@ -492,11 +492,9 @@ async function validateStep(step) {
|
||||||
} else if (step === 1) {
|
} else if (step === 1) {
|
||||||
const email = document.getElementById('email').value;
|
const email = document.getElementById('email').value;
|
||||||
const companyName = document.getElementById('company-name').value;
|
const companyName = document.getElementById('company-name').value;
|
||||||
const password = document.getElementById('password').value;
|
if (!validateEmail(email) && !companyName) {
|
||||||
const confirmPassword = document.getElementById('confirmPassword').value;
|
|
||||||
if (!validateEmail(email) && !companyName && !password && !confirmPassword) {
|
|
||||||
isValid = false;
|
isValid = false;
|
||||||
showError('Fields are Required', null, step);
|
showError('Both Fields are Required', null, step);
|
||||||
let requiredFields1 = form.querySelectorAll('[required]');
|
let requiredFields1 = form.querySelectorAll('[required]');
|
||||||
requiredFields1.forEach(field => {
|
requiredFields1.forEach(field => {
|
||||||
if (!field.value.trim()) {
|
if (!field.value.trim()) {
|
||||||
|
|
@ -524,21 +522,6 @@ async function validateStep(step) {
|
||||||
return isValid;
|
return isValid;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (password.length < 8) {
|
|
||||||
showError('Password must be at least 8 characters long', 'password', step);
|
|
||||||
isValid = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (password !== confirmPassword) {
|
|
||||||
document.getElementById('password-match-error').style.display = 'block';
|
|
||||||
document.getElementById('confirmPassword').classList.add('field-error');
|
|
||||||
showError('Passwords do not match', 'confirmPassword', step);
|
|
||||||
isValid = false;
|
|
||||||
} else {
|
|
||||||
document.getElementById('password-match-error').style.display = 'none';
|
|
||||||
document.getElementById('confirmPassword').classList.remove('field-error');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate other required fields
|
// Validate other required fields
|
||||||
const requiredFields = form.querySelectorAll('[required]');
|
const requiredFields = form.querySelectorAll('[required]');
|
||||||
requiredFields.forEach(field => {
|
requiredFields.forEach(field => {
|
||||||
|
|
@ -629,7 +612,6 @@ function saveStepData(step) {
|
||||||
case 1:
|
case 1:
|
||||||
formData.userDetails.name = document.getElementById('company-name').value;
|
formData.userDetails.name = document.getElementById('company-name').value;
|
||||||
formData.userDetails.email = document.getElementById('email').value;
|
formData.userDetails.email = document.getElementById('email').value;
|
||||||
formData.userDetails.password = document.getElementById('password').value;
|
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
formData.userDetails.address = document.getElementById('address').value;
|
formData.userDetails.address = document.getElementById('address').value;
|
||||||
|
|
@ -1972,52 +1954,4 @@ function preventInvalidAccountHolderChars(event) {
|
||||||
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Function to generate a strong password
|
|
||||||
function generatePassword() {
|
|
||||||
const length = 12;
|
|
||||||
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*";
|
|
||||||
let password = "";
|
|
||||||
|
|
||||||
// Ensure at least one of each required character type
|
|
||||||
password += "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[Math.floor(Math.random() * 26)];
|
|
||||||
password += "abcdefghijklmnopqrstuvwxyz"[Math.floor(Math.random() * 26)];
|
|
||||||
password += "0123456789"[Math.floor(Math.random() * 10)];
|
|
||||||
password += "!@#$%^&*"[Math.floor(Math.random() * 8)];
|
|
||||||
|
|
||||||
// Fill the rest randomly
|
|
||||||
for (let i = password.length; i < length; i++) {
|
|
||||||
password += charset.charAt(Math.floor(Math.random() * charset.length));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shuffle the password
|
|
||||||
password = password.split('').sort(() => 0.5 - Math.random()).join('');
|
|
||||||
|
|
||||||
// Set the password field
|
|
||||||
document.getElementById('password').value = password;
|
|
||||||
document.getElementById('confirmPassword').value = password;
|
|
||||||
|
|
||||||
// Trigger input events to update UI
|
|
||||||
document.getElementById('password').dispatchEvent(new Event('input'));
|
|
||||||
document.getElementById('confirmPassword').dispatchEvent(new Event('input'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to toggle password visibility
|
|
||||||
function togglePasswordVisibility() {
|
|
||||||
const passwordField = document.getElementById('password');
|
|
||||||
const confirmField = document.getElementById('confirmPassword');
|
|
||||||
const toggleButton = document.querySelector('.password-toggle');
|
|
||||||
|
|
||||||
if (passwordField.type === 'password') {
|
|
||||||
passwordField.type = 'text';
|
|
||||||
confirmField.type = 'text';
|
|
||||||
toggleButton.textContent = 'Hide Password';
|
|
||||||
} else {
|
|
||||||
passwordField.type = 'password';
|
|
||||||
confirmField.type = 'password';
|
|
||||||
toggleButton.textContent = 'Show Password';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { VAModel, LoggedModel, ClientModel, ProjectModel } from "../../../models/vaModel.js";
|
import { VAModel, LoggedModel, ClientModel, ProjectModel } from "../../../models/vaModel.js";
|
||||||
import jwt from "jsonwebtoken";
|
import jwt from "jsonwebtoken";
|
||||||
import getDayNumber from "../../../utils/getDayNumber.js";
|
|
||||||
|
|
||||||
// Getting client dashboard data - all VAs, projects, and active VAs
|
// Getting client dashboard data - all VAs, projects, and active VAs
|
||||||
async function client_dashboard(req, res) {
|
async function client_dashboard(req, res) {
|
||||||
|
|
@ -315,11 +314,9 @@ async function client_dashboard(req, res) {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const dayNumber = getDayNumber(logTime);
|
|
||||||
|
|
||||||
// Calculate dashboard statistics
|
// Calculate dashboard statistics
|
||||||
const totalHoursLogged = clientProjects.reduce((sum, p) => sum + (p.totalLoggedHours || 0), 0);
|
const totalHoursLogged = clientProjects.reduce((sum, p) => sum + (p.totalLoggedHours || 0), 0);
|
||||||
const totalDedicatedHours = clientProjects.reduce((sum, p) => sum + (p.dedicatedHours[dayNumber] || 0), 0);
|
const totalDedicatedHours = clientProjects.reduce((sum, p) => sum + (p.dedicatedHours || 0), 0);
|
||||||
|
|
||||||
const currentlyActiveVAs = allVAsFormatted.filter(va => va.isCurrentlyActive);
|
const currentlyActiveVAs = allVAsFormatted.filter(va => va.isCurrentlyActive);
|
||||||
const inactiveVAs = allVAsFormatted.filter(va => !va.isCurrentlyActive);
|
const inactiveVAs = allVAsFormatted.filter(va => !va.isCurrentlyActive);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { ClientModel, ProjectModel, VAModel, LoggedModel } from "../../../models/vaModel.js";
|
import { ClientModel, ProjectModel, VAModel, LoggedModel } from "../../../models/vaModel.js";
|
||||||
import jwt from "jsonwebtoken";
|
import jwt from "jsonwebtoken";
|
||||||
import getDayNumber from "../../../utils/getDayNumber.js";
|
|
||||||
|
|
||||||
function formatHoursToReadable(hours) {
|
function formatHoursToReadable(hours) {
|
||||||
const wholeHours = Math.floor(hours);
|
const wholeHours = Math.floor(hours);
|
||||||
|
|
@ -113,15 +112,13 @@ async function client_dashboard_projects(req, res) {
|
||||||
const validVADetails = assignedVADetails.filter(Boolean);
|
const validVADetails = assignedVADetails.filter(Boolean);
|
||||||
const activeVAsCount = validVADetails.filter(va => va.isActive).length;
|
const activeVAsCount = validVADetails.filter(va => va.isActive).length;
|
||||||
|
|
||||||
const dayNumber = getDayNumber();
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: project._id,
|
id: project._id,
|
||||||
name: project.name,
|
name: project.name,
|
||||||
hourlyRate: project.hourlyRate,
|
hourlyRate: project.hourlyRate,
|
||||||
vaAssigned: validVADetails.length,
|
vaAssigned: validVADetails.length,
|
||||||
activeVAs: activeVAsCount,
|
activeVAs: activeVAsCount,
|
||||||
dedicatedHours: formatHoursToReadable(project.dedicatedHours[dayNumber]) || "0h",
|
dedicatedHours: formatHoursToReadable(project.dedicatedHours) || "0h",
|
||||||
status: project.status || '',
|
status: project.status || '',
|
||||||
assignedVADetails: validVADetails
|
assignedVADetails: validVADetails
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -25,10 +25,10 @@ async function va_register_client(req, res) {
|
||||||
try {
|
try {
|
||||||
const payload = req.body || {};
|
const payload = req.body || {};
|
||||||
const { userDetails } = payload;
|
const { userDetails } = payload;
|
||||||
if (!userDetails || !userDetails.email || !userDetails.name || !userDetails.password) {
|
if (!userDetails || !userDetails.email || !userDetails.name) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: "Missing required fields: name, email, and password are required"
|
message: "Missing required fields: name and email are required"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import bcrypt from "bcrypt";
|
import bcrypt from "bcrypt";
|
||||||
import jwt from "jsonwebtoken";
|
import jwt from "jsonwebtoken";
|
||||||
import { ClientModel, VAModel } from "../../models/vaModel.js";
|
import { ClientModel, VAModel } from "../../models/vaModel.js";
|
||||||
|
import { va_timesheet_adjustment_function } from "../va/vaTimesheetAdjustment/vaTimesheetAdjustment.controller.js";
|
||||||
|
|
||||||
// Login VA
|
// Login VA
|
||||||
async function va_login_client(req, res) {
|
async function va_login_client(req, res) {
|
||||||
|
|
|
||||||
|
|
@ -4,99 +4,99 @@ import { VAModel, LoggedModel, WorkTrackModel } from "../../../models/vaModel.js
|
||||||
import authFetch from "../../../utils/authFetch.js";
|
import authFetch from "../../../utils/authFetch.js";
|
||||||
|
|
||||||
// function to close open work tracks and create logout
|
// function to close open work tracks and create logout
|
||||||
// async function closePreviousSession(vaId, lastLog) {
|
async function closePreviousSession(vaId, lastLog) {
|
||||||
// try {
|
try {
|
||||||
// const currentTime = new Date();
|
const currentTime = new Date();
|
||||||
// // Find all open work tracks for this VA and project (where to is not set or totalTime is 0)
|
// Find all open work tracks for this VA and project (where to is not set or totalTime is 0)
|
||||||
// const openWorkTracks = await WorkTrackModel.find({
|
const openWorkTracks = await WorkTrackModel.find({
|
||||||
// vaId,
|
vaId,
|
||||||
// logId: lastLog._id,
|
logId: lastLog._id,
|
||||||
// $or: [
|
$or: [
|
||||||
// { to: { $exists: false } },
|
{ to: { $exists: false } },
|
||||||
// { totalTime: 0 },
|
{ totalTime: 0 },
|
||||||
// { to: null }
|
{ to: null }
|
||||||
// ]
|
]
|
||||||
// });
|
});
|
||||||
|
|
||||||
// console.log(`Found ${openWorkTracks.length} open work tracks to close`);
|
// console.log(`Found ${openWorkTracks.length} open work tracks to close`);
|
||||||
|
|
||||||
// // Close all open work tracks
|
// Close all open work tracks
|
||||||
// for (const workTrack of openWorkTracks) {
|
for (const workTrack of openWorkTracks) {
|
||||||
// const fromTime = new Date(workTrack.from);
|
const fromTime = new Date(workTrack.from);
|
||||||
// const totalTimeMs = currentTime - fromTime;
|
const totalTimeMs = currentTime - fromTime;
|
||||||
|
|
||||||
// // Update the work track with end time and total time
|
// Update the work track with end time and total time
|
||||||
// workTrack.to = currentTime;
|
workTrack.to = currentTime;
|
||||||
// workTrack.totalTime = totalTimeMs;
|
workTrack.totalTime = totalTimeMs;
|
||||||
// await workTrack.save();
|
await workTrack.save();
|
||||||
|
|
||||||
// // console.log(`Closed work track ${workTrack._id} with duration: ${totalTimeMs}ms`);
|
// console.log(`Closed work track ${workTrack._id} with duration: ${totalTimeMs}ms`);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// // Create a logout log entry
|
// Create a logout log entry
|
||||||
// const logoutLog = await LoggedModel.create({
|
const logoutLog = await LoggedModel.create({
|
||||||
// loggedType: "LOGOUT",
|
loggedType: "LOGOUT",
|
||||||
// vaId,
|
vaId,
|
||||||
// vaEmail: lastLog.vaEmail, // Use email from previous log
|
vaEmail: lastLog.vaEmail, // Use email from previous log
|
||||||
// projectId: lastLog.projectId,
|
projectId: lastLog.projectId,
|
||||||
// time: currentTime // Use same timestamp for consistent logging
|
time: currentTime // Use same timestamp for consistent logging
|
||||||
// });
|
});
|
||||||
|
|
||||||
// // console.log(`Created logout log: ${logoutLog._id}`);
|
// console.log(`Created logout log: ${logoutLog._id}`);
|
||||||
// try {
|
try {
|
||||||
// const salesforceUrl = `${process.env.SF_DOCTRACKER_API_URL}services/apexrest/docva/api/va-project-log`;
|
const salesforceUrl = `${process.env.SF_DOCTRACKER_API_URL}services/apexrest/docva/api/va-project-log`;
|
||||||
// const sfDataSync = {
|
const sfDataSync = {
|
||||||
// "_id": logoutLog._id,
|
"_id": logoutLog._id,
|
||||||
// "loggedType": "LOGOUT",
|
"loggedType": "LOGOUT",
|
||||||
// "vaId": vaId,
|
"vaId": vaId,
|
||||||
// "vaEmail": lastLog.vaEmail,
|
"vaEmail": lastLog.vaEmail,
|
||||||
// "projectId": lastLog.projectId,
|
"projectId": lastLog.projectId,
|
||||||
// "time": logoutLog.time,
|
"time": logoutLog.time,
|
||||||
// }
|
}
|
||||||
|
|
||||||
// const response = await authFetch(salesforceUrl, {
|
const response = await authFetch(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(sfDataSync)
|
body: JSON.stringify(sfDataSync)
|
||||||
// });
|
});
|
||||||
|
|
||||||
// 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 {
|
return {
|
||||||
// closedWorkTracks: openWorkTracks.length,
|
closedWorkTracks: openWorkTracks.length,
|
||||||
// logoutLogId: logoutLog._id,
|
logoutLogId: logoutLog._id,
|
||||||
// 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 {
|
// return {
|
||||||
// // closedWorkTracks: openWorkTracks.length,
|
// closedWorkTracks: openWorkTracks.length,
|
||||||
// // logoutLogId: logoutLog._id,
|
// logoutLogId: logoutLog._id,
|
||||||
// // };
|
// };
|
||||||
|
|
||||||
// } catch (error) {
|
} catch (error) {
|
||||||
// console.error("Error closing previous session:", error);
|
console.error("Error closing previous session:", error);
|
||||||
// throw error;
|
throw error;
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
// Login VA
|
// Login VA
|
||||||
async function va_login_callback(req, res) {
|
async function va_login_callback(req, res) {
|
||||||
|
|
@ -132,17 +132,17 @@ async function va_login_callback(req, res) {
|
||||||
// also on main login when user try to login into app then logout project and workModel track then allow login on same timestamp
|
// also on main login when user try to login into app then logout project and workModel track then allow login on same timestamp
|
||||||
|
|
||||||
// Check if VA has any active sessions (LOGIN without LOGOUT)
|
// Check if VA has any active sessions (LOGIN without LOGOUT)
|
||||||
// const activeSessions = await LoggedModel.find({
|
const activeSessions = await LoggedModel.find({
|
||||||
// vaId: user._id
|
vaId: user._id
|
||||||
// }).sort({ time: -1 });
|
}).sort({ time: -1 });
|
||||||
|
|
||||||
// let closeResult = [];
|
let closeResult = [];
|
||||||
|
|
||||||
// if (activeSessions.length > 0) {
|
if (activeSessions.length > 0) {
|
||||||
// // Get the latest login to check if there's no corresponding logout
|
// Get the latest login to check if there's no corresponding logout
|
||||||
// const latestLog = activeSessions[0];
|
const latestLog = activeSessions[0];
|
||||||
// closeResult = await closePreviousSession(user._id, latestLog);
|
closeResult = await closePreviousSession(user._id, latestLog);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// Generate JWT accessToken with 24h expiry
|
// Generate JWT accessToken with 24h expiry
|
||||||
const accessToken = jwt.sign(
|
const accessToken = jwt.sign(
|
||||||
|
|
@ -172,23 +172,23 @@ async function va_login_callback(req, res) {
|
||||||
|
|
||||||
let responseData = [];
|
let responseData = [];
|
||||||
|
|
||||||
// if (closeResult) {
|
if (closeResult) {
|
||||||
// responseData = {
|
responseData = {
|
||||||
// success: true,
|
success: true,
|
||||||
// message: "Login Successfull",
|
message: "Login Successfull",
|
||||||
// accessToken,
|
accessToken,
|
||||||
// refreshToken,
|
refreshToken,
|
||||||
// user: {
|
user: {
|
||||||
// id: user._id,
|
id: user._id,
|
||||||
// name: user.userDetails.firstName,
|
name: user.userDetails.firstName,
|
||||||
// email: user.userDetails.email,
|
email: user.userDetails.email,
|
||||||
// },
|
},
|
||||||
// previousSession: {
|
previousSession: {
|
||||||
// closedWorkTracks: closeResult.closedWorkTracks,
|
closedWorkTracks: closeResult.closedWorkTracks,
|
||||||
// logoutLogId: closeResult.logoutLogId
|
logoutLogId: closeResult.logoutLogId
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// } else {
|
} else {
|
||||||
responseData = {
|
responseData = {
|
||||||
success: true,
|
success: true,
|
||||||
message: "Login Successfull",
|
message: "Login Successfull",
|
||||||
|
|
@ -200,7 +200,7 @@ async function va_login_callback(req, res) {
|
||||||
email: user.userDetails.email,
|
email: user.userDetails.email,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
// Return accessToken and user info
|
// Return accessToken and user info
|
||||||
return res.status(200).json(responseData);
|
return res.status(200).json(responseData);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { LoggedModel, WorkTrackModel, ProjectModel, VAModel } from "../../../models/vaModel.js";
|
import { LoggedModel, WorkTrackModel, ProjectModel, VAModel } from "../../../models/vaModel.js";
|
||||||
import authFetch from "../../../utils/authFetch.js";
|
import authFetch from "../../../utils/authFetch.js";
|
||||||
import getDayNumber from "../../../utils/getDayNumber.js";
|
|
||||||
|
|
||||||
// Helper function to get start and end of day in UTC
|
// Helper function to get start and end of day in UTC
|
||||||
function getDayRange(date = new Date()) {
|
function getDayRange(date = new Date()) {
|
||||||
|
|
@ -429,14 +428,12 @@ async function va_project_log_callback(req, res) {
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|
||||||
const totalTodayHours = totalTodayMs / (1000 * 60 * 60);
|
const totalTodayHours = totalTodayMs / (1000 * 60 * 60);
|
||||||
|
|
||||||
const dayNumber = getDayNumber(logTime);
|
|
||||||
|
|
||||||
// Check if daily hours are already completed
|
// Check if daily hours are already completed
|
||||||
if (project.dedicatedHours[dayNumber] > 0 && totalTodayHours >= project.dedicatedHours[dayNumber]) {
|
if (project.dedicatedHours > 0 && totalTodayHours >= project.dedicatedHours) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: `Cannot login - daily hours already completed (${formatHoursToReadable(totalTodayHours)} / ${formatHoursToReadable(project.dedicatedHours[dayNumber])})`,
|
message: `Cannot login - daily hours already completed (${formatHoursToReadable(totalTodayHours)} / ${formatHoursToReadable(project.dedicatedHours)})`,
|
||||||
// warnings: scheduleCheck.warnings
|
// warnings: scheduleCheck.warnings
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import jwt from "jsonwebtoken";
|
import jwt from "jsonwebtoken";
|
||||||
import { ProjectModel, VAModel, LoggedModel } from "../../../models/vaModel.js";
|
import { ProjectModel, VAModel, LoggedModel } from "../../../models/vaModel.js";
|
||||||
import getDayNumber from "../../../utils/getDayNumber.js";
|
|
||||||
|
|
||||||
function formatHMS(ms) {
|
function formatHMS(ms) {
|
||||||
const totalSeconds = Math.floor(ms / 1000);
|
const totalSeconds = Math.floor(ms / 1000);
|
||||||
|
|
@ -111,15 +110,13 @@ async function va_projects_log_list_function(req, res) {
|
||||||
const lastLog = logs[logs.length - 1];
|
const lastLog = logs[logs.length - 1];
|
||||||
const activityStatus = lastLog?.loggedType === "LOGIN" ? "ACTIVE" : "AWAY";
|
const activityStatus = lastLog?.loggedType === "LOGIN" ? "ACTIVE" : "AWAY";
|
||||||
|
|
||||||
const dayNumber = getDayNumber();
|
|
||||||
|
|
||||||
finalReport.push({
|
finalReport.push({
|
||||||
projectId: project._id,
|
projectId: project._id,
|
||||||
projectName: project.name,
|
projectName: project.name,
|
||||||
activityStatus,
|
activityStatus,
|
||||||
totalWorkTime: formatHMS(totalWorkMs),
|
totalWorkTime: formatHMS(totalWorkMs),
|
||||||
totalWorkMs,
|
totalWorkMs,
|
||||||
dedicatedHours: formatHoursToReadable(project?.dedicatedHours[dayNumber]) || "0h",
|
dedicatedHours: formatHoursToReadable(project?.dedicatedHours),
|
||||||
totalLogs: logs.length,
|
totalLogs: logs.length,
|
||||||
status: project?.status
|
status: project?.status
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import jwt from "jsonwebtoken";
|
import jwt from "jsonwebtoken";
|
||||||
import { VAModel, ProjectModel, LoggedModel } from "../../../models/vaModel.js";
|
import { VAModel, ProjectModel, LoggedModel } from "../../../models/vaModel.js";
|
||||||
import getDayNumber from "../../../utils/getDayNumber.js";
|
|
||||||
|
|
||||||
async function va_timesheet_summary_function(req, res) {
|
async function va_timesheet_summary_function(req, res) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -163,10 +162,8 @@ async function va_timesheet_summary_function(req, res) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const dayNumber = getDayNumber();
|
|
||||||
|
|
||||||
// Calculate dedicated hours and pending hours
|
// Calculate dedicated hours and pending hours
|
||||||
const dedicatedHours = project.dedicatedHours[dayNumber] || 0;
|
const dedicatedHours = project.dedicatedHours || 0;
|
||||||
const totalWorkedHours = totalWorkSeconds / 3600; // Convert to hours
|
const totalWorkedHours = totalWorkSeconds / 3600; // Convert to hours
|
||||||
const pendingHours = Math.max(0, dedicatedHours - totalWorkedHours);
|
const pendingHours = Math.max(0, dedicatedHours - totalWorkedHours);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ class VaClientService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sanitize and prepare data
|
// Sanitize and prepare data
|
||||||
const clientData = await this.sanitizeClientData(data);
|
const clientData = this.sanitizeClientData(data);
|
||||||
const response = await this.clientRepository.create(clientData);
|
const response = await this.clientRepository.create(clientData);
|
||||||
|
|
||||||
if(response) {
|
if(response) {
|
||||||
|
|
@ -263,17 +263,13 @@ class VaClientService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper method to sanitize data
|
// Helper method to sanitize data
|
||||||
async sanitizeClientData(payload) {
|
sanitizeClientData(payload) {
|
||||||
const { userDetails } = payload;
|
const { userDetails } = payload;
|
||||||
|
|
||||||
const saltRounds = 10;
|
|
||||||
const hashedPassword = await bcrypt.hash(userDetails.password, saltRounds);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
userDetails: {
|
userDetails: {
|
||||||
name: userDetails.name,
|
name: userDetails.name,
|
||||||
email: userDetails.email.toLowerCase(),
|
email: userDetails.email.toLowerCase(),
|
||||||
password: hashedPassword,
|
|
||||||
address: userDetails.address || null,
|
address: userDetails.address || null,
|
||||||
city: userDetails.city || null,
|
city: userDetails.city || null,
|
||||||
state: userDetails.state || null,
|
state: userDetails.state || null,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
function getDayNumber(date = new Date()) {
|
|
||||||
const today = new Date(date);
|
|
||||||
return today.getUTCDay(); // getDay() already returns 0-6 for Sunday-Saturday
|
|
||||||
}
|
|
||||||
|
|
||||||
export default getDayNumber;
|
|
||||||
|
|
@ -111,24 +111,6 @@
|
||||||
<label for="email" class="input-label">Email *</label>
|
<label for="email" class="input-label">Email *</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
|
||||||
<div class="input-container">
|
|
||||||
<input type="password" id="password" class="input-field" placeholder=" " required autocomplete="new-password">
|
|
||||||
<label for="password" class="input-label">Create Password *</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="input-container">
|
|
||||||
<input type="password" id="confirmPassword" class="input-field" placeholder=" " required autocomplete="new-password">
|
|
||||||
<label for="confirmPassword" class="input-label">Confirm Password *</label>
|
|
||||||
</div>
|
|
||||||
<div class="invalid-feedback" id="password-match-error" style="display: none; color: #dc3545; font-size: 12px; margin-top: 5px;">Passwords do not match</div>
|
|
||||||
<div class="password-actions">
|
|
||||||
<button type="button" class="password-toggle" onclick="togglePasswordVisibility()">Show Password</button>
|
|
||||||
<button type="button" class="password-generate" onclick="generatePassword()">Generate Strong Password</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-actions">
|
<div class="form-actions">
|
||||||
<div></div> <!-- Empty div for spacing -->
|
<div></div> <!-- Empty div for spacing -->
|
||||||
<button type="button" class="btn btn-primary" onclick="handleNextStep(2)">Continue</button>
|
<button type="button" class="btn btn-primary" onclick="handleNextStep(2)">Continue</button>
|
||||||
|
|
@ -260,7 +242,7 @@
|
||||||
<div class="error-message" id="step-4-error-message"></div>
|
<div class="error-message" id="step-4-error-message"></div>
|
||||||
</div>
|
</div>
|
||||||
<p style="margin-bottom: 20px; color: #6b7280; font-size: 14px;">
|
<p style="margin-bottom: 20px; color: #6b7280; font-size: 14px;">
|
||||||
We will use the details below in the agreement that we will send to your prior to starting the project.
|
We will use the details below in the agreement that we will send to your prior to entering the project.
|
||||||
</p>
|
</p>
|
||||||
<form id="signatory-form">
|
<form id="signatory-form">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
|
@ -340,7 +322,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="payment-option" onclick="selectPaymentOption('bank')">
|
<div class="payment-option" onclick="selectPaymentOption('bank')">
|
||||||
<h4>Bank Account (ACH)</h4>
|
<h4>Bank Account (ACR)</h4>
|
||||||
<p>Direct bank transfer</p>
|
<p>Direct bank transfer</p>
|
||||||
</div>
|
</div>
|
||||||
<div id="bank-details" class="payment-details">
|
<div id="bank-details" class="payment-details">
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue