Updated track logs api
This commit is contained in:
parent
4ed62b25e8
commit
645adea832
|
|
@ -670,6 +670,13 @@ body {
|
|||
background-color: rgba(220, 38, 38, 0.1) !important;
|
||||
}
|
||||
|
||||
.additional-info-heading {
|
||||
margin-top: 24px;
|
||||
margin-bottom: 16px;
|
||||
border-top: 1px solid #e5e7eb;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
/* Animations */
|
||||
@keyframes slideDown {
|
||||
from {
|
||||
|
|
|
|||
|
|
@ -617,11 +617,11 @@
|
|||
} else {
|
||||
const paymentMethod = selectedPayment.getAttribute('onclick').match(/'([^']+)'/)[1];
|
||||
if (paymentMethod === 'payoneer') {
|
||||
// const payoneerAccount = document.getElementById('payoneerAccount').value;
|
||||
// if (!payoneerAccount) {
|
||||
// showError('Please enter your Payoneer account number', 'payoneerAccount', step);
|
||||
// isValid = false;
|
||||
// }
|
||||
const payoneerAccount = document.getElementById('payoneerAccount').value;
|
||||
if (!payoneerAccount) {
|
||||
showError('Please enter your Payoneer account number', 'payoneerAccount', step);
|
||||
isValid = false;
|
||||
}
|
||||
} else if (paymentMethod === 'veem') {
|
||||
const veemAccount = document.getElementById('veemAccount').value;
|
||||
if (!veemAccount) {
|
||||
|
|
@ -703,9 +703,9 @@
|
|||
const paymentMethod = selectedPayment.getAttribute('onclick').match(/'([^']+)'/)[1];
|
||||
formData.userDetails.paymentMethod = paymentMethod;
|
||||
|
||||
// if (document.getElementById('payoneerAccount').value) {
|
||||
// formData.userDetails.payoneerAccount = document.getElementById('payoneerAccount').value;
|
||||
// }
|
||||
if (document.getElementById('payoneerAccount').value) {
|
||||
formData.userDetails.payoneerAccount = document.getElementById('payoneerAccount').value;
|
||||
}
|
||||
if (document.getElementById('veemAccount').value) {
|
||||
formData.userDetails.veemAccount = document.getElementById('veemAccount').value;
|
||||
}
|
||||
|
|
@ -734,7 +734,7 @@
|
|||
|
||||
// Show appropriate payment details
|
||||
if (method === 'payoneer') {
|
||||
// document.getElementById('payoneer-details').style.display = 'block';
|
||||
document.getElementById('payoneer-details').style.display = 'block';
|
||||
} else if (method === 'veem') {
|
||||
document.getElementById('veem-details').style.display = 'block';
|
||||
} else if (method === 'wise') {
|
||||
|
|
|
|||
|
|
@ -26,140 +26,147 @@ function formatHoursToReadable(hours) {
|
|||
}
|
||||
}
|
||||
|
||||
// Helper function to check daily hours and create logout log if needed
|
||||
async function checkDailyHoursAndLogout(vaId, vaEmail, projectId, project, currentSessionId = null, additionalTimeMs = 0) {
|
||||
try {
|
||||
const today = new Date();
|
||||
const { start, end } = getDayRange(today);
|
||||
// // Helper function to check daily hours and create logout log if needed
|
||||
// async function checkDailyHoursAndLogout(vaId, vaEmail, projectId, project, currentSessionId = null, additionalTimeMs = 0) {
|
||||
// try {
|
||||
// const today = new Date();
|
||||
// const { start, end } = getDayRange(today);
|
||||
|
||||
// Get today's work entries for this VA and project
|
||||
// Exclude the current session if it's already in the database
|
||||
const query = {
|
||||
vaId,
|
||||
projectId,
|
||||
from: { $gte: start, $lte: end }
|
||||
};
|
||||
// // Get today's work entries for this VA and project
|
||||
// // Exclude the current session if it's already in the database
|
||||
// const query = {
|
||||
// vaId,
|
||||
// projectId,
|
||||
// from: { $gte: start, $lte: end }
|
||||
// };
|
||||
|
||||
if (currentSessionId) {
|
||||
query._id = { $ne: currentSessionId };
|
||||
}
|
||||
// if (currentSessionId) {
|
||||
// query._id = { $ne: currentSessionId };
|
||||
// }
|
||||
|
||||
const todayWorkEntries = await WorkTrackModel.find(query);
|
||||
// const todayWorkEntries = await WorkTrackModel.find(query);
|
||||
|
||||
// Calculate total hours worked today
|
||||
let totalTodayMs = todayWorkEntries.reduce((total, entry) => {
|
||||
return total + (entry.totalTime || 0);
|
||||
}, 0);
|
||||
// // Calculate total hours worked today
|
||||
// let totalTodayMs = todayWorkEntries.reduce((total, entry) => {
|
||||
// return total + (entry.totalTime || 0);
|
||||
// }, 0);
|
||||
|
||||
// Add the additional time from current session if provided
|
||||
totalTodayMs += additionalTimeMs;
|
||||
// // Add the additional time from current session if provided
|
||||
// totalTodayMs += additionalTimeMs;
|
||||
|
||||
const totalTodayHours = totalTodayMs / (1000 * 60 * 60);
|
||||
// const totalTodayHours = totalTodayMs / (1000 * 60 * 60);
|
||||
|
||||
// Check if daily dedicated hours are completed
|
||||
if (project.dedicatedHours > 0 && totalTodayHours >= project.dedicatedHours) {
|
||||
// Create automatic logout log
|
||||
const logoutLog = await LoggedModel.create({
|
||||
loggedType: "LOGOUT",
|
||||
vaId,
|
||||
vaEmail,
|
||||
projectId
|
||||
});
|
||||
// // Check if daily dedicated hours are completed
|
||||
// if (project.dedicatedHours > 0 && totalTodayHours >= project.dedicatedHours) {
|
||||
// // Create automatic logout log
|
||||
// const logoutLog = await LoggedModel.create({
|
||||
// loggedType: "LOGOUT",
|
||||
// vaId,
|
||||
// vaEmail,
|
||||
// projectId
|
||||
// });
|
||||
|
||||
try {
|
||||
const salesforceUrl = `${process.env.SF_DOCTRACKER_API_URL}services/apexrest/docva/api/va-project-log`;
|
||||
const sfDataSync = {
|
||||
"_id": logoutLog._id,
|
||||
"loggedType": "LOGOUT",
|
||||
"vaId": vaId,
|
||||
"vaEmail": vaEmail,
|
||||
"projectId": projectId,
|
||||
"time": logoutLog.time,
|
||||
}
|
||||
// try {
|
||||
// const salesforceUrl = `${process.env.SF_DOCTRACKER_API_URL}services/apexrest/docva/api/va-project-log`;
|
||||
// const sfDataSync = {
|
||||
// "_id": logoutLog._id,
|
||||
// "loggedType": "LOGOUT",
|
||||
// "vaId": vaId,
|
||||
// "vaEmail": vaEmail,
|
||||
// "projectId": projectId,
|
||||
// "time": logoutLog.time,
|
||||
// }
|
||||
|
||||
const response = await fetch(salesforceUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
// Add any required headers for Salesforce authentication
|
||||
// 'Authorization': 'Bearer ' + process.env.SF_ACCESS_TOKEN
|
||||
},
|
||||
body: JSON.stringify(sfDataSync)
|
||||
});
|
||||
// const response = await fetch(salesforceUrl, {
|
||||
// method: 'POST',
|
||||
// headers: {
|
||||
// 'Content-Type': 'application/json',
|
||||
// // Add any required headers for Salesforce authentication
|
||||
// // 'Authorization': 'Bearer ' + process.env.SF_ACCESS_TOKEN
|
||||
// },
|
||||
// body: JSON.stringify(sfDataSync)
|
||||
// });
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
throw new Error(`Salesforce API responded with status ${response.status}: ${errorText}`);
|
||||
}
|
||||
// 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);
|
||||
// const result = await response.json();
|
||||
// // console.log("✅ Successfully synced VA data to Salesforce:", result);
|
||||
|
||||
return {
|
||||
shouldLogout: true,
|
||||
totalTodayHours: totalTodayHours,
|
||||
dedicatedHours: project.dedicatedHours,
|
||||
logoutLogId: logoutLog._id,
|
||||
SyncSalesforce: result
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Failed to sync with Salesforce:", error.message);
|
||||
console.log(error)
|
||||
// return {
|
||||
// shouldLogout: true,
|
||||
// totalTodayHours: totalTodayHours,
|
||||
// dedicatedHours: project.dedicatedHours,
|
||||
// logoutLogId: logoutLog._id,
|
||||
// SyncSalesforce: 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;
|
||||
}
|
||||
}
|
||||
// // Create a custom error to distinguish Salesforce errors
|
||||
// const salesforceError = new Error(`Salesforce sync failed: ${error.message}`);
|
||||
// salesforceError.isSalesforceError = true;
|
||||
// throw salesforceError;
|
||||
// }
|
||||
// }
|
||||
|
||||
return {
|
||||
shouldLogout: false,
|
||||
totalTodayHours: totalTodayHours,
|
||||
dedicatedHours: project.dedicatedHours
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error in checkDailyHoursAndLogout:", error);
|
||||
return { shouldLogout: false, error: error.message };
|
||||
}
|
||||
}
|
||||
// return {
|
||||
// shouldLogout: false,
|
||||
// totalTodayHours: totalTodayHours,
|
||||
// dedicatedHours: project.dedicatedHours
|
||||
// };
|
||||
// } catch (error) {
|
||||
// console.error("Error in checkDailyHoursAndLogout:", error);
|
||||
// return { shouldLogout: false, error: error.message };
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
// Getting logged hours in a specific project of VA
|
||||
async function va_log_track_work_callback(req, res) {
|
||||
try {
|
||||
const accessToken = req.headers.token;
|
||||
if (!accessToken) {
|
||||
return res.status(401).json({ success: false, message: "Missing token" });
|
||||
}
|
||||
// const accessToken = req.headers.token;
|
||||
// if (!accessToken) {
|
||||
// return res.status(401).json({ success: false, message: "Missing token" });
|
||||
// }
|
||||
|
||||
const decoded = jwt.verify(accessToken, process.env.JWT_SECRET_ACCESS_TOKEN);
|
||||
// const decoded = jwt.verify(accessToken, process.env.JWT_SECRET_ACCESS_TOKEN);
|
||||
|
||||
if (!decoded || !decoded.email) {
|
||||
return res.status(401).json({ success: false, message: "Invalid token" });
|
||||
}
|
||||
// if (!decoded || !decoded.email) {
|
||||
// return res.status(401).json({ success: false, message: "Invalid token" });
|
||||
// }
|
||||
|
||||
const vaEmail = decoded.email;
|
||||
const vaUser = await VAModel.findOne({ "userDetails.email": vaEmail });
|
||||
if (!vaUser) {
|
||||
return res.status(404).json({ success: false, message: "VA not found" });
|
||||
}
|
||||
// const vaEmail = decoded.email;
|
||||
// const vaUser = await VAModel.findOne({ "userDetails.email": vaEmail });
|
||||
// if (!vaUser) {
|
||||
// return res.status(404).json({ success: false, message: "VA not found" });
|
||||
// }
|
||||
|
||||
const vaId = vaUser._id;
|
||||
// const vaId = vaUser._id;
|
||||
const payload = req.body || {};
|
||||
|
||||
let { projectId, logId, title, appName, url = null } = payload;
|
||||
|
||||
if (title === "") {
|
||||
title = ""
|
||||
let { vaId, projectId, logId, logs } = payload;
|
||||
|
||||
if (!projectId || !logId || !vaId || !Array.isArray(logs)) {
|
||||
return res.status(400).json({ success: false, message: "Missing Props" });
|
||||
}
|
||||
|
||||
const vaUser = await VAModel.findById(vaId);
|
||||
if (!vaUser) return res.status(400).json({ success: false, message: "VA not found" });
|
||||
|
||||
const project = await ProjectModel.findById(projectId);
|
||||
if (!project) return res.status(400).json({ success: false, message: "Project not found" });
|
||||
|
||||
const logEntry = await LoggedModel.findById(logId);
|
||||
if (!logEntry) return res.status(400).json({ success: false, message: "Log entry not found" });
|
||||
|
||||
if (logs.length === 0) {
|
||||
return res.status(400).json({ success: false, message: "No logs to track" });
|
||||
}
|
||||
|
||||
// Check last log entry for this VA and project
|
||||
const lastLog = await LoggedModel.findOne({ vaId, projectId })
|
||||
.sort({ time: -1 }) // get the most recent log
|
||||
|
|
@ -173,104 +180,94 @@ async function va_log_track_work_callback(req, res) {
|
|||
});
|
||||
}
|
||||
|
||||
if (!projectId || !logId || !title || !appName) {
|
||||
return res.status(400).json({ success: false, message: "Missing Props" });
|
||||
}
|
||||
|
||||
// Handle POST → create entry
|
||||
if (req.method === "POST") {
|
||||
const dailyCheck = await checkDailyHoursAndLogout(vaId, vaEmail, projectId, project);
|
||||
|
||||
if (dailyCheck.shouldLogout) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: `Cannot start work - daily hours completed (${formatHoursToReadable(dailyCheck.totalTodayHours)} / ${formatHoursToReadable(dailyCheck.dedicatedHours)})`,
|
||||
autoLogout: true
|
||||
});
|
||||
}
|
||||
|
||||
const createdTrack = await WorkTrackModel.create({
|
||||
vaId,
|
||||
projectId,
|
||||
logId,
|
||||
title,
|
||||
appName,
|
||||
url,
|
||||
from: Date.now(),
|
||||
});
|
||||
// if (req.method === "POST") {
|
||||
// const dailyCheck = await checkDailyHoursAndLogout(vaId, vaEmail, projectId, project);
|
||||
const createdTracks = await Promise.all(
|
||||
logs.map(async (log) => {
|
||||
const createdTrack = await WorkTrackModel.create({
|
||||
vaId,
|
||||
projectId,
|
||||
logId,
|
||||
titlew: log.title,
|
||||
appName: log.appName,
|
||||
url: log.url,
|
||||
from: log.from,
|
||||
to: log.to, // Fixed: Changed from duplicate "from" to "to"
|
||||
totalTime: log.totalTime,
|
||||
});
|
||||
|
||||
return createdTrack;
|
||||
})
|
||||
);
|
||||
|
||||
return res.status(201).json({
|
||||
success: true,
|
||||
message: "Work tracking started",
|
||||
data: createdTrack,
|
||||
dailyHours: {
|
||||
todayTotal: formatHoursToReadable(dailyCheck.totalTodayHours),
|
||||
dailyLimit: formatHoursToReadable(dailyCheck.dedicatedHours),
|
||||
remaining: formatHoursToReadable(dailyCheck.dedicatedHours - dailyCheck.totalTodayHours)
|
||||
}
|
||||
message: "Work tracking created",
|
||||
data: createdTracks
|
||||
});
|
||||
}
|
||||
// }
|
||||
|
||||
// Handle PATCH → update entry (add 'to' and calculate total time)
|
||||
if (req.method === "PATCH") {
|
||||
const { id } = payload; // expecting the workTrack record ID to update
|
||||
if (!id) {
|
||||
return res.status(400).json({ success: false, message: "Missing workTrack ID" });
|
||||
}
|
||||
// // Handle PATCH → update entry (add 'to' and calculate total time)
|
||||
// if (req.method === "PATCH") {
|
||||
// const { id } = payload; // expecting the workTrack record ID to update
|
||||
// if (!id) {
|
||||
// return res.status(400).json({ success: false, message: "Missing workTrack ID" });
|
||||
// }
|
||||
|
||||
const workEntry = await WorkTrackModel.findById(id);
|
||||
if (!workEntry) {
|
||||
return res.status(404).json({ success: false, message: "Work entry not found" });
|
||||
}
|
||||
// const workEntry = await WorkTrackModel.findById(id);
|
||||
// if (!workEntry) {
|
||||
// return res.status(404).json({ success: false, message: "Work entry not found" });
|
||||
// }
|
||||
|
||||
const toTime = new Date();
|
||||
const fromTime = new Date(workEntry.from);
|
||||
const totalTimeMs = toTime - fromTime;
|
||||
// const toTime = new Date();
|
||||
// const fromTime = new Date(workEntry.from);
|
||||
// const totalTimeMs = toTime - fromTime;
|
||||
|
||||
// Optional: store totalTime in minutes or hours instead of raw ms
|
||||
const hours = Math.floor(totalTimeMs / (1000 * 60 * 60));
|
||||
const minutes = Math.floor((totalTimeMs % (1000 * 60 * 60)) / (1000 * 60));
|
||||
const seconds = Math.floor((totalTimeMs % (1000 * 60)) / 1000);
|
||||
// // Optional: store totalTime in minutes or hours instead of raw ms
|
||||
// const hours = Math.floor(totalTimeMs / (1000 * 60 * 60));
|
||||
// const minutes = Math.floor((totalTimeMs % (1000 * 60 * 60)) / (1000 * 60));
|
||||
// const seconds = Math.floor((totalTimeMs % (1000 * 60)) / 1000);
|
||||
|
||||
const formattedTotalTime = `${hours}h ${minutes}m ${seconds}s`;
|
||||
// const formattedTotalTime = `${hours}h ${minutes}m ${seconds}s`;
|
||||
|
||||
// Check if daily dedicated hours are completed after this work session
|
||||
const dailyCheck = await checkDailyHoursAndLogout(vaId, vaEmail, projectId, project, id, totalTimeMs);
|
||||
// // Check if daily dedicated hours are completed after this work session
|
||||
// const dailyCheck = await checkDailyHoursAndLogout(vaId, vaEmail, projectId, project, id, totalTimeMs);
|
||||
|
||||
workEntry.to = toTime;
|
||||
workEntry.totalTime = totalTimeMs; // store as number of minutes
|
||||
await workEntry.save();
|
||||
// workEntry.to = toTime;
|
||||
// workEntry.totalTime = totalTimeMs; // store as number of minutes
|
||||
// await workEntry.save();
|
||||
|
||||
const response = {
|
||||
success: true,
|
||||
message: "Work tracking stopped",
|
||||
data: {
|
||||
id: workEntry._id,
|
||||
from: workEntry.from,
|
||||
to: workEntry.to,
|
||||
formattedTotalTime,
|
||||
totalTimeMs,
|
||||
sessionHours: totalTimeMs / (1000 * 60 * 60)
|
||||
},
|
||||
dailyHours: {
|
||||
todayTotal: formatHoursToReadable(dailyCheck.totalTodayHours),
|
||||
dailyLimit: formatHoursToReadable(dailyCheck.dedicatedHours),
|
||||
remaining: formatHoursToReadable(Math.max(0, dailyCheck.dedicatedHours - dailyCheck.totalTodayHours))
|
||||
}
|
||||
};
|
||||
// const response = {
|
||||
// success: true,
|
||||
// message: "Work tracking stopped",
|
||||
// data: {
|
||||
// id: workEntry._id,
|
||||
// from: workEntry.from,
|
||||
// to: workEntry.to,
|
||||
// formattedTotalTime,
|
||||
// totalTimeMs,
|
||||
// sessionHours: totalTimeMs / (1000 * 60 * 60)
|
||||
// },
|
||||
// dailyHours: {
|
||||
// todayTotal: formatHoursToReadable(dailyCheck.totalTodayHours),
|
||||
// dailyLimit: formatHoursToReadable(dailyCheck.dedicatedHours),
|
||||
// remaining: formatHoursToReadable(Math.max(0, dailyCheck.dedicatedHours - dailyCheck.totalTodayHours))
|
||||
// }
|
||||
// };
|
||||
|
||||
// If auto-logout was triggered, add that info to response
|
||||
if (dailyCheck.shouldLogout) {
|
||||
response.autoLogout = true;
|
||||
response.message = `Work tracking stopped. Auto-logout created - daily hours completed (${formatHoursToReadable(dailyCheck.totalTodayHours)} / ${formatHoursToReadable(dailyCheck.dedicatedHours)})`;
|
||||
response.logoutLogId = dailyCheck.logoutLogId;
|
||||
}
|
||||
// // If auto-logout was triggered, add that info to response
|
||||
// if (dailyCheck.shouldLogout) {
|
||||
// response.autoLogout = true;
|
||||
// response.message = `Work tracking stopped. Auto-logout created - daily hours completed (${formatHoursToReadable(dailyCheck.totalTodayHours)} / ${formatHoursToReadable(dailyCheck.dedicatedHours)})`;
|
||||
// response.logoutLogId = dailyCheck.logoutLogId;
|
||||
// }
|
||||
|
||||
return res.status(200).json(response);
|
||||
}
|
||||
// return res.status(200).json(response);
|
||||
// }
|
||||
|
||||
// Fallback for unsupported methods
|
||||
return res.status(405).json({ success: false, message: "Method not allowed" });
|
||||
// return res.status(405).json({ success: false, message: "Method not allowed" });
|
||||
} catch (error) {
|
||||
return res.status(500).send("Internal Server Error");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ vaRoutes.get("/list/projects/:projectId?/:from?/:to?", authValidator, va_project
|
|||
vaRoutes.post("/log/project", authValidator, va_project_log_callback); // VA Logged IN / Logged Out in project
|
||||
|
||||
vaRoutes.post("/log/project/track", authValidator, va_log_track_work_callback); // Track Window Tabs Logs Data
|
||||
vaRoutes.patch("/log/project/track", authValidator, va_log_track_work_callback); // Update Track Window Tab Closing Time
|
||||
// vaRoutes.patch("/log/project/track", authValidator, va_log_track_work_callback); // Update Track Window Tab Closing Time
|
||||
vaRoutes.get("/log/workReport/", authValidator, va_log_work_report_callback); // VA Audit Report // All Projects
|
||||
vaRoutes.get("/log/project/:projectId/:from?/:to?", authValidator, va_log_callback); // VA / Project Audit Report
|
||||
// VA filter search: supports query params `name`, `email`, `from`, `to`
|
||||
|
|
|
|||
|
|
@ -261,23 +261,7 @@
|
|||
<div class="error-message" id="step-4-error-message"></div>
|
||||
</div>
|
||||
<form id="payment-form">
|
||||
<div class="form-group">
|
||||
<!-- <div class="payment-option" onclick="selectPaymentMethod('payoneer')">
|
||||
<h4>Payoneer</h4>
|
||||
<p>Receive payments via Payoneer</p>
|
||||
</div>
|
||||
<div id="payoneer-details" class="payment-details">
|
||||
<div class="form-group">
|
||||
<div class="input-container">
|
||||
<input type="text" id="payoneerAccount" class="input-field" placeholder=" " minlength="5" maxlength="20" oninput="validatePayoneerAccount(this)" onkeypress="return event.charCode >= 48 && event.charCode <= 57" required>
|
||||
<label for="payoneerAccount" class="input-label">Payoneer Account Number *</label>
|
||||
</div>
|
||||
<div class="invalid-feedback" id="payoneer-error" style="display: none; color: #dc3545; font-size: 12px; margin-top: 5px;">
|
||||
Account number must be 5-20 characters
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<div class="form-group">
|
||||
<div class="payment-option" onclick="selectPaymentMethod('veem')">
|
||||
<h4>Veem</h4>
|
||||
<p>Receive payments via Veem</p>
|
||||
|
|
@ -309,6 +293,23 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 class="form-title additional-info-heading">Additional Information</h2>
|
||||
<div class="payment-option" onclick="selectPaymentMethod('payoneer')">
|
||||
<h4>Payoneer</h4>
|
||||
<p>Receive payments via Payoneer</p>
|
||||
</div>
|
||||
<div id="payoneer-details" class="payment-details">
|
||||
<div class="form-group">
|
||||
<div class="input-container">
|
||||
<input type="text" id="payoneerAccount" class="input-field" placeholder=" " minlength="5" maxlength="20" oninput="validatePayoneerAccount(this)" onkeypress="return event.charCode >= 48 && event.charCode <= 57" required>
|
||||
<label for="payoneerAccount" class="input-label">Payoneer Account Number *</label>
|
||||
</div>
|
||||
<div class="invalid-feedback" id="payoneer-error" style="display: none; color: #dc3545; font-size: 12px; margin-top: 5px;">
|
||||
Account number must be 5-20 characters
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
|
|
|
|||
Loading…
Reference in New Issue