Updated dashboard, Added payroll API

This commit is contained in:
Aakash Mittal 2025-11-28 12:51:08 +05:30
parent 4d757daf97
commit 870d81e2ca
8 changed files with 617 additions and 311 deletions

View File

@ -1309,4 +1309,337 @@ tr[data-vaid]:hover {
.modal-actions a { .modal-actions a {
text-decoration: none; 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;
}
} }

View File

@ -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 };

View File

@ -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 };

View File

@ -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_list_function } from "../../controllers/va/vaLeaves/vaLeaveList.controller.js"
import { va_leave_manage_function } from "../../controllers/va/vaLeaves/vaLeaveManage.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_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 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.get("/profile", authValidator, va_profile_callback); // Get VA Profile
vaRoutes.patch("/profile", authValidator, va_profile_update_callback); // Update 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 vaRoutes.delete("/:id", va_remove_function); // VA Username Callback
// timesheet adjustment routes // 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` // 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; export default vaRoutes;

View File

@ -46,6 +46,12 @@
<aside class="dashboard-sidebar"> <aside class="dashboard-sidebar">
<nav class="sidebar-nav"> <nav class="sidebar-nav">
<ul class="sidebar-list"> <ul class="sidebar-list">
<li class="sidebar-item">
<a href="/client/profile" class="sidebar-link">
<i class="fas fa-user sidebar-icon"></i>
<span>Profile</span>
</a>
</li>
<li class="sidebar-item"> <li class="sidebar-item">
<a href="/client/dashboard" class="sidebar-link active"> <a href="/client/dashboard" class="sidebar-link active">
<i class="fas fa-chart-bar sidebar-icon"></i> <i class="fas fa-chart-bar sidebar-icon"></i>
@ -64,12 +70,6 @@
<span>Projects</span> <span>Projects</span>
</a> </a>
</li> </li>
<li class="sidebar-item">
<a href="/client/profile" class="sidebar-link">
<i class="fas fa-user sidebar-icon"></i>
<span>Profile</span>
</a>
</li>
<li class="sidebar-item"> <li class="sidebar-item">
<a href="javascript:void(0);" class="sidebar-link" id="logoutButton"> <a href="javascript:void(0);" class="sidebar-link" id="logoutButton">
<i class="fas fa-sign-out-alt sidebar-icon"></i> <i class="fas fa-sign-out-alt sidebar-icon"></i>
@ -203,6 +203,28 @@
</div> </div>
</div> </div>
</div> </div>
<div id="logoutModal" class="modal-overlay">
<div class="modal-content">
<div class="modal-header">
<h2 class="modal-title">Confirm Logout</h2>
<button class="modal-close" onclick="closeLogoutModal()">
<i class="fas fa-times"></i>
</button>
</div>
<div class="logout-modal-body">
<div class="logout-icon">
<i class="fas fa-sign-out-alt"></i>
</div>
<h3 class="logout-title">Are you sure you want to logout?</h3>
<p class="logout-message">You will be logged out of your account and redirected to the login page.</p>
<div class="logout-actions">
<button class="logout-cancel-btn" onclick="closeLogoutModal()">Cancel</button>
<button class="logout-confirm-btn" id="confirmLogoutBtn">Yes, Logout</button>
</div>
</div>
</div>
</div>
</div> </div>
<script> <script>
// Theme management // Theme management
@ -486,6 +508,19 @@
// Optional: Auto-refresh data every 30 seconds // Optional: Auto-refresh data every 30 seconds
setInterval(loadDashboardData, 30000); setInterval(loadDashboardData, 30000);
// Logout Modal Functions
function openLogoutModal() {
const modal = document.getElementById('logoutModal');
modal.style.display = 'flex';
document.body.style.overflow = 'hidden';
}
function closeLogoutModal() {
const modal = document.getElementById('logoutModal');
modal.style.display = 'none';
document.body.style.overflow = 'auto';
}
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
// Load dashboard data // Load dashboard data
loadDashboardData(); loadDashboardData();
@ -497,13 +532,24 @@
sidebar.classList.toggle('active'); sidebar.classList.toggle('active');
}); });
// Logout button // Close modal with escape key
const logoutButton = document.getElementById('logoutButton'); document.addEventListener('keydown', function(e) {
logoutButton.addEventListener('click', () => { if (e.key === 'Escape') {
if (confirm('Are you sure you want to logout?')) { closeLogoutModal();
handleLogout();
} }
}); });
document.getElementById('logoutModal').addEventListener('click', function(e) {
if (e.target === this) {
closeLogoutModal();
}
});
// Logout button - opens confirmation modal instead of directly logging out
document.getElementById('logoutButton').addEventListener('click', openLogoutModal);
// Confirm logout button
document.getElementById('confirmLogoutBtn').addEventListener('click', handleLogout);
}); });
</script> </script>

View File

@ -6,272 +6,7 @@
<title>DocVA Client | Profile</title> <title>DocVA Client | Profile</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<link rel="stylesheet" href="/public/css/dashboard.css"> <link rel="stylesheet" href="/public/css/dashboard.css">
<style>
.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;
}
@media (max-width: 768px) {
.form-grid {
grid-template-columns: 1fr;
}
.modal-actions {
flex-direction: column;
}
}
</style>
</head> </head>
<body class="dashboard-body"> <body class="dashboard-body">
<div id="root"> <div id="root">
@ -312,6 +47,12 @@
<aside class="dashboard-sidebar"> <aside class="dashboard-sidebar">
<nav class="sidebar-nav"> <nav class="sidebar-nav">
<ul class="sidebar-list"> <ul class="sidebar-list">
<li class="sidebar-item">
<a href="/client/profile" class="sidebar-link active">
<i class="fas fa-user sidebar-icon"></i>
<span>Profile</span>
</a>
</li>
<li class="sidebar-item"> <li class="sidebar-item">
<a href="/client/dashboard" class="sidebar-link"> <a href="/client/dashboard" class="sidebar-link">
<i class="fas fa-chart-bar sidebar-icon"></i> <i class="fas fa-chart-bar sidebar-icon"></i>
@ -330,12 +71,6 @@
<span>Projects</span> <span>Projects</span>
</a> </a>
</li> </li>
<li class="sidebar-item">
<a href="/client/profile" class="sidebar-link active">
<i class="fas fa-user sidebar-icon"></i>
<span>Profile</span>
</a>
</li>
<li class="sidebar-item"> <li class="sidebar-item">
<a href="javascript:void(0);" class="sidebar-link" id="logoutButton"> <a href="javascript:void(0);" class="sidebar-link" id="logoutButton">
<i class="fas fa-sign-out-alt sidebar-icon"></i> <i class="fas fa-sign-out-alt sidebar-icon"></i>
@ -450,6 +185,28 @@
</div> </div>
</div> </div>
</div> </div>
<div id="logoutModal" class="modal-overlay">
<div class="modal-content">
<div class="modal-header">
<h2 class="modal-title">Confirm Logout</h2>
<button class="modal-close" onclick="closeLogoutModal()">
<i class="fas fa-times"></i>
</button>
</div>
<div class="logout-modal-body">
<div class="logout-icon">
<i class="fas fa-sign-out-alt"></i>
</div>
<h3 class="logout-title">Are you sure you want to logout?</h3>
<p class="logout-message">You will be logged out of your account and redirected to the login page.</p>
<div class="logout-actions">
<button class="logout-cancel-btn" onclick="closeLogoutModal()">Cancel</button>
<button class="logout-confirm-btn" id="confirmLogoutBtn">Yes, Logout</button>
</div>
</div>
</div>
</div>
</div> </div>
<script> <script>
@ -646,6 +403,19 @@
} }
} }
// Logout Modal Functions
function openLogoutModal() {
const modal = document.getElementById('logoutModal');
modal.style.display = 'flex';
document.body.style.overflow = 'hidden';
}
function closeLogoutModal() {
const modal = document.getElementById('logoutModal');
modal.style.display = 'none';
document.body.style.overflow = 'auto';
}
// Event listeners // Event listeners
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
loadProfileData(); loadProfileData();
@ -667,18 +437,28 @@
document.addEventListener('keydown', function(e) { document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') { if (e.key === 'Escape') {
closePasswordModal(); closePasswordModal();
closeLogoutModal();
} }
}); });
document.getElementById('logoutModal').addEventListener('click', function(e) {
if (e.target === this) {
closeLogoutModal();
}
});
// Logout button - opens confirmation modal instead of directly logging out
document.getElementById('logoutButton').addEventListener('click', openLogoutModal);
// Confirm logout button
document.getElementById('confirmLogoutBtn').addEventListener('click', handleLogout);
// Mobile menu toggle // Mobile menu toggle
const menuToggle = document.querySelector('.mobile-menu-button'); const menuToggle = document.querySelector('.mobile-menu-button');
const sidebar = document.querySelector('.dashboard-sidebar'); const sidebar = document.querySelector('.dashboard-sidebar');
menuToggle.addEventListener('click', () => { menuToggle.addEventListener('click', () => {
sidebar.classList.toggle('active'); sidebar.classList.toggle('active');
}); });
// Logout button
document.getElementById('logoutButton').addEventListener('click', handleLogout);
}); });
</script> </script>
</body> </body>

View File

@ -60,6 +60,12 @@
</div> --> </div> -->
<nav class="sidebar-nav"> <nav class="sidebar-nav">
<ul class="sidebar-list"> <ul class="sidebar-list">
<li class="sidebar-item">
<a href="/client/profile" class="sidebar-link">
<i class="fas fa-user sidebar-icon"></i>
<span>Profile</span>
</a>
</li>
<li class="sidebar-item"> <li class="sidebar-item">
<a href="/client/dashboard" class="sidebar-link"> <a href="/client/dashboard" class="sidebar-link">
<i class="fas fa-chart-bar sidebar-icon"></i> <i class="fas fa-chart-bar sidebar-icon"></i>
@ -78,12 +84,6 @@
<span>Projects</span> <span>Projects</span>
</a> </a>
</li> </li>
<li class="sidebar-item">
<a href="/client/profile" class="sidebar-link">
<i class="fas fa-user sidebar-icon"></i>
<span>Profile</span>
</a>
</li>
<li class="sidebar-item"> <li class="sidebar-item">
<a href="javascript:void(0);" class="sidebar-link" id="logoutButton"> <a href="javascript:void(0);" class="sidebar-link" id="logoutButton">
<i class="fas fa-sign-out-alt sidebar-icon"></i> <i class="fas fa-sign-out-alt sidebar-icon"></i>
@ -167,6 +167,28 @@
</div> </div>
</div> </div>
</div> </div>
<div id="logoutModal" class="modal-overlay">
<div class="modal-content">
<div class="modal-header">
<h2 class="modal-title">Confirm Logout</h2>
<button class="modal-close" onclick="closeLogoutModal()">
<i class="fas fa-times"></i>
</button>
</div>
<div class="logout-modal-body">
<div class="logout-icon">
<i class="fas fa-sign-out-alt"></i>
</div>
<h3 class="logout-title">Are you sure you want to logout?</h3>
<p class="logout-message">You will be logged out of your account and redirected to the login page.</p>
<div class="logout-actions">
<button class="logout-cancel-btn" onclick="closeLogoutModal()">Cancel</button>
<button class="logout-confirm-btn" id="confirmLogoutBtn">Yes, Logout</button>
</div>
</div>
</div>
</div>
</div> </div>
<script> <script>
// Theme management // Theme management
@ -435,6 +457,18 @@
// Optional: Auto-refresh data every 30 seconds // Optional: Auto-refresh data every 30 seconds
setInterval(loadDashboardData, 30000); setInterval(loadDashboardData, 30000);
// Logout Modal Functions
function openLogoutModal() {
const modal = document.getElementById('logoutModal');
modal.style.display = 'flex';
document.body.style.overflow = 'hidden';
}
function closeLogoutModal() {
const modal = document.getElementById('logoutModal');
modal.style.display = 'none';
document.body.style.overflow = 'auto';
}
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
// Load dashboard data // Load dashboard data
@ -447,13 +481,24 @@
sidebar.classList.toggle('active'); sidebar.classList.toggle('active');
}); });
// Logout button // Close modal with escape key
const logoutButton = document.getElementById('logoutButton'); document.addEventListener('keydown', function(e) {
logoutButton.addEventListener('click', () => { if (e.key === 'Escape') {
if (confirm('Are you sure you want to logout?')) { closeLogoutModal();
handleLogout();
} }
}); });
document.getElementById('logoutModal').addEventListener('click', function(e) {
if (e.target === this) {
closeLogoutModal();
}
});
// Logout button - opens confirmation modal instead of directly logging out
document.getElementById('logoutButton').addEventListener('click', openLogoutModal);
// Confirm logout button
document.getElementById('confirmLogoutBtn').addEventListener('click', handleLogout);
}); });
modalClose.addEventListener('click', closeProjectModal); modalClose.addEventListener('click', closeProjectModal);

View File

@ -46,6 +46,12 @@
<aside class="dashboard-sidebar"> <aside class="dashboard-sidebar">
<nav class="sidebar-nav"> <nav class="sidebar-nav">
<ul class="sidebar-list"> <ul class="sidebar-list">
<li class="sidebar-item">
<a href="/client/profile" class="sidebar-link">
<i class="fas fa-user sidebar-icon"></i>
<span>Profile</span>
</a>
</li>
<li class="sidebar-item"> <li class="sidebar-item">
<a href="/client/dashboard" class="sidebar-link"> <a href="/client/dashboard" class="sidebar-link">
<i class="fas fa-chart-bar sidebar-icon"></i> <i class="fas fa-chart-bar sidebar-icon"></i>
@ -64,12 +70,6 @@
<span>Projects</span> <span>Projects</span>
</a> </a>
</li> </li>
<li class="sidebar-item">
<a href="/client/profile" class="sidebar-link">
<i class="fas fa-user sidebar-icon"></i>
<span>Profile</span>
</a>
</li>
<li class="sidebar-item"> <li class="sidebar-item">
<a href="javascript:void(0);" class="sidebar-link" id="logoutButton"> <a href="javascript:void(0);" class="sidebar-link" id="logoutButton">
<i class="fas fa-sign-out-alt sidebar-icon"></i> <i class="fas fa-sign-out-alt sidebar-icon"></i>
@ -203,6 +203,28 @@
</div> </div>
</div> </div>
</div> </div>
<div id="logoutModal" class="modal-overlay">
<div class="modal-content">
<div class="modal-header">
<h2 class="modal-title">Confirm Logout</h2>
<button class="modal-close" onclick="closeLogoutModal()">
<i class="fas fa-times"></i>
</button>
</div>
<div class="logout-modal-body">
<div class="logout-icon">
<i class="fas fa-sign-out-alt"></i>
</div>
<h3 class="logout-title">Are you sure you want to logout?</h3>
<p class="logout-message">You will be logged out of your account and redirected to the login page.</p>
<div class="logout-actions">
<button class="logout-cancel-btn" onclick="closeLogoutModal()">Cancel</button>
<button class="logout-confirm-btn" id="confirmLogoutBtn">Yes, Logout</button>
</div>
</div>
</div>
</div>
</div> </div>
<script> <script>
// Theme management // Theme management
@ -486,6 +508,19 @@
// Optional: Auto-refresh data every 30 seconds // Optional: Auto-refresh data every 30 seconds
setInterval(loadDashboardData, 30000); setInterval(loadDashboardData, 30000);
// Logout Modal Functions
function openLogoutModal() {
const modal = document.getElementById('logoutModal');
modal.style.display = 'flex';
document.body.style.overflow = 'hidden';
}
function closeLogoutModal() {
const modal = document.getElementById('logoutModal');
modal.style.display = 'none';
document.body.style.overflow = 'auto';
}
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
// Load dashboard data // Load dashboard data
loadDashboardData(); loadDashboardData();
@ -497,13 +532,24 @@
sidebar.classList.toggle('active'); sidebar.classList.toggle('active');
}); });
// Logout button // Close modal with escape key
const logoutButton = document.getElementById('logoutButton'); document.addEventListener('keydown', function(e) {
logoutButton.addEventListener('click', () => { if (e.key === 'Escape') {
if (confirm('Are you sure you want to logout?')) { closeLogoutModal();
handleLogout();
} }
}); });
document.getElementById('logoutModal').addEventListener('click', function(e) {
if (e.target === this) {
closeLogoutModal();
}
});
// Logout button - opens confirmation modal instead of directly logging out
document.getElementById('logoutButton').addEventListener('click', openLogoutModal);
// Confirm logout button
document.getElementById('confirmLogoutBtn').addEventListener('click', handleLogout);
}); });
</script> </script>