Changeset 79fff4f
- Timestamp:
- 02/22/26 22:32:07 (7 months ago)
- Branches:
- finki-main, main
- Children:
- 6fea37e, 81bc7da
- Parents:
- 4dff800
- Files:
-
- 6 edited
-
interfejs/admin.html (modified) (8 diffs)
-
interfejs/change-password.html (modified) (5 diffs)
-
interfejs/client-dashboard.html (modified) (9 diffs)
-
interfejs/store-employee.html (modified) (16 diffs)
-
interfejs/store-owner.html (modified) (12 diffs)
-
server.js (modified) (60 diffs)
Legend:
- Unmodified
- Added
- Removed
-
interfejs/admin.html
r4dff800 r79fff4f 18 18 <li><a href="products.html">Products</a></li> 19 19 <li><a href="#" id="dashboard-link" class="active">Admin</a></li> 20 <li><a href="change-password.html" class="change-password-btn">🔑 Change Password</a></li> 21 <li><a href="#" id="logout-link">Logout</a></li> 20 22 </ul> 21 23 </nav> … … 201 203 document.addEventListener('DOMContentLoaded', async () => { 202 204 const user = await loadUserData(); 205 203 206 if (!user || user.userType !== 'admin') { 204 207 window.location.href = 'index.html'; … … 207 210 208 211 document.getElementById('admin-name').textContent = user.username || 'Administrator'; 212 213 // Logout functionality 214 document.getElementById('logout-link').addEventListener('click', async (e) => { 215 e.preventDefault(); 216 await logout(); 217 }); 209 218 210 219 // Tab switching … … 213 222 document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active')); 214 223 document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active')); 224 215 225 btn.classList.add('active'); 216 226 document.getElementById(`${btn.dataset.tab}-tab`).classList.add('active'); … … 271 281 272 282 const tbody = document.getElementById('users-list'); 283 273 284 if (data.success && data.users.length > 0) { 274 285 tbody.innerHTML = data.users.map(user => ` 275 <tr>276 <td>${user.id || user.client_id || 'N/A'}</td>277 <td>${user.firstName || user.first_name || user.username || 'N/A'} ${user.lastName || user.last_name || ''}</td>278 <td>${user.email || 'N/A'}</td>279 <td><span class="status-badge status-${user.user_type || user.userType || 'unknown'}">${user.user_type || user.userType || 'unknown'}</span></td>280 <td>281 <button class="btn-small" onclick="viewUser('${user.id || user.client_id}')">View</button>282 </td>283 </tr>284 `).join('');286 <tr> 287 <td>${user.id || user.client_id || 'N/A'}</td> 288 <td>${user.firstName || user.first_name || user.username || 'N/A'} ${user.lastName || user.last_name || ''}</td> 289 <td>${user.email || 'N/A'}</td> 290 <td><span class="status-badge status-${user.user_type || user.userType || 'unknown'}">${user.user_type || user.userType || 'unknown'}</span></td> 291 <td> 292 <button class="btn-small" onclick="viewUser('${user.id || user.client_id}')">View</button> 293 </td> 294 </tr> 295 `).join(''); 285 296 } else { 286 297 tbody.innerHTML = '<tr><td colspan="5">No users found</td></tr>'; … … 298 309 299 310 const tbody = document.getElementById('stores-list'); 311 300 312 if (data.success && data.stores.length > 0) { 301 313 tbody.innerHTML = data.stores.map(store => ` 302 <tr>303 <td>${store.store_id}</td>304 <td>${store.name}</td>305 <td>${store.store_email}</td>306 <td>${store.rating || '0.0'}</td>307 <td>308 <button class="btn-small" onclick="viewStore('${store.store_id}')">View</button>309 </td>310 </tr>311 `).join('');314 <tr> 315 <td>${store.store_id}</td> 316 <td>${store.name}</td> 317 <td>${store.store_email}</td> 318 <td>${store.rating || '0.0'}</td> 319 <td> 320 <button class="btn-small" onclick="viewStore('${store.store_id}')">View</button> 321 </td> 322 </tr> 323 `).join(''); 312 324 } else { 313 325 tbody.innerHTML = '<tr><td colspan="5">No stores found</td></tr>'; … … 325 337 326 338 const tbody = document.getElementById('orders-list'); 339 327 340 if (data.success && data.orders.length > 0) { 328 341 tbody.innerHTML = data.orders.map(order => ` 329 <tr>330 <td>${order.order_num}</td>331 <td>${order.first_name} ${order.last_name}</td>332 <td>${order.store_name}</td>333 <td>${new Date(order.order_date).toLocaleDateString()}</td>334 <td><span class="status-badge status-${order.status}">${order.status}</span></td>335 <td>$${parseFloat(order.total || 0).toFixed(2)}</td>336 </tr>337 `).join('');342 <tr> 343 <td>${order.order_num}</td> 344 <td>${order.first_name} ${order.last_name}</td> 345 <td>${order.store_name}</td> 346 <td>${new Date(order.order_date).toLocaleDateString()}</td> 347 <td><span class="status-badge status-${order.status}">${order.status}</span></td> 348 <td>$${parseFloat(order.total || 0).toFixed(2)}</td> 349 </tr> 350 `).join(''); 338 351 } else { 339 352 tbody.innerHTML = '<tr><td colspan="6">No orders found</td></tr>'; … … 362 375 // Mock report generation 363 376 document.getElementById('report-results').innerHTML = ` 364 <div class="dashboard-section">365 <h4>Generated Report: ${type}</h4>366 <p>Period: ${start} to ${end}</p>367 <div class="loading">Generating report...</div>368 </div>369 `;377 <div class="dashboard-section"> 378 <h4>Generated Report: ${type}</h4> 379 <p>Period: ${start} to ${end}</p> 380 <div class="loading">Generating report...</div> 381 </div> 382 `; 370 383 371 384 // Simulate report generation 372 385 setTimeout(() => { 373 386 document.getElementById('report-results').innerHTML = ` 374 <div class="dashboard-section">375 <h4>Report Results</h4>376 <p><strong>Total Records:</strong> 42</p>377 <p><strong>Summary:</strong> Sample report data for demonstration</p>378 <button class="btn-small" onclick="downloadReport()">Download Report</button>379 </div>380 `;387 <div class="dashboard-section"> 388 <h4>Report Results</h4> 389 <p><strong>Total Records:</strong> 42</p> 390 <p><strong>Summary:</strong> Sample report data for demonstration</p> 391 <button class="btn-small" onclick="downloadReport()">Download Report</button> 392 </div> 393 `; 381 394 }, 1500); 382 395 } -
interfejs/change-password.html
r4dff800 r79fff4f 17 17 <li><a href="index.html">Home</a></li> 18 18 <li><a href="products.html">Products</a></li> 19 <li><a href=" login.html">Login</a></li>19 <li><a href="#" id="dashboard-link">Dashboard</a></li> 20 20 </ul> 21 21 </nav> … … 23 23 24 24 <main> 25 <div class=" form-container">25 <div class="auth-container"> 26 26 <h2>Change Password</h2> 27 <p class="form-subtitle" id="password-message">Please set a new password</p> 28 29 <div id="error-message" class="error-message" style="display: none; color: red; margin-bottom: 1rem; padding: 10px; background-color: #ffeeee; border-radius: 5px;"></div> 30 <div id="success-message" class="success-message" style="display: none; color: green; margin-bottom: 1rem; padding: 10px; background-color: #eeffee; border-radius: 5px;"></div> 27 <div id="password-message" class="message"></div> 31 28 32 29 <form id="change-password-form" class="form"> 33 30 <div class="form-group"> 34 31 <label for="new-password">New Password</label> 35 <input type="password" id="new-password" name="new-password"required>32 <input type="password" id="new-password" required> 36 33 <small class="password-hint">At least 8 characters with uppercase, lowercase, number, and special character</small> 37 34 </div> … … 39 36 <div class="form-group"> 40 37 <label for="confirm-password">Confirm New Password</label> 41 <input type="password" id="confirm-password" name="confirm-password"required>38 <input type="password" id="confirm-password" required> 42 39 </div> 43 40 44 <div class="form-group"> 45 <button type="submit" class="btn-primary btn-full" id="submit-btn">Change Password</button> 46 </div> 41 <button type="submit" class="btn-primary">Change Password</button> 47 42 </form> 48 43 </div> … … 62 57 </ul> 63 58 </div> 59 <div class="footer-section"> 60 <h3>Contact</h3> 61 <p>Email: support@handcraft.com</p> 62 </div> 64 63 </div> 65 64 <div class="footer-bottom"> … … 70 69 <script src="script.js"></script> 71 70 <script> 72 document.addEventListener('DOMContentLoaded', () =>{71 document.addEventListener('DOMContentLoaded', async function() { 73 72 const urlParams = new URLSearchParams(window.location.search); 74 const isForced = urlParams.get('forced') === 'true';75 let userType = 'admin'; // Default73 const forced = urlParams.get('forced'); 74 const redirect = urlParams.get('redirect'); 76 75 77 if (isForced) { 78 document.getElementById('password-message').textContent = 79 'You must change your password before continuing.'; 76 // Check if user is authenticated for password change 77 try { 78 const user = await loadUserData(); 79 if (!user) { 80 window.location.href = 'login.html'; 81 return; 82 } 83 } catch (error) { 84 console.error('Error loading user:', error); 85 window.location.href = 'login.html'; 86 return; 80 87 } 81 88 82 const form = document.getElementById('change-password-form'); 83 const submitBtn = document.getElementById('submit-btn'); 84 const errorDiv = document.getElementById('error-message'); 85 const successDiv = document.getElementById('success-message'); 89 if (forced === 'true') { 90 const messageDiv = document.getElementById('password-message'); 91 messageDiv.className = 'message warning'; 92 messageDiv.innerHTML = '⚠️ You are required to change your password before continuing.'; 93 } 86 94 87 // Check if user is authenticated for password change and get user type 88 checkAuthStatus(); 95 // Store redirect URL in session storage 96 if (redirect) { 97 sessionStorage.setItem('passwordChangeRedirect', redirect); 98 } 89 99 90 form.addEventListener('submit', async (e) => { 100 // Handle form submission 101 document.getElementById('change-password-form').addEventListener('submit', async function(e) { 91 102 e.preventDefault(); 92 93 // Clear previous messages94 errorDiv.style.display = 'none';95 successDiv.style.display = 'none';96 97 // Disable submit button to prevent double submission98 submitBtn.disabled = true;99 submitBtn.textContent = 'Changing Password...';100 103 101 104 const newPassword = document.getElementById('new-password').value; 102 105 const confirmPassword = document.getElementById('confirm-password').value; 103 106 104 // Client-side validation 105 if (!newPassword || !confirmPassword) { 106 showError('All fields are required'); 107 submitBtn.disabled = false; 108 submitBtn.textContent = 'Change Password'; 107 if (newPassword !== confirmPassword) { 108 showMessage('Passwords do not match', 'error'); 109 109 return; 110 110 } 111 111 112 if (newPassword !== confirmPassword) {113 showError('New passwords do not match');114 submitBtn.disabled = false;115 s ubmitBtn.textContent = 'Change Password';112 // Password validation 113 const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/; 114 if (!passwordRegex.test(newPassword)) { 115 showMessage('Password must have at least 8 characters, including uppercase, lowercase, number and special character', 'error'); 116 116 return; 117 117 } 118 118 119 // Validate password strength 120 const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/; 121 if (!passwordRegex.test(newPassword)) { 122 showError('Password must have at least 8 characters, including uppercase, lowercase, number and special character'); 123 submitBtn.disabled = false; 124 submitBtn.textContent = 'Change Password'; 125 return; 126 } 127 128 const formData = { 129 newPassword: newPassword, 130 confirmPassword: confirmPassword 131 }; 119 // Get redirect from session storage or URL 120 const redirectTo = sessionStorage.getItem('passwordChangeRedirect') || ''; 132 121 133 122 try { 134 console.log('Sending password change request...');135 123 const response = await fetch('/api/force-change-password', { 136 124 method: 'POST', 137 headers: { 138 'Content-Type': 'application/json' 139 }, 140 body: JSON.stringify(formData), 141 credentials: 'include' // Important: include cookies 125 headers: { 'Content-Type': 'application/json' }, 126 body: JSON.stringify({ 127 newPassword, 128 confirmPassword, 129 redirectTo: redirectTo 130 }) 142 131 }); 143 132 144 133 const data = await response.json(); 145 console.log('Response:', data);146 134 147 135 if (data.success) { 148 showSuccess('Password changed successfully! Redirecting...'); 136 showMessage('Password changed successfully! Redirecting...', 'success'); 137 sessionStorage.removeItem('passwordChangeRedirect'); 149 138 150 // Clear form 151 form.reset(); 152 153 // Redirect after short delay 139 // Redirect to appropriate dashboard 154 140 setTimeout(() => { 155 141 window.location.href = data.redirectTo || 'dashboard.html'; 156 142 }, 1500); 157 143 } else { 158 showError(data.message || 'Failed to change password'); 159 submitBtn.disabled = false; 160 submitBtn.textContent = 'Change Password'; 144 showMessage(data.message || 'Failed to change password', 'error'); 161 145 } 162 146 } catch (error) { 163 console.error('Password change error:', error); 164 showError('Network error: ' + error.message); 165 submitBtn.disabled = false; 166 submitBtn.textContent = 'Change Password'; 147 console.error('Error changing password:', error); 148 showMessage('An error occurred. Please try again.', 'error'); 167 149 } 168 150 }); 169 151 170 async function checkAuthStatus() { 171 try { 172 const response = await fetch('/api/user', { 173 credentials: 'include' 174 }); 175 const data = await response.json(); 176 177 if (!data.success) { 178 // Not authenticated, redirect to login 179 window.location.href = 'login.html'; 180 } else if (data.isTempSession) { 181 console.log('Valid temporary session for password change'); 182 if (data.user && data.user.userType) { 183 userType = data.user.userType; 184 } 185 } else { 186 // Already have full session, redirect to appropriate dashboard 187 if (data.user && data.user.userType) { 188 switch(data.user.userType) { 189 case 'admin': 190 window.location.href = 'admin.html'; 191 break; 192 case 'store_owner': 193 window.location.href = 'store-owner.html'; 194 break; 195 case 'store_employee': 196 window.location.href = 'store-employee.html'; 197 break; 198 case 'client': 199 window.location.href = 'client-dashboard.html'; 200 break; 201 default: 202 window.location.href = 'dashboard.html'; 203 } 204 } 205 } 206 } catch (error) { 207 console.error('Auth check error:', error); 208 } 209 } 210 211 function showError(message) { 212 errorDiv.textContent = message; 213 errorDiv.style.display = 'block'; 214 setTimeout(() => { 215 errorDiv.style.display = 'none'; 216 }, 5000); 217 } 218 219 function showSuccess(message) { 220 successDiv.textContent = message; 221 successDiv.style.display = 'block'; 152 function showMessage(msg, type) { 153 const messageDiv = document.getElementById('password-message'); 154 messageDiv.className = 'message ' + type; 155 messageDiv.textContent = msg; 222 156 } 223 157 }); -
interfejs/client-dashboard.html
r4dff800 r79fff4f 18 18 <li><a href="products.html">Products</a></li> 19 19 <li><a href="#" id="dashboard-link" class="active">My Dashboard</a></li> 20 <li><a href="change-password.html" class="change-password-btn">🔑 Change Password</a></li> 21 <li><a href="#" id="logout-link">Logout</a></li> 20 22 </ul> 21 23 </nav> … … 96 98 <input type="text" id="new-address" required> 97 99 </div> 98 99 100 <div class="form-row"> 100 101 <div class="form-group"> … … 107 108 </div> 108 109 </div> 109 110 110 <div class="form-group"> 111 111 <label for="new-country">Country *</label> 112 112 <input type="text" id="new-country" required> 113 113 </div> 114 115 114 <div class="form-group checkbox"> 116 115 <input type="checkbox" id="new-is-default"> 117 116 <label for="new-is-default">Set as default address</label> 118 117 </div> 119 120 118 <button type="submit" class="btn-primary">Save Address</button> 121 119 <button type="button" class="btn-secondary" id="cancel-add-address">Cancel</button> … … 158 156 </select> 159 157 </div> 160 161 158 <div class="form-group"> 162 159 <label for="request-problem">Problem Description *</label> 163 160 <textarea id="request-problem" rows="4" required></textarea> 164 161 </div> 165 166 162 <button type="submit" class="btn-primary">Submit Request</button> 167 163 <button type="button" class="btn-secondary" id="cancel-new-request">Cancel</button> … … 262 258 document.addEventListener('DOMContentLoaded', async () => { 263 259 const user = await loadUserData(); 260 264 261 if (!user || user.userType !== 'client') { 265 262 window.location.href = 'index.html'; … … 268 265 269 266 document.getElementById('client-name').textContent = `${user.firstName} ${user.lastName || ''}`; 267 268 // Logout functionality 269 document.getElementById('logout-link').addEventListener('click', async (e) => { 270 e.preventDefault(); 271 await logout(); 272 }); 270 273 271 274 // Load profile data … … 297 300 document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active')); 298 301 document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active')); 302 299 303 btn.classList.add('active'); 300 304 document.getElementById(`${btn.dataset.tab}-tab`).classList.add('active'); … … 352 356 353 357 const tbody = document.getElementById('orders-list'); 358 354 359 if (data.success && data.orders.length > 0) { 355 360 tbody.innerHTML = data.orders.map(order => { 356 361 const total = order.items ? order.items.reduce((sum, item) => 357 362 sum + (item.price * item.quantity), 0) : 0; 363 358 364 return ` 359 <tr>360 <td>${order.order_num}</td>361 <td>${order.store_name || 'Unknown'}</td>362 <td>${new Date(order.order_date).toLocaleDateString()}</td>363 <td><span class="status-badge status-${order.status}">${order.status}</span></td>364 <td>$${total.toFixed(2)}</td>365 <td>366 <button class="btn-small" onclick="viewOrder('${order.order_num}')">View</button>367 ${order.status === 'delivered' ?365 <tr> 366 <td>${order.order_num}</td> 367 <td>${order.store_name || 'Unknown'}</td> 368 <td>${new Date(order.order_date).toLocaleDateString()}</td> 369 <td><span class="status-badge status-${order.status}">${order.status}</span></td> 370 <td>$${total.toFixed(2)}</td> 371 <td> 372 <button class="btn-small" onclick="viewOrder('${order.order_num}')">View</button> 373 ${order.status === 'delivered' ? 368 374 `<button class="btn-small" onclick="reviewOrder('${order.order_num}')">Review</button>` : ''} 369 </td>370 </tr>371 `;375 </td> 376 </tr> 377 `; 372 378 }).join(''); 373 379 } else { … … 383 389 // This would need a separate API endpoint 384 390 document.getElementById('addresses-list').innerHTML = ` 385 <div class="address-card">386 <p><strong>Default Address:</strong></p>387 <p>st. Center num.10, Skopje 1000, North Macedonia</p>388 <button class="btn-small">Edit</button>389 <button class="btn-small" style="background: var(--danger);">Delete</button>390 </div>391 `;391 <div class="address-card"> 392 <p><strong>Default Address:</strong></p> 393 <p>st. Center num.10, Skopje 1000, North Macedonia</p> 394 <button class="btn-small">Edit</button> 395 <button class="btn-small" style="background: var(--danger);">Delete</button> 396 </div> 397 `; 392 398 } 393 399 -
interfejs/store-employee.html
r4dff800 r79fff4f 18 18 <li><a href="products.html">Products</a></li> 19 19 <li><a href="#" id="dashboard-link" class="active">Employee</a></li> 20 <li><a href="change-password.html" class="change-password-btn">🔑 Change Password</a></li> 21 <li><a href="#" id="logout-link">Logout</a></li> 20 22 </ul> 21 23 </nav> … … 170 172 </main> 171 173 174 <!-- Respond to Request Modal --> 172 175 <div id="respond-request-modal" class="modal" style="display: none;"> 173 176 <div class="modal-content"> … … 176 179 <form id="respond-request-form" class="form"> 177 180 <input type="hidden" id="respond-request-num"> 178 179 181 <div class="form-group"> 180 182 <label for="request-response">Your Response</label> 181 183 <textarea id="request-response" rows="4" required></textarea> 182 184 </div> 183 184 185 <div class="form-group"> 185 186 <label for="request-status">Update Status</label> … … 190 191 </select> 191 192 </div> 192 193 193 <button type="submit" class="btn-primary">Submit Response</button> 194 194 </form> … … 196 196 </div> 197 197 198 <!-- Process Refund Modal --> 198 199 <div id="process-refund-modal" class="modal" style="display: none;"> 199 200 <div class="modal-content"> … … 202 203 <form id="process-refund-form" class="form"> 203 204 <input type="hidden" id="process-refund-id"> 204 205 205 <div class="form-group"> 206 206 <label for="refund-action">Action</label> … … 210 210 </select> 211 211 </div> 212 213 212 <div class="form-group"> 214 213 <label for="refund-notes">Notes</label> 215 214 <textarea id="refund-notes" rows="3"></textarea> 216 215 </div> 217 218 216 <button type="submit" class="btn-primary">Process Refund</button> 219 217 </form> … … 244 242 document.addEventListener('DOMContentLoaded', async () => { 245 243 const user = await loadUserData(); 244 246 245 if (!user || user.userType !== 'store_employee') { 247 246 window.location.href = 'index.html'; … … 250 249 251 250 document.getElementById('employee-name').textContent = `${user.firstName} ${user.lastName || ''}`; 251 252 // Logout functionality 253 document.getElementById('logout-link').addEventListener('click', async (e) => { 254 e.preventDefault(); 255 await logout(); 256 }); 252 257 253 258 // Load stores … … 277 282 document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active')); 278 283 document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active')); 284 279 285 btn.classList.add('active'); 280 286 document.getElementById(`${btn.dataset.tab}-tab`).classList.add('active'); … … 316 322 // Display tasks 317 323 const tasksList = document.getElementById('tasks-list'); 324 318 325 if (data.tasks.pending_orders.length > 0 || data.tasks.pending_requests.length > 0 || data.tasks.pending_refunds.length > 0) { 319 326 let tasksHtml = '<div class="task-items">'; … … 323 330 data.tasks.pending_orders.forEach(order => { 324 331 tasksHtml += ` 325 <div class="task-item">326 <p><strong>Order #${order.order_num}</strong> - ${order.first_name} ${order.last_name}</p>327 <button class="btn-small" onclick="updateOrderStatus('${order.order_num}')">Update Status</button>328 </div>329 `;332 <div class="task-item"> 333 <p><strong>Order #${order.order_num}</strong> - ${order.first_name} ${order.last_name}</p> 334 <button class="btn-small" onclick="updateOrderStatus('${order.order_num}')">Update Status</button> 335 </div> 336 `; 330 337 }); 331 338 } … … 335 342 data.tasks.pending_requests.forEach(request => { 336 343 tasksHtml += ` 337 <div class="task-item">338 <p><strong>Request #${request.request_num}</strong> - ${request.first_name} ${request.last_name}</p>339 <p>${request.problem.substring(0, 50)}${request.problem.length > 50 ? '...' : ''}</p>340 <button class="btn-small" onclick="respondToRequest('${request.request_num}')">Respond</button>341 </div>342 `;344 <div class="task-item"> 345 <p><strong>Request #${request.request_num}</strong> - ${request.first_name} ${request.last_name}</p> 346 <p>${request.problem.substring(0, 50)}${request.problem.length > 50 ? '...' : ''}</p> 347 <button class="btn-small" onclick="respondToRequest('${request.request_num}')">Respond</button> 348 </div> 349 `; 343 350 }); 344 351 } … … 348 355 data.tasks.pending_refunds.forEach(refund => { 349 356 tasksHtml += ` 350 <div class="task-item">351 <p><strong>Refund #${refund.refund_id}</strong> - Order #${refund.order_num}</p>352 <p>Amount: $${parseFloat(refund.amount).toFixed(2)}</p>353 <button class="btn-small" onclick="processRefund('${refund.refund_id}')">Process</button>354 </div>355 `;357 <div class="task-item"> 358 <p><strong>Refund #${refund.refund_id}</strong> - Order #${refund.order_num}</p> 359 <p>Amount: $${parseFloat(refund.amount).toFixed(2)}</p> 360 <button class="btn-small" onclick="processRefund('${refund.refund_id}')">Process</button> 361 </div> 362 `; 356 363 }); 357 364 } … … 374 381 375 382 const tbody = document.getElementById('orders-list'); 383 376 384 if (data.success && data.orders.length > 0) { 377 385 tbody.innerHTML = data.orders.map(order => { 378 386 const total = order.items ? order.items.reduce((sum, item) => 379 387 sum + (item.price * item.quantity), 0) : 0; 388 380 389 return ` 381 <tr>382 <td>${order.order_num}</td>383 <td>${order.first_name} ${order.last_name}</td>384 <td>${new Date(order.order_date).toLocaleDateString()}</td>385 <td><span class="status-badge status-${order.status}">${order.status}</span></td>386 <td>$${total.toFixed(2)}</td>387 <td>388 <button class="btn-small" onclick="updateOrderStatus('${order.order_num}')">Update</button>389 <button class="btn-small" onclick="viewOrderDetails('${order.order_num}')">View</button>390 </td>391 </tr>392 `;390 <tr> 391 <td>${order.order_num}</td> 392 <td>${order.first_name} ${order.last_name}</td> 393 <td>${new Date(order.order_date).toLocaleDateString()}</td> 394 <td><span class="status-badge status-${order.status}">${order.status}</span></td> 395 <td>$${total.toFixed(2)}</td> 396 <td> 397 <button class="btn-small" onclick="updateOrderStatus('${order.order_num}')">Update</button> 398 <button class="btn-small" onclick="viewOrderDetails('${order.order_num}')">View</button> 399 </td> 400 </tr> 401 `; 393 402 }).join(''); 394 403 } else { … … 417 426 418 427 const tbody = document.getElementById('products-list'); 428 419 429 if (data.success && data.products.length > 0) { 420 430 tbody.innerHTML = data.products.map(product => ` 421 <tr>422 <td>${product.code}</td>423 <td>${product.description.substring(0, 30)}${product.description.length > 30 ? '...' : ''}</td>424 <td>$${parseFloat(product.price).toFixed(2)}</td>425 <td>${product.availability}</td>426 <td>427 <button class="btn-small" onclick="viewProduct('${product.code}')">View</button>428 </td>429 </tr>430 `).join('');431 <tr> 432 <td>${product.code}</td> 433 <td>${product.description.substring(0, 30)}${product.description.length > 30 ? '...' : ''}</td> 434 <td>$${parseFloat(product.price).toFixed(2)}</td> 435 <td>${product.availability}</td> 436 <td> 437 <button class="btn-small" onclick="viewProduct('${product.code}')">View</button> 438 </td> 439 </tr> 440 `).join(''); 431 441 } else { 432 442 tbody.innerHTML = '<tr><td colspan="5">No products found</td></tr>'; -
interfejs/store-owner.html
r4dff800 r79fff4f 18 18 <li><a href="products.html">Products</a></li> 19 19 <li><a href="#" id="dashboard-link" class="active">Store Owner</a></li> 20 <li><a href="change-password.html" class="change-password-btn">🔑 Change Password</a></li> 21 <li><a href="#" id="logout-link">Logout</a></li> 20 22 </ul> 21 23 </nav> … … 457 459 document.getElementById('owner-name').textContent = `${user.firstName} ${user.lastName || ''}`; 458 460 461 // Logout functionality 462 document.getElementById('logout-link').addEventListener('click', async (e) => { 463 e.preventDefault(); 464 await logout(); 465 }); 466 459 467 // Load stores 460 468 const stores = user.stores || []; … … 484 492 document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active')); 485 493 document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active')); 494 486 495 btn.classList.add('active'); 487 496 document.getElementById(`${btn.dataset.tab}-tab`).classList.add('active'); … … 558 567 const response = await fetch(`/api/store-products?storeId=${storeId}`); 559 568 const data = await response.json(); 569 560 570 const tbody = document.getElementById('products-list'); 561 571 562 572 if (data.success && data.products.length > 0) { 563 573 tbody.innerHTML = data.products.map(product => ` 564 <tr>565 <td>${product.code}</td>566 <td>${product.description.substring(0, 30)}${product.description.length > 30 ? '...' : ''}</td>567 <td>$${parseFloat(product.price).toFixed(2)}</td>568 <td>${product.availability}</td>569 <td>${product.category_name || 'General'}</td>570 <td>571 <button class="btn-small" onclick="editProduct('${product.code}')">Edit</button>572 <button class="btn-small" style="background: var(--danger);" onclick="deleteProduct('${product.code}', '${storeId}')">Delete</button>573 </td>574 </tr>575 `).join('');574 <tr> 575 <td>${product.code}</td> 576 <td>${product.description.substring(0, 30)}${product.description.length > 30 ? '...' : ''}</td> 577 <td>$${parseFloat(product.price).toFixed(2)}</td> 578 <td>${product.availability}</td> 579 <td>${product.category_name || 'General'}</td> 580 <td> 581 <button class="btn-small" onclick="editProduct('${product.code}')">Edit</button> 582 <button class="btn-small" style="background: var(--danger);" onclick="deleteProduct('${product.code}', '${storeId}')">Delete</button> 583 </td> 584 </tr> 585 `).join(''); 576 586 } else { 577 587 tbody.innerHTML = '<tr><td colspan="6">No products found</td></tr>'; … … 587 597 const response = await fetch(`/api/store-orders?storeId=${storeId}`); 588 598 const data = await response.json(); 599 589 600 const tbody = document.getElementById('orders-list'); 590 601 … … 593 604 const total = order.items ? order.items.reduce((sum, item) => 594 605 sum + (item.price * item.quantity), 0) : 0; 606 595 607 return ` 596 <tr>597 <td>${order.order_num}</td>598 <td>${order.first_name} ${order.last_name}</td>599 <td>${new Date(order.order_date).toLocaleDateString()}</td>600 <td><span class="status-badge status-${order.status}">${order.status}</span></td>601 <td>$${total.toFixed(2)}</td>602 <td>603 <button class="btn-small" onclick="viewOrder('${order.order_num}')">View</button>604 </td>605 </tr>606 `;608 <tr> 609 <td>${order.order_num}</td> 610 <td>${order.first_name} ${order.last_name}</td> 611 <td>${new Date(order.order_date).toLocaleDateString()}</td> 612 <td><span class="status-badge status-${order.status}">${order.status}</span></td> 613 <td>$${total.toFixed(2)}</td> 614 <td> 615 <button class="btn-small" onclick="viewOrder('${order.order_num}')">View</button> 616 </td> 617 </tr> 618 `; 607 619 }).join(''); 608 620 } else { … … 619 631 const response = await fetch(`/api/store-employees?storeId=${storeId}`); 620 632 const data = await response.json(); 633 621 634 const tbody = document.getElementById('employees-list'); 622 635 623 636 if (data.success && data.employees.length > 0) { 624 637 tbody.innerHTML = data.employees.map(emp => ` 625 <tr>626 <td>${emp.id}</td>627 <td>${emp.first_name} ${emp.last_name}</td>628 <td>${emp.email}</td>629 <td>${emp.date_of_hire || 'N/A'}</td>630 <td><span class="status-badge">${emp.permission_type || 'EMPLOYEE'}</span></td>631 <td>632 <button class="btn-small" onclick="editEmployee('${emp.id}')">Edit</button>633 <button class="btn-small" style="background: var(--danger);" onclick="deleteEmployee('${emp.id}', '${storeId}')">Delete</button>634 </td>635 </tr>636 `).join('');638 <tr> 639 <td>${emp.id}</td> 640 <td>${emp.first_name} ${emp.last_name}</td> 641 <td>${emp.email}</td> 642 <td>${emp.date_of_hire || 'N/A'}</td> 643 <td><span class="status-badge">${emp.permission_type || 'EMPLOYEE'}</span></td> 644 <td> 645 <button class="btn-small" onclick="editEmployee('${emp.id}')">Edit</button> 646 <button class="btn-small" style="background: var(--danger);" onclick="deleteEmployee('${emp.id}', '${storeId}')">Delete</button> 647 </td> 648 </tr> 649 `).join(''); 637 650 } else { 638 651 tbody.innerHTML = '<tr><td colspan="6">No employees found</td></tr>'; … … 661 674 `<option value="${cat.category_id}">${cat.name}</option>` 662 675 ).join(''); 676 663 677 document.getElementById('category-parent').innerHTML = '<option value="">None (Top Level Category)</option>' + parentOptions; 664 678 } … … 787 801 document.getElementById('add-product-form').reset(); 788 802 loadProducts(storeId); 803 789 804 // Switch to products tab 790 805 document.querySelector('[data-tab="products"]').click(); … … 839 854 document.getElementById('add-employee-form').reset(); 840 855 loadEmployees(storeId); 856 841 857 // Switch to employees tab 842 858 document.querySelector('[data-tab="employees"]').click(); … … 1061 1077 1062 1078 document.getElementById('report-results').innerHTML = ` 1063 <div class="dashboard-section">1064 <h4>Generating Report...</h4>1065 <div class="loading">Please wait</div>1066 </div>1067 `;1079 <div class="dashboard-section"> 1080 <h4>Generating Report...</h4> 1081 <div class="loading">Please wait</div> 1082 </div> 1083 `; 1068 1084 1069 1085 try { … … 1084 1100 if (data.success) { 1085 1101 document.getElementById('report-results').innerHTML = ` 1086 <div class="dashboard-section">1087 <h4>Report Generated Successfully</h4>1088 <p><strong>Report ID:</strong> ${data.reportId}</p>1089 <p><strong>Period:</strong> ${data.report.period}</p>1090 <p><strong>Date Range:</strong> ${data.report.startDate} to ${data.report.endDate}</p>1091 <p><strong>Type:</strong> ${data.report.type}</p>1092 <p><strong>Generated:</strong> ${new Date(data.report.generatedAt).toLocaleString()}</p>1093 <button class="btn-small" onclick="downloadReport('${data.reportId}')">Download Report</button>1094 </div>1095 `;1102 <div class="dashboard-section"> 1103 <h4>Report Generated Successfully</h4> 1104 <p><strong>Report ID:</strong> ${data.reportId}</p> 1105 <p><strong>Period:</strong> ${data.report.period}</p> 1106 <p><strong>Date Range:</strong> ${data.report.startDate} to ${data.report.endDate}</p> 1107 <p><strong>Type:</strong> ${data.report.type}</p> 1108 <p><strong>Generated:</strong> ${new Date(data.report.generatedAt).toLocaleString()}</p> 1109 <button class="btn-small" onclick="downloadReport('${data.reportId}')">Download Report</button> 1110 </div> 1111 `; 1096 1112 } else { 1097 1113 document.getElementById('report-results').innerHTML = ` 1098 <div class="dashboard-section">1099 <p class="error">Failed to generate report: ${data.message}</p>1100 </div>1101 `;1114 <div class="dashboard-section"> 1115 <p class="error">Failed to generate report: ${data.message}</p> 1116 </div> 1117 `; 1102 1118 } 1103 1119 } catch (error) { 1104 1120 console.error('Error generating report:', error); 1105 1121 document.getElementById('report-results').innerHTML = ` 1106 <div class="dashboard-section">1107 <p class="error">An error occurred while generating the report</p>1108 </div>1109 `;1122 <div class="dashboard-section"> 1123 <p class="error">An error occurred while generating the report</p> 1124 </div> 1125 `; 1110 1126 } 1111 1127 } -
server.js
r4dff800 r79fff4f 10 10 11 11 const port = process.env.PORT || 3000; 12 12 13 const sessions = new Map(); 13 14 const verificationCodes = new Map(); … … 31 32 } 32 33 }; 34 33 35 emailTransporter = nodemailer.createTransport(emailConfig); 36 34 37 emailTransporter.verify(function(error, success) { 35 38 if (error) { … … 52 55 const codeMatch = mailOptions.html.match(/\b\d{6}\b/); 53 56 const code = codeMatch ? codeMatch[0] : 'unknown'; 57 54 58 console.log(''); 55 59 console.log('🎯 ===== VERIFICATION CODE ====='); … … 60 64 console.log('================================'); 61 65 console.log(''); 66 62 67 resolve({ messageId: 'dev-' + Date.now() }); 63 68 }); … … 932 937 } 933 938 rolesInserted++; 939 934 940 if (rolesInserted === roles.length) { 935 941 console.log('✅ Roles inserted'); 936 937 942 // Create admin user with ID 000000 938 943 createAdminUser(); … … 1151 1156 // Check if store owner or employee 1152 1157 const personalId = userId.replace('personal_', ''); 1158 1153 1159 database.database.get( 1154 1160 'SELECT boss_id FROM boss WHERE boss_id = ?', … … 1201 1207 body += chunk.toString(); 1202 1208 }); 1203 1204 1209 req.on('end', () => { 1205 1210 const { username, email, password, userType, firstName, lastName } = JSON.parse(body); … … 1266 1271 console.log('✅ Verification email sent to:', email); 1267 1272 database.logAudit(null, 'REGISTER_ATTEMPT', 'user', null, `Registration attempt for ${email} as ${userType}`, ipAddress); 1268 1269 1273 res.writeHead(200, { 'Content-Type': 'application/json' }); 1270 1274 res.end(JSON.stringify({ … … 1294 1298 body += chunk.toString(); 1295 1299 }); 1296 1297 1300 req.on('end', () => { 1298 1301 const formData = JSON.parse(body); … … 1325 1328 1326 1329 const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; 1330 1327 1331 if (!emailRegex.test(formData.ownerEmail)) { 1328 1332 res.writeHead(400, { 'Content-Type': 'application/json' }); … … 1406 1410 // Next store number is max + 1, starting from 1 if no stores exist 1407 1411 let nextStoreNumber = 1; 1412 1408 1413 if (result && result.max_store_num) { 1409 1414 // Extract numeric part from store_id (format: XXX) … … 1461 1466 console.log('✅ Store registration email sent to:', formData.ownerEmail); 1462 1467 database.logAudit(null, 'STORE_REGISTER_ATTEMPT', 'store', null, `Store registration attempt: ${formData.storeName}`, ipAddress); 1463 1464 1468 res.writeHead(200, { 'Content-Type': 'application/json' }); 1465 1469 res.end(JSON.stringify({ … … 1494 1498 body += chunk.toString(); 1495 1499 }); 1496 1497 1500 req.on('end', () => { 1498 1501 const { firstName, lastName, email, password, address, city, postcode, country, isDefaultAddress } = JSON.parse(body); … … 1559 1562 console.log('✅ Verification email sent to:', email); 1560 1563 database.logAudit(null, 'CLIENT_REGISTER_ATTEMPT', 'client', null, `Client registration attempt for ${email}`, ipAddress); 1561 1562 1564 res.writeHead(200, { 'Content-Type': 'application/json' }); 1563 1565 res.end(JSON.stringify({ … … 1586 1588 body += chunk.toString(); 1587 1589 }); 1588 1589 1590 req.on('end', () => { 1590 1591 const { email } = JSON.parse(body); … … 1721 1722 body += chunk.toString(); 1722 1723 }); 1723 1724 1724 req.on('end', () => { 1725 1725 const { email, code } = JSON.parse(body); … … 1793 1793 database.database.run('ROLLBACK'); 1794 1794 console.error('Error inserting personal:', err); 1795 1795 1796 if (err.code === '23505') { 1796 1797 res.writeHead(400, { 'Content-Type': 'application/json' }); … … 1841 1842 } 1842 1843 1843 database.database.run('COMMIT', (commitErr) => { 1844 if (commitErr) { 1845 console.error('Error committing transaction:', commitErr); 1846 database.database.run('ROLLBACK'); 1847 res.writeHead(500, { 'Content-Type': 'application/json' }); 1848 res.end(JSON.stringify({ success: false, message: 'Error completing registration' })); 1849 return; 1844 // Also create entry in users table for login with force_password_change = 1 1845 database.database.run( 1846 'INSERT INTO users (id, username, email, password, user_type, force_password_change) VALUES (?, ?, ?, ?, ?, ?)', 1847 [ 1848 tempStoreData.personalId, 1849 `${tempStoreData.ownerFirstName} ${tempStoreData.ownerLastName}`, 1850 tempStoreData.ownerEmail, 1851 bcrypt.hashSync(tempStoreData.password, 10), 1852 'store_owner', 1853 1 1854 ], 1855 (err) => { 1856 if (err) { 1857 console.error('Error creating user entry for store owner:', err); 1858 } 1859 1860 database.database.run('COMMIT', (commitErr) => { 1861 if (commitErr) { 1862 console.error('Error committing transaction:', commitErr); 1863 database.database.run('ROLLBACK'); 1864 res.writeHead(500, { 'Content-Type': 'application/json' }); 1865 res.end(JSON.stringify({ success: false, message: 'Error completing registration' })); 1866 return; 1867 } 1868 1869 tempStoreRegistrations.delete(code); 1870 verificationCodes.delete(email); 1871 1872 console.log(`✅ Store registration completed successfully:`); 1873 console.log(` Store ID: ${tempStoreData.storeId}`); 1874 console.log(` Store Name: ${tempStoreData.storeName}`); 1875 console.log(` Personal ID: ${tempStoreData.personalId}`); 1876 console.log(` Owner: ${tempStoreData.ownerFirstName} ${tempStoreData.ownerLastName}`); 1877 1878 database.logAudit(tempStoreData.personalId, 'STORE_REGISTER_SUCCESS', 'store', tempStoreData.storeId, `Store registered: ${tempStoreData.storeName}`, ipAddress); 1879 1880 res.writeHead(200, { 'Content-Type': 'application/json' }); 1881 res.end(JSON.stringify({ 1882 success: true, 1883 message: 'Store registration successful! You can now login.', 1884 storeId: tempStoreData.storeId, 1885 storeIdPadded: tempStoreData.storeIdPadded, 1886 storeName: tempStoreData.storeName, 1887 personalId: tempStoreData.personalId, 1888 userType: 'store_owner', 1889 redirectTo: 'login.html' 1890 })); 1891 }); 1850 1892 } 1851 1852 tempStoreRegistrations.delete(code); 1853 verificationCodes.delete(email); 1854 1855 console.log(`✅ Store registration completed successfully:`); 1856 console.log(` Store ID: ${tempStoreData.storeId}`); 1857 console.log(` Store Name: ${tempStoreData.storeName}`); 1858 console.log(` Personal ID: ${tempStoreData.personalId}`); 1859 console.log(` Owner: ${tempStoreData.ownerFirstName} ${tempStoreData.ownerLastName}`); 1860 1861 database.logAudit(tempStoreData.personalId, 'STORE_REGISTER_SUCCESS', 'store', tempStoreData.storeId, `Store registered: ${tempStoreData.storeName}`, ipAddress); 1862 1863 res.writeHead(200, { 'Content-Type': 'application/json' }); 1864 res.end(JSON.stringify({ 1865 success: true, 1866 message: 'Store registration successful! You can now login.', 1867 storeId: tempStoreData.storeId, 1868 storeIdPadded: tempStoreData.storeIdPadded, 1869 storeName: tempStoreData.storeName, 1870 personalId: tempStoreData.personalId, 1871 userType: 'store_owner', 1872 redirectTo: 'login.html' 1873 })); 1874 }); 1893 ); 1875 1894 } 1876 1895 ); … … 1952 1971 } else { 1953 1972 const userId = 'user_' + Date.now().toString().slice(-8); 1973 1954 1974 database.createUser(userId, tempUserData.username, tempUserData.email, tempUserData.password, tempUserData.userType, (err, userId) => { 1955 1975 if (err) { … … 1982 2002 body += chunk.toString(); 1983 2003 }); 1984 1985 2004 req.on('end', () => { 1986 2005 const { email, password } = JSON.parse(body); … … 2003 2022 2004 2023 const twoFACode = generateVerificationCode(); 2005 2006 2024 verificationCodes.set(adminUser.email, { 2007 2025 code: twoFACode, … … 2048 2066 } 2049 2067 }); 2068 2050 2069 return; 2051 2070 } … … 2094 2113 'Set-Cookie': `sessionId=${sessionId}; HttpOnly; Path=/; Max-Age=3600; SameSite=Strict` 2095 2114 }); 2115 2096 2116 res.end(JSON.stringify({ 2097 2117 success: true, … … 2155 2175 2156 2176 const twoFACode = generateVerificationCode(); 2157 2158 2177 verificationCodes.set(personal.email, { 2159 2178 code: twoFACode, … … 2216 2235 2217 2236 const twoFACode = generateVerificationCode(); 2218 2219 2237 verificationCodes.set(personal.email, { 2220 2238 code: twoFACode, … … 2278 2296 2279 2297 const twoFACode = generateVerificationCode(); 2280 2281 2298 verificationCodes.set(userByUsername.email, { 2282 2299 code: twoFACode, … … 2329 2346 2330 2347 const twoFACode = generateVerificationCode(); 2331 2332 2348 verificationCodes.set(user.email, { 2333 2349 code: twoFACode, … … 2396 2412 body += chunk.toString(); 2397 2413 }); 2398 2399 2414 req.on('end', () => { 2400 2415 const { email } = JSON.parse(body); … … 2419 2434 2420 2435 const newTwoFACode = generateVerificationCode(); 2421 2422 2436 verificationCodes.set(userByUsername.email, { 2423 2437 code: newTwoFACode, … … 2456 2470 2457 2471 const newTwoFACode = generateVerificationCode(); 2458 2459 2472 verificationCodes.set(user.email, { 2460 2473 code: newTwoFACode, … … 2497 2510 body += chunk.toString(); 2498 2511 }); 2499 2500 2512 req.on('end', () => { 2501 2513 const { email, code } = JSON.parse(body); … … 2532 2544 verificationCodes.delete(email); 2533 2545 2546 // Determine redirect based on user type 2547 let redirectTo = 'change-password.html?forced=true'; 2548 if (verificationData.userType === 'store_owner') { 2549 redirectTo = 'change-password.html?forced=true&redirect=store-owner.html'; 2550 } else if (verificationData.userType === 'store_employee') { 2551 redirectTo = 'change-password.html?forced=true&redirect=store-employee.html'; 2552 } else if (verificationData.userType === 'admin') { 2553 redirectTo = 'change-password.html?forced=true&redirect=admin.html'; 2554 } else if (verificationData.userType === 'client') { 2555 redirectTo = 'change-password.html?forced=true&redirect=client-dashboard.html'; 2556 } 2557 2534 2558 res.writeHead(200, { 2535 2559 'Content-Type': 'application/json', 2536 2560 'Set-Cookie': `sessionId=${tempSessionId}; HttpOnly; Path=/; Max-Age=3600; SameSite=Strict` 2537 2561 }); 2562 2538 2563 res.end(JSON.stringify({ 2539 2564 success: true, … … 2541 2566 requiresPasswordChange: true, 2542 2567 userType: verificationData.userType, 2543 redirectTo: 'change-password.html?forced=true'2568 redirectTo: redirectTo 2544 2569 })); 2545 2570 … … 2590 2615 'Set-Cookie': `sessionId=${sessionId}; HttpOnly; Path=/; Max-Age=3600; SameSite=Strict` 2591 2616 }); 2617 2592 2618 res.end(JSON.stringify({ 2593 2619 success: true, … … 2616 2642 'Set-Cookie': 'sessionId=; HttpOnly; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Strict' 2617 2643 }); 2644 2618 2645 res.end(JSON.stringify({ success: true, message: 'Successfully logged out' })); 2619 2646 } … … 2736 2763 }); 2737 2764 } 2738 2739 2765 else if (userIdStr.startsWith('personal_')) { 2740 2766 const personalId = userIdStr.replace('personal_', ''); … … 2885 2911 body += chunk.toString(); 2886 2912 }); 2887 2888 2913 req.on('end', () => { 2889 2914 const categoryData = JSON.parse(body); … … 2968 2993 body += chunk.toString(); 2969 2994 }); 2970 2971 2995 req.on('end', () => { 2972 2996 const orderData = JSON.parse(body); … … 3066 3090 body += chunk.toString(); 3067 3091 }); 3068 3069 3092 req.on('end', () => { 3070 3093 const reviewData = JSON.parse(body); … … 3073 3096 if (userIdStr.startsWith('client_')) { 3074 3097 const clientId = parseInt(userIdStr.replace('client_', '')); 3075 3076 3098 reviewData.client_id = clientId; 3077 3099 … … 3100 3122 body += chunk.toString(); 3101 3123 }); 3102 3103 3124 req.on('end', () => { 3104 3125 const requestData = JSON.parse(body); … … 3170 3191 body += chunk.toString(); 3171 3192 }); 3172 3173 3193 req.on('end', () => { 3174 3194 const refundData = JSON.parse(body); … … 3240 3260 body += chunk.toString(); 3241 3261 }); 3242 3243 3262 req.on('end', () => { 3244 3263 const productData = JSON.parse(body); … … 3333 3352 body += chunk.toString(); 3334 3353 }); 3335 3336 3354 req.on('end', () => { 3337 3355 const productData = JSON.parse(body); … … 3427 3445 } 3428 3446 3447 // Updated /api/force-change-password endpoint with redirect handling 3429 3448 else if (pathname === '/api/force-change-password' && req.method === 'POST') { 3430 3449 const cookies = parseCookies(req); … … 3439 3458 3440 3459 let body = ''; 3441 3442 3460 req.on('data', chunk => { 3443 3461 body += chunk.toString(); 3444 3462 }); 3445 3446 3463 req.on('end', () => { 3447 3464 try { 3448 const { newPassword, confirmPassword } = JSON.parse(body);3465 const { newPassword, confirmPassword, redirectTo } = JSON.parse(body); 3449 3466 3450 3467 if (!newPassword || !confirmPassword) { … … 3508 3525 // Create new permanent session 3509 3526 const newSessionId = generateSessionId(); 3510 sessions.set(newSessionId, String(userId)); 3511 3512 // Determine redirect based on user type 3513 let redirectTo = 'dashboard.html'; 3514 3515 if (user.username === 'admin' || user.user_type === 'admin') { 3516 redirectTo = 'admin.html'; 3517 } else if (user.user_type === 'store_owner') { 3518 redirectTo = 'store-owner.html'; 3519 } else if (user.user_type === 'store_employee') { 3520 redirectTo = 'store-employee.html'; 3521 } else if (user.user_type === 'client') { 3522 redirectTo = 'client-dashboard.html'; 3527 3528 // Determine how to store the user ID based on user type 3529 let sessionUserId = String(userId); 3530 3531 if (user.user_type === 'store_owner' || user.user_type === 'store_employee') { 3532 sessionUserId = `personal_${userId}`; 3523 3533 } 3524 3534 3525 console.log(`Password changed successfully for user ${userId}, redirecting to ${redirectTo}`); 3535 sessions.set(newSessionId, sessionUserId); 3536 3537 // Determine redirect based on user type or provided redirectTo 3538 let finalRedirect = redirectTo || 'dashboard.html'; 3539 3540 if (!redirectTo) { 3541 if (user.username === 'admin' || user.user_type === 'admin') { 3542 finalRedirect = 'admin.html'; 3543 } else if (user.user_type === 'store_owner') { 3544 finalRedirect = 'store-owner.html'; 3545 } else if (user.user_type === 'store_employee') { 3546 finalRedirect = 'store-employee.html'; 3547 } else if (user.user_type === 'client') { 3548 finalRedirect = 'client-dashboard.html'; 3549 } 3550 } 3551 3552 console.log(`Password changed successfully for user ${userId}, redirecting to ${finalRedirect}`); 3526 3553 3527 3554 database.logAudit(userId, 'FORCED_PASSWORD_CHANGE', 'auth', userId.toString(), … … 3531 3558 res.writeHead(200, { 3532 3559 'Content-Type': 'application/json', 3533 'Set-Cookie': `sessionId=${newSessionId}; HttpOnly; Path=/; Max-Age= 3600; SameSite=Strict`3560 'Set-Cookie': `sessionId=${newSessionId}; HttpOnly; Path=/; Max-Age=86400; SameSite=Strict` // Extended to 24 hours 3534 3561 }); 3562 3535 3563 res.end(JSON.stringify({ 3536 3564 success: true, 3537 3565 message: 'Password changed successfully.', 3538 redirectTo: redirectTo,3566 redirectTo: finalRedirect, 3539 3567 userType: user.user_type || 'user' 3540 3568 })); … … 3584 3612 (err, boss) => { 3585 3613 let userType = 'store_employee'; 3586 let redirectTo ='store-employee.html';3614 let finalRedirect = redirectTo || 'store-employee.html'; 3587 3615 3588 3616 if (boss) { 3589 3617 userType = 'store_owner'; 3590 redirectTo ='store-owner.html';3618 finalRedirect = redirectTo || 'store-owner.html'; 3591 3619 } 3592 3620 … … 3594 3622 tempAdminSessions.delete(sessionId); 3595 3623 3596 // Create new permanent session 3624 // Create new permanent session with personal_ prefix 3597 3625 const newSessionId = generateSessionId(); 3598 3626 sessions.set(newSessionId, `personal_${userId}`); 3599 3627 3600 console.log(`Password changed successfully for ${userType} ${userId}, redirecting to ${ redirectTo}`);3628 console.log(`Password changed successfully for ${userType} ${userId}, redirecting to ${finalRedirect}`); 3601 3629 3602 3630 database.logAudit(userId, 'FORCED_PASSWORD_CHANGE', 'auth', userId.toString(), 3603 3631 `${userType} forced password change completed`, ipAddress); 3604 3632 3605 // Set the cookie with proper options 3633 // Set the cookie with proper options - extended to 24 hours 3606 3634 res.writeHead(200, { 3607 3635 'Content-Type': 'application/json', 3608 'Set-Cookie': `sessionId=${newSessionId}; HttpOnly; Path=/; Max-Age= 3600; SameSite=Strict`3636 'Set-Cookie': `sessionId=${newSessionId}; HttpOnly; Path=/; Max-Age=86400; SameSite=Strict` 3609 3637 }); 3638 3610 3639 res.end(JSON.stringify({ 3611 3640 success: true, 3612 3641 message: 'Password changed successfully.', 3613 redirectTo: redirectTo,3642 redirectTo: finalRedirect, 3614 3643 userType: userType 3615 3644 })); … … 3668 3697 body += chunk.toString(); 3669 3698 }); 3670 3671 3699 req.on('end', () => { 3672 3700 const { firstName, lastName, ssn, email, password, storeId, dateOfHire } = JSON.parse(body); … … 3775 3803 database.database.run('ROLLBACK'); 3776 3804 console.error('Error inserting personal:', err); 3805 3777 3806 if (err.code === '23505') { 3778 3807 res.writeHead(400, { 'Content-Type': 'application/json' }); 3779 res.end(JSON.stringify({ success: false, message: 'This personal ID is already taken. Please try again.' })); 3808 res.end(JSON.stringify({ 3809 success: false, 3810 message: 'This personal ID is already taken. Please try again.' 3811 })); 3780 3812 } else { 3781 3813 res.writeHead(400, { 'Content-Type': 'application/json' }); … … 3817 3849 } 3818 3850 3819 database.database.run('COMMIT', (err) => { 3820 if (err) { 3821 database.database.run('ROLLBACK'); 3822 console.error('Error committing transaction:', err); 3823 res.writeHead(500, { 'Content-Type': 'application/json' }); 3824 res.end(JSON.stringify({ success: false, message: 'Error completing registration' })); 3825 return; 3851 // Also create entry in users table for login with force_password_change = 1 3852 database.database.run( 3853 'INSERT INTO users (id, username, email, password, user_type, force_password_change) VALUES (?, ?, ?, ?, ?, ?)', 3854 [ 3855 newPersonalId, 3856 `${firstName} ${lastName}`, 3857 email, 3858 bcrypt.hashSync(password, 10), 3859 'store_employee', 3860 1 3861 ], 3862 (err) => { 3863 if (err) { 3864 console.error('Error creating user entry for employee:', err); 3865 } 3866 3867 database.database.run('COMMIT', (err) => { 3868 if (err) { 3869 database.database.run('ROLLBACK'); 3870 console.error('Error committing transaction:', err); 3871 res.writeHead(500, { 'Content-Type': 'application/json' }); 3872 res.end(JSON.stringify({ success: false, message: 'Error completing registration' })); 3873 return; 3874 } 3875 3876 database.logAudit(personalId, 'EMPLOYEE_REGISTERED', 'employee', newPersonalId, `Employee registered: ${firstName} ${lastName}`, ipAddress); 3877 3878 res.writeHead(200, { 'Content-Type': 'application/json' }); 3879 res.end(JSON.stringify({ 3880 success: true, 3881 message: 'Employee registered successfully!', 3882 employeeId: newPersonalId, 3883 name: `${firstName} ${lastName}` 3884 })); 3885 }); 3826 3886 } 3827 3828 database.logAudit(personalId, 'EMPLOYEE_REGISTERED', 'employee', newPersonalId, `Employee registered: ${firstName} ${lastName}`, ipAddress); 3829 3830 res.writeHead(200, { 'Content-Type': 'application/json' }); 3831 res.end(JSON.stringify({ 3832 success: true, 3833 message: 'Employee registered successfully!', 3834 employeeId: newPersonalId, 3835 name: `${firstName} ${lastName}` 3836 })); 3837 }); 3887 ); 3838 3888 } 3839 3889 ); … … 3887 3937 body += chunk.toString(); 3888 3938 }); 3889 3890 3939 req.on('end', () => { 3891 3940 const { employeeId, storeId } = JSON.parse(body); … … 3975 4024 } 3976 4025 3977 database.database.run('COMMIT', (commitErr) => { 3978 if (commitErr) { 3979 database.database.run('ROLLBACK'); 3980 console.error('Error committing transaction:', commitErr); 3981 res.writeHead(500, { 'Content-Type': 'application/json' }); 3982 res.end(JSON.stringify({ success: false, message: 'Error completing deletion' })); 3983 return; 4026 // Also delete from users table 4027 database.database.run( 4028 'DELETE FROM users WHERE id = ?', 4029 [employeeId], 4030 (err) => { 4031 if (err) { 4032 console.error('Error deleting from users:', err); 4033 } 4034 4035 database.database.run('COMMIT', (commitErr) => { 4036 if (commitErr) { 4037 database.database.run('ROLLBACK'); 4038 console.error('Error committing transaction:', commitErr); 4039 res.writeHead(500, { 'Content-Type': 'application/json' }); 4040 res.end(JSON.stringify({ success: false, message: 'Error completing deletion' })); 4041 return; 4042 } 4043 4044 database.logAudit(personalId, 'EMPLOYEE_DELETED', 'employee', employeeId, `Employee deleted from store ${storeId}`, ipAddress); 4045 4046 res.writeHead(200, { 'Content-Type': 'application/json' }); 4047 res.end(JSON.stringify({ 4048 success: true, 4049 message: 'Employee deleted successfully' 4050 })); 4051 }); 3984 4052 } 3985 3986 database.logAudit(personalId, 'EMPLOYEE_DELETED', 'employee', employeeId, `Employee deleted from store ${storeId}`, ipAddress); 3987 3988 res.writeHead(200, { 'Content-Type': 'application/json' }); 3989 res.end(JSON.stringify({ 3990 success: true, 3991 message: 'Employee deleted successfully' 3992 })); 3993 }); 4053 ); 3994 4054 } 3995 4055 ); … … 4046 4106 body += chunk.toString(); 4047 4107 }); 4048 4049 4108 req.on('end', () => { 4050 4109 const { employeeId, storeId, status } = JSON.parse(body); … … 4153 4212 body += chunk.toString(); 4154 4213 }); 4155 4156 4214 req.on('end', () => { 4157 4215 const { employeeId, storeId, firstName, lastName, email } = JSON.parse(body); … … 4223 4281 res.end(JSON.stringify({ success: false, message: 'Error updating employee information' })); 4224 4282 return; 4283 } 4284 4285 // Also update in users table if email was changed 4286 if (email) { 4287 database.database.run( 4288 'UPDATE users SET email = ? WHERE id = ?', 4289 [email, employeeId], 4290 (err) => { 4291 if (err) { 4292 console.error('Error updating user email:', err); 4293 } 4294 } 4295 ); 4296 } 4297 4298 if (firstName || lastName) { 4299 database.database.get( 4300 'SELECT first_name, last_name FROM personal WHERE id = ?', 4301 [employeeId], 4302 (err, personal) => { 4303 if (!err && personal) { 4304 const newUsername = `${personal.first_name} ${personal.last_name}`; 4305 database.database.run( 4306 'UPDATE users SET username = ? WHERE id = ?', 4307 [newUsername, employeeId], 4308 (err) => { 4309 if (err) { 4310 console.error('Error updating user username:', err); 4311 } 4312 } 4313 ); 4314 } 4315 } 4316 ); 4225 4317 } 4226 4318 … … 4582 4674 body += chunk.toString(); 4583 4675 }); 4584 4585 4676 req.on('end', () => { 4586 4677 const { productCode, storeId } = JSON.parse(body); … … 4652 4743 body += chunk.toString(); 4653 4744 }); 4654 4655 4745 req.on('end', () => { 4656 4746 const { storeId, period, startDate, endDate, type } = JSON.parse(body);
Note:
See TracChangeset
for help on using the changeset viewer.
