From 870d81e2ca0df7007e9132c9a155758708238ea2 Mon Sep 17 00:00:00 2001 From: Aakash Date: Fri, 28 Nov 2025 12:51:08 +0530 Subject: [PATCH] Updated dashboard, Added payroll API --- public/css/dashboard.css | 333 ++++++++++++++++++ .../va/vaPayroll/vaPaySlip.controller.js | 12 + .../vaPayroll/vaPayrollSchedule.controller.js | 37 ++ src/routes/v1/va.routes.js | 13 +- views/client/dashboard-dashboard.ejs | 68 +++- views/client/dashboard-profile.ejs | 330 +++-------------- views/client/dashboard-projects.ejs | 67 +++- views/client/dashboard-virtual-assistants.ejs | 68 +++- 8 files changed, 617 insertions(+), 311 deletions(-) create mode 100644 src/controllers/va/vaPayroll/vaPaySlip.controller.js create mode 100644 src/controllers/va/vaPayroll/vaPayrollSchedule.controller.js diff --git a/public/css/dashboard.css b/public/css/dashboard.css index c20f34e..15b6b02 100644 --- a/public/css/dashboard.css +++ b/public/css/dashboard.css @@ -1309,4 +1309,337 @@ tr[data-vaid]:hover { .modal-actions a { text-decoration: none; +} + +.profile-container { + max-width: 1200px; + margin: 0 auto; + padding: 20px; +} + +.profile-header { + margin-bottom: 30px; + padding-bottom: 20px; + border-bottom: 1px solid #e1e5e9; +} + +.profile-title { + font-size: 2rem; + font-weight: 600; + color: #2d3748; + margin: 0; +} + +.profile-sections { + display: grid; + gap: 30px; +} + +.profile-section { + background: white; + border-radius: 12px; + padding: 30px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + border: 1px solid #e1e5e9; +} + +.section-header { + display: flex; + align-items: center; + gap: 12px; + margin-bottom: 25px; + padding-bottom: 15px; + border-bottom: 1px solid #e1e5e9; +} + +.section-icon { + font-size: 1.5rem; + color: #4299e1; +} + +.section-title { + font-size: 1.5rem; + font-weight: 600; + color: #2d3748; + margin: 0; +} + +.form-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 20px; +} + +.form-group { + display: flex; + flex-direction: column; + gap: 8px; +} + +.form-label { + font-weight: 500; + color: #4a5568; + font-size: 0.875rem; +} + +.form-input { + padding: 12px; + border: 1px solid #e1e5e9; + border-radius: 8px; + font-size: 1rem; + background: #f7fafc; + color: #4a5568; +} + +.empty-field { + color: #a0aec0; + font-style: italic; +} + +.password-section { + text-align: center; + padding: 40px 20px; +} + +.change-password-btn { + background: #4299e1; + color: white; + border: none; + padding: 12px 24px; + border-radius: 8px; + cursor: pointer; + font-weight: 500; + font-size: 1rem; + transition: background-color 0.2s; +} + +.change-password-btn:hover { + background: #3182ce; +} + +/* Modal Styles */ +.modal-overlay { + display: none; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + z-index: 1000; + align-items: center; + justify-content: center; + padding: 20px; +} + +.modal-content { + background: white; + border-radius: 12px; + width: 100%; + max-width: 500px; + max-height: 90vh; + overflow-y: auto; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); +} + +.modal-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 24px; + border-bottom: 1px solid #e1e5e9; +} + +.modal-title { + font-size: 1.5rem; + font-weight: 600; + color: #2d3748; + margin: 0; +} + +.modal-close { + background: none; + border: none; + font-size: 1.25rem; + cursor: pointer; + color: #718096; + padding: 5px; + border-radius: 4px; +} + +.modal-close:hover { + background: #f7fafc; + color: #2d3748; +} + +.modal-body { + padding: 24px; +} + +.modal-form { + display: flex; + flex-direction: column; + gap: 20px; +} + +.modal-form .form-group { + gap: 8px; +} + +.modal-form .form-label { + font-weight: 500; + color: #4a5568; +} + +.modal-form .form-input { + padding: 12px; + border: 1px solid #cbd5e0; + border-radius: 8px; + font-size: 1rem; + background: white; + transition: border-color 0.2s; +} + +.modal-form .form-input:focus { + outline: none; + border-color: #4299e1; + box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.1); +} + +.modal-form .form-input.error { + border-color: #e53e3e; +} + +.error-message { + color: #e53e3e; + font-size: 0.875rem; + margin-top: 4px; +} + +.success-message { + color: #38a169; + font-size: 0.875rem; + margin-top: 4px; +} + +.modal-actions { + display: flex; + gap: 15px; + justify-content: flex-end; + margin-top: 30px; + padding-top: 20px; + border-top: 1px solid #e1e5e9; +} + +.save-btn { + background: #48bb78; + color: white; + border: none; + padding: 12px 24px; + border-radius: 8px; + cursor: pointer; + font-weight: 500; + transition: background-color 0.2s; +} + +.save-btn:hover { + background: #38a169; +} + +.save-btn:disabled { + background: #a0aec0; + cursor: not-allowed; +} + +.cancel-btn { + background: #e53e3e; + color: white; + border: none; + padding: 12px 24px; + border-radius: 8px; + cursor: pointer; + font-weight: 500; + transition: background-color 0.2s; +} + +.cancel-btn:hover { + background: #c53030; +} + +/* Logout Confirmation Modal Styles */ +.logout-modal-body { + text-align: center; + padding: 30px 20px; +} + +.logout-icon { + font-size: 3rem; + color: #e53e3e; + margin-bottom: 20px; +} + +.logout-title { + font-size: 1.5rem; + font-weight: 600; + color: #2d3748; + margin-bottom: 10px; +} + +.logout-message { + color: #718096; + font-size: 1rem; + line-height: 1.5; + margin-bottom: 30px; +} + +.logout-actions { + display: flex; + gap: 15px; + justify-content: center; +} + +.logout-confirm-btn { + background: #e53e3e; + color: white; + border: none; + padding: 12px 24px; + border-radius: 8px; + cursor: pointer; + font-weight: 500; + transition: background-color 0.2s; + min-width: 120px; +} + +.logout-confirm-btn:hover { + background: #c53030; +} + +.logout-cancel-btn { + background: #718096; + color: white; + border: none; + padding: 12px 24px; + border-radius: 8px; + cursor: pointer; + font-weight: 500; + transition: background-color 0.2s; + min-width: 120px; +} + +.logout-cancel-btn:hover { + background: #4a5568; +} + +@media (max-width: 768px) { + .form-grid { + grid-template-columns: 1fr; + } + + .modal-actions { + flex-direction: column; + } + + .logout-actions { + flex-direction: column; + } } \ No newline at end of file diff --git a/src/controllers/va/vaPayroll/vaPaySlip.controller.js b/src/controllers/va/vaPayroll/vaPaySlip.controller.js new file mode 100644 index 0000000..d81bfa8 --- /dev/null +++ b/src/controllers/va/vaPayroll/vaPaySlip.controller.js @@ -0,0 +1,12 @@ +async function va_payslip_data_function(req, res) { + try { + // Assuming we have a service function to get payroll schedule + const payrollSchedule = await getPayrollScheduleForVA(req.user.id); + return res.status(200).json({ success: true, data: payrollSchedule }); + } catch (error) { + console.error("Error fetching payroll schedule:", error); + return res.status(500).json({ success: false, message: "Internal Server Error" }); + } +} + +export { va_payslip_data_function }; \ No newline at end of file diff --git a/src/controllers/va/vaPayroll/vaPayrollSchedule.controller.js b/src/controllers/va/vaPayroll/vaPayrollSchedule.controller.js new file mode 100644 index 0000000..543abc5 --- /dev/null +++ b/src/controllers/va/vaPayroll/vaPayrollSchedule.controller.js @@ -0,0 +1,37 @@ +async function va_payroll_schedule_function(req, res) { + try { + const salesforceUrl = `${process.env.SF_DOCTRACKER_API_URL}services/apexrest/docva/api/payroll-schedule`; + + const response = await fetch(salesforceUrl, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + // Add any required headers for Salesforce authentication + // 'Authorization': 'Bearer ' + process.env.SF_ACCESS_TOKEN + }, + }); + + 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); + + res.status(201).json({ + success: true, + 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; + } +} + +export { va_payroll_schedule_function }; \ No newline at end of file diff --git a/src/routes/v1/va.routes.js b/src/routes/v1/va.routes.js index c80794d..e50f1f0 100644 --- a/src/routes/v1/va.routes.js +++ b/src/routes/v1/va.routes.js @@ -27,6 +27,8 @@ import { va_leave_apply_function } from "../../controllers/va/vaLeaves/vaLeavesA 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"; +import { va_payroll_schedule_function } from "../../controllers/va/vaPayroll/vaPayrollSchedule.controller.js"; +// import { va_payslip_data_function } from "../../controllers/va/vaPayroll/vaPaySlip.controller.js"; vaRoutes.get("/", va_callback); // VA Callback for getting active VA @@ -40,9 +42,6 @@ vaRoutes.post("/verify-va-email", va_verify_email); vaRoutes.get("/profile", authValidator, va_profile_callback); // Get VA Profile vaRoutes.patch("/profile", authValidator, va_profile_update_callback); // Update VA Profile - - -vaRoutes.get("/:userName", va_username_callback); // VA Username Callback vaRoutes.delete("/:id", va_remove_function); // VA Username Callback // timesheet adjustment routes @@ -81,6 +80,14 @@ vaRoutes.get("/log/project/:projectId/:from?/:to?", authValidator, va_log_callba // VA filter search: supports query params `name`, `email`, `from`, `to` +// Payroll routes +vaRoutes.get("/payrollSchedule", authValidator, va_payroll_schedule_function); // VA Payroll Schedule +// vaRoutes.get("/paySlip/:vaId", authValidator, va_payslip_data_function); // VA Payslip + + + + +vaRoutes.get("/:userName", va_username_callback); // VA Username Callback export default vaRoutes; \ No newline at end of file diff --git a/views/client/dashboard-dashboard.ejs b/views/client/dashboard-dashboard.ejs index a845ea3..e317683 100644 --- a/views/client/dashboard-dashboard.ejs +++ b/views/client/dashboard-dashboard.ejs @@ -46,6 +46,12 @@