Updated dashboard, Added payroll API
This commit is contained in:
parent
4d757daf97
commit
870d81e2ca
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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 };
|
||||
|
|
@ -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 };
|
||||
|
|
@ -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;
|
||||
|
|
@ -46,6 +46,12 @@
|
|||
<aside class="dashboard-sidebar">
|
||||
<nav class="sidebar-nav">
|
||||
<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">
|
||||
<a href="/client/dashboard" class="sidebar-link active">
|
||||
<i class="fas fa-chart-bar sidebar-icon"></i>
|
||||
|
|
@ -64,12 +70,6 @@
|
|||
<span>Projects</span>
|
||||
</a>
|
||||
</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">
|
||||
<a href="javascript:void(0);" class="sidebar-link" id="logoutButton">
|
||||
<i class="fas fa-sign-out-alt sidebar-icon"></i>
|
||||
|
|
@ -203,6 +203,28 @@
|
|||
</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>
|
||||
<script>
|
||||
// Theme management
|
||||
|
|
@ -486,6 +508,19 @@
|
|||
// Optional: Auto-refresh data every 30 seconds
|
||||
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() {
|
||||
// Load dashboard data
|
||||
loadDashboardData();
|
||||
|
|
@ -497,13 +532,24 @@
|
|||
sidebar.classList.toggle('active');
|
||||
});
|
||||
|
||||
// Logout button
|
||||
const logoutButton = document.getElementById('logoutButton');
|
||||
logoutButton.addEventListener('click', () => {
|
||||
if (confirm('Are you sure you want to logout?')) {
|
||||
handleLogout();
|
||||
// Close modal with escape key
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') {
|
||||
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);
|
||||
});
|
||||
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -6,272 +6,7 @@
|
|||
<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="/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>
|
||||
<body class="dashboard-body">
|
||||
<div id="root">
|
||||
|
|
@ -312,6 +47,12 @@
|
|||
<aside class="dashboard-sidebar">
|
||||
<nav class="sidebar-nav">
|
||||
<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">
|
||||
<a href="/client/dashboard" class="sidebar-link">
|
||||
<i class="fas fa-chart-bar sidebar-icon"></i>
|
||||
|
|
@ -330,12 +71,6 @@
|
|||
<span>Projects</span>
|
||||
</a>
|
||||
</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">
|
||||
<a href="javascript:void(0);" class="sidebar-link" id="logoutButton">
|
||||
<i class="fas fa-sign-out-alt sidebar-icon"></i>
|
||||
|
|
@ -450,6 +185,28 @@
|
|||
</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>
|
||||
|
||||
<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
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
loadProfileData();
|
||||
|
|
@ -667,18 +437,28 @@
|
|||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') {
|
||||
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
|
||||
const menuToggle = document.querySelector('.mobile-menu-button');
|
||||
const sidebar = document.querySelector('.dashboard-sidebar');
|
||||
menuToggle.addEventListener('click', () => {
|
||||
sidebar.classList.toggle('active');
|
||||
});
|
||||
|
||||
// Logout button
|
||||
document.getElementById('logoutButton').addEventListener('click', handleLogout);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
|||
|
|
@ -60,6 +60,12 @@
|
|||
</div> -->
|
||||
<nav class="sidebar-nav">
|
||||
<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">
|
||||
<a href="/client/dashboard" class="sidebar-link">
|
||||
<i class="fas fa-chart-bar sidebar-icon"></i>
|
||||
|
|
@ -78,12 +84,6 @@
|
|||
<span>Projects</span>
|
||||
</a>
|
||||
</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">
|
||||
<a href="javascript:void(0);" class="sidebar-link" id="logoutButton">
|
||||
<i class="fas fa-sign-out-alt sidebar-icon"></i>
|
||||
|
|
@ -167,6 +167,28 @@
|
|||
</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>
|
||||
<script>
|
||||
// Theme management
|
||||
|
|
@ -435,6 +457,18 @@
|
|||
// Optional: Auto-refresh data every 30 seconds
|
||||
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() {
|
||||
// Load dashboard data
|
||||
|
|
@ -447,13 +481,24 @@
|
|||
sidebar.classList.toggle('active');
|
||||
});
|
||||
|
||||
// Logout button
|
||||
const logoutButton = document.getElementById('logoutButton');
|
||||
logoutButton.addEventListener('click', () => {
|
||||
if (confirm('Are you sure you want to logout?')) {
|
||||
handleLogout();
|
||||
// Close modal with escape key
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') {
|
||||
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);
|
||||
});
|
||||
|
||||
modalClose.addEventListener('click', closeProjectModal);
|
||||
|
|
|
|||
|
|
@ -46,6 +46,12 @@
|
|||
<aside class="dashboard-sidebar">
|
||||
<nav class="sidebar-nav">
|
||||
<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">
|
||||
<a href="/client/dashboard" class="sidebar-link">
|
||||
<i class="fas fa-chart-bar sidebar-icon"></i>
|
||||
|
|
@ -64,12 +70,6 @@
|
|||
<span>Projects</span>
|
||||
</a>
|
||||
</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">
|
||||
<a href="javascript:void(0);" class="sidebar-link" id="logoutButton">
|
||||
<i class="fas fa-sign-out-alt sidebar-icon"></i>
|
||||
|
|
@ -203,6 +203,28 @@
|
|||
</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>
|
||||
<script>
|
||||
// Theme management
|
||||
|
|
@ -486,6 +508,19 @@
|
|||
// Optional: Auto-refresh data every 30 seconds
|
||||
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() {
|
||||
// Load dashboard data
|
||||
loadDashboardData();
|
||||
|
|
@ -497,13 +532,24 @@
|
|||
sidebar.classList.toggle('active');
|
||||
});
|
||||
|
||||
// Logout button
|
||||
const logoutButton = document.getElementById('logoutButton');
|
||||
logoutButton.addEventListener('click', () => {
|
||||
if (confirm('Are you sure you want to logout?')) {
|
||||
handleLogout();
|
||||
// Close modal with escape key
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') {
|
||||
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);
|
||||
});
|
||||
|
||||
</script>
|
||||
|
|
|
|||
Loading…
Reference in New Issue