Compare commits
5 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
066010efac | |
|
|
7ecf177467 | |
|
|
1cdb7a1351 | |
|
|
e96986923e | |
|
|
de77d795f3 |
|
|
@ -631,7 +631,7 @@ select,
|
||||||
.data-table tbody tr {
|
.data-table tbody tr {
|
||||||
border-bottom: 1px solid var(--border-color);
|
border-bottom: 1px solid var(--border-color);
|
||||||
transition: background-color 0.2s;
|
transition: background-color 0.2s;
|
||||||
/* cursor: pointer; */
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.data-table tbody tr:hover {
|
.data-table tbody tr:hover {
|
||||||
|
|
@ -2226,3 +2226,12 @@ tr[data-vaid]:hover {
|
||||||
background-color: rgba(16, 185, 129, 0.1);
|
background-color: rgba(16, 185, 129, 0.1);
|
||||||
color: var(--color-success);
|
color: var(--color-success);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.field-error {
|
||||||
|
border: 1px solid var(--error-color) !important;
|
||||||
|
background-color: rgba(220, 38, 38, 0.05) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .field-error {
|
||||||
|
background-color: rgba(220, 38, 38, 0.1) !important;
|
||||||
|
}
|
||||||
|
|
@ -24,7 +24,9 @@ function openVAModal(va) {
|
||||||
|
|
||||||
// Set projects
|
// Set projects
|
||||||
const projects = va.projects || [];
|
const projects = va.projects || [];
|
||||||
document.getElementById('projectsCountBadge').textContent = projects.length;
|
if(document.getElementById('projectsCountBadge')) {
|
||||||
|
document.getElementById('projectsCountBadge').textContent = projects.length;
|
||||||
|
}
|
||||||
|
|
||||||
const projectsTableBody = document.getElementById('modalProjectsTableBody');
|
const projectsTableBody = document.getElementById('modalProjectsTableBody');
|
||||||
const noProjectsMessage = document.getElementById('modalNoProjects');
|
const noProjectsMessage = document.getElementById('modalNoProjects');
|
||||||
|
|
@ -45,9 +47,13 @@ function openVAModal(va) {
|
||||||
|
|
||||||
if (va.isCurrentlyActive && va.lastProjectId) {
|
if (va.isCurrentlyActive && va.lastProjectId) {
|
||||||
viewSessionBtn.href = `/live/${va.lastProjectId}/${va.id}`;
|
viewSessionBtn.href = `/live/${va.lastProjectId}/${va.id}`;
|
||||||
modalActions.style.display = 'block';
|
if(modalActions){
|
||||||
|
modalActions.style.display = 'block';
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
modalActions.style.display = 'none';
|
if(modalActions){
|
||||||
|
modalActions.style.display = 'none';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show modal
|
// Show modal
|
||||||
|
|
@ -161,7 +167,7 @@ async function loadDashboardData() {
|
||||||
document.querySelectorAll('.va-row').forEach((row, index) => {
|
document.querySelectorAll('.va-row').forEach((row, index) => {
|
||||||
row.addEventListener('click', function (e) {
|
row.addEventListener('click', function (e) {
|
||||||
// Don't open modal if clicking on view link
|
// Don't open modal if clicking on view link
|
||||||
if (!e.target.closest('.view-link')) {
|
if (!e.target.closest('.view-link, .va-logs')) {
|
||||||
const va = allVAs[index];
|
const va = allVAs[index];
|
||||||
openVAModal(va);
|
openVAModal(va);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,17 @@ const API_BASE = '/api/v1/client';
|
||||||
const vaCreditsTableBody = document.getElementById("vaCreditTableBody");
|
const vaCreditsTableBody = document.getElementById("vaCreditTableBody");
|
||||||
const vaCreditsEmptyState = document.getElementById("vaEmptyCreditState");
|
const vaCreditsEmptyState = document.getElementById("vaEmptyCreditState");
|
||||||
|
|
||||||
|
function formatDateTime(dateTimeStr) {
|
||||||
|
const date = new Date(dateTimeStr);
|
||||||
|
return date.toLocaleDateString();
|
||||||
|
// return date.toLocaleDateString() + ' ' + date.toLocaleTimeString();
|
||||||
|
}
|
||||||
|
|
||||||
function createCreditRow(credit) {
|
function createCreditRow(credit) {
|
||||||
return `
|
return `
|
||||||
<tr data-credit-id="${credit.id}">
|
<tr data-credit-id="${credit.id}">
|
||||||
<td><span class="status ${credit.status.toLowerCase()}">${credit.status}</span></td>
|
<td><span class="status ${credit.status.toLowerCase()}">${credit.status}</span></td>
|
||||||
<td>${credit.date}</td>
|
<td>${formatDateTime(credit.date)}</td>
|
||||||
<td>${credit.comment}</td>
|
<td>${credit.comment}</td>
|
||||||
<td>$${credit.creditAmount}</td>
|
<td>$${credit.creditAmount}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
||||||
|
|
@ -53,17 +53,39 @@ async function client_dashboard_projects(req, res) {
|
||||||
const allProjectVaIds = [];
|
const allProjectVaIds = [];
|
||||||
clientProjects.forEach(project => {
|
clientProjects.forEach(project => {
|
||||||
if (project.vaAssigned && project.vaAssigned.length > 0) {
|
if (project.vaAssigned && project.vaAssigned.length > 0) {
|
||||||
allProjectVaIds.push(...project.vaAssigned);
|
// allProjectVaIds.push(...project.vaAssigned);
|
||||||
|
project.vaAssigned.forEach(vaItem => {
|
||||||
|
// Handle different possible structures
|
||||||
|
if (vaItem._id) {
|
||||||
|
allProjectVaIds.push(vaItem._id);
|
||||||
|
} else if (vaItem.vaId) {
|
||||||
|
allProjectVaIds.push(vaItem.vaId);
|
||||||
|
} else if (typeof vaItem === 'string') {
|
||||||
|
allProjectVaIds.push(vaItem);
|
||||||
|
} else if (vaItem.id) {
|
||||||
|
allProjectVaIds.push(vaItem.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// STEP 3: Get all VAs assigned to these projects
|
// STEP 3: Get all VAs assigned to these projects
|
||||||
const allAssignedVAs = await VAModel.find({
|
// const allAssignedVAs = await VAModel.find({
|
||||||
_id: { $in: allProjectVaIds.vaId }
|
// _id: { $in: allProjectVaIds.vaId }
|
||||||
})
|
// })
|
||||||
.select('userDetails.firstName userDetails.lastName userDetails.email status')
|
// .select('userDetails.firstName userDetails.lastName userDetails.email status')
|
||||||
.lean()
|
// .lean()
|
||||||
.exec();
|
// .exec();
|
||||||
|
|
||||||
|
let allAssignedVAs = [];
|
||||||
|
if (allProjectVaIds.length > 0) {
|
||||||
|
allAssignedVAs = await VAModel.find({
|
||||||
|
_id: { $in: allProjectVaIds }
|
||||||
|
})
|
||||||
|
.select('userDetails.firstName userDetails.lastName userDetails.email status')
|
||||||
|
.lean()
|
||||||
|
.exec();
|
||||||
|
}
|
||||||
|
|
||||||
// Create a map for quick VA lookup
|
// Create a map for quick VA lookup
|
||||||
const vaMap = allAssignedVAs.reduce((acc, va) => {
|
const vaMap = allAssignedVAs.reduce((acc, va) => {
|
||||||
|
|
@ -79,9 +101,28 @@ async function client_dashboard_projects(req, res) {
|
||||||
const assignedVAIds = project.vaAssigned || [];
|
const assignedVAIds = project.vaAssigned || [];
|
||||||
|
|
||||||
// Get VA details and check activity for each assigned VA
|
// Get VA details and check activity for each assigned VA
|
||||||
const assignedVADetails = await Promise.all(assignedVAIds.map(async (vaId) => {
|
const assignedVADetails = await Promise.all(assignedVAIds.map(async (vaItem) => {
|
||||||
|
// const va = vaMap[vaId.toString()];
|
||||||
|
// if (!va) return null;
|
||||||
|
|
||||||
|
let vaId;
|
||||||
|
if (vaItem._id) {
|
||||||
|
vaId = vaItem._id;
|
||||||
|
} else if (vaItem.vaId) {
|
||||||
|
vaId = vaItem.vaId;
|
||||||
|
} else if (typeof vaItem === 'string') {
|
||||||
|
vaId = vaItem;
|
||||||
|
} else if (vaItem.id) {
|
||||||
|
vaId = vaItem.id;
|
||||||
|
} else {
|
||||||
|
vaId = vaItem; // Try as-is
|
||||||
|
}
|
||||||
|
|
||||||
const va = vaMap[vaId.toString()];
|
const va = vaMap[vaId.toString()];
|
||||||
if (!va) return null;
|
if (!va) {
|
||||||
|
console.log(`VA with ID ${vaId} not found in vaMap`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if this VA is currently active on THIS specific project
|
// Check if this VA is currently active on THIS specific project
|
||||||
const latestLog = await LoggedModel.findOne({
|
const latestLog = await LoggedModel.findOne({
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
import { ClientModel } from "../../../models/vaModel.js";
|
||||||
|
import authFetch from "../../../utils/authFetch.js";
|
||||||
|
|
||||||
|
async function update_client_bank_details(req, res) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
const clientId = req.body.clientId;
|
||||||
|
const clientEmail = req.body.clientEmail;
|
||||||
|
const payload = req.body || {};
|
||||||
|
const { userDetails } = payload;
|
||||||
|
|
||||||
|
const client = await ClientModel.findById(clientId);
|
||||||
|
if (!client) {
|
||||||
|
return res.status(404).json({
|
||||||
|
success: false,
|
||||||
|
message: "Client not found"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!userDetails) {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
message: "Missing userDetails in request body"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
userDetails._id = clientId; // Ensure the _id is set to clientId
|
||||||
|
userDetails.email = clientEmail; // Ensure the email is set to clientEmail
|
||||||
|
|
||||||
|
// console.log("Final userDetails to update:", userDetails);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const salesforceUrl = `${process.env.SF_DOCTRACKER_API_URL}/services/apexrest/docva/api/client`;
|
||||||
|
|
||||||
|
const response = await authFetch(salesforceUrl, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ userDetails })
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorText = await response.text();
|
||||||
|
throw new Error(`Salesforce API responded with status ${response.status}: ${errorText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
// console.log("✅ Successfully synced VA data to Salesforce:", result);
|
||||||
|
|
||||||
|
return res.status(200).json({
|
||||||
|
success: true,
|
||||||
|
message: "Client bank details updated and synced with Salesforce successfully",
|
||||||
|
data: result
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to sync with Salesforce:", error.message);
|
||||||
|
console.log(error)
|
||||||
|
|
||||||
|
// Create a custom error to distinguish Salesforce errors
|
||||||
|
const salesforceError = new Error(`Salesforce sync failed: ${error.message}`);
|
||||||
|
salesforceError.isSalesforceError = true;
|
||||||
|
throw salesforceError;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
if (error.message === "Client not found") {
|
||||||
|
return res.status(404).json({
|
||||||
|
success: false,
|
||||||
|
message: error.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
message: "Internal Server Error"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { update_client_bank_details };
|
||||||
|
|
@ -16,6 +16,7 @@ import { client_logout_callback } from "../../controllers/client/dashboard/clien
|
||||||
import { va_tips_delete, va_tips_get, va_tips_manage_function, va_tips_update } from "../../controllers/va/vaTips/vaTipsManage.controller.js";
|
import { va_tips_delete, va_tips_get, va_tips_manage_function, va_tips_update } from "../../controllers/va/vaTips/vaTipsManage.controller.js";
|
||||||
import { clientDashboardGetVaLogsController } from "../../controllers/client/dashboard/clientDashboardGetVaLogs.controller.js";
|
import { clientDashboardGetVaLogsController } from "../../controllers/client/dashboard/clientDashboardGetVaLogs.controller.js";
|
||||||
import { va_credits_get } from "../../controllers/client/dashboard/clientDashboardCredits.controller.js";
|
import { va_credits_get } from "../../controllers/client/dashboard/clientDashboardCredits.controller.js";
|
||||||
|
import { update_client_bank_details } from "../../controllers/client/dashboard/clientUpdateBankDetails.controller.js";
|
||||||
|
|
||||||
// clients routes
|
// clients routes
|
||||||
clientRoutes.post("/register", va_register_client); // Register a new VA
|
clientRoutes.post("/register", va_register_client); // Register a new VA
|
||||||
|
|
@ -43,6 +44,7 @@ clientRoutes.post("/dashboard/va-report", clientAuthValidator, client_dashboard_
|
||||||
clientRoutes.get("/dashboard-projects", clientAuthValidator, client_dashboard_projects); // VA Clients Projects Callback
|
clientRoutes.get("/dashboard-projects", clientAuthValidator, client_dashboard_projects); // VA Clients Projects Callback
|
||||||
clientRoutes.get("/profile", clientAuthValidator, get_client_dashboard_profile);
|
clientRoutes.get("/profile", clientAuthValidator, get_client_dashboard_profile);
|
||||||
clientRoutes.patch("/profile", clientAuthValidator, update_client_password);
|
clientRoutes.patch("/profile", clientAuthValidator, update_client_password);
|
||||||
|
clientRoutes.post("/profile", clientAuthValidator, update_client_bank_details);
|
||||||
clientRoutes.patch("/:clientId", update_client);
|
clientRoutes.patch("/:clientId", update_client);
|
||||||
clientRoutes.delete("/:clientId", delete_client);
|
clientRoutes.delete("/:clientId", delete_client);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue