From ea3648ad904f7a1a4ef9bd064a3ea1194f9e11c8 Mon Sep 17 00:00:00 2001 From: NguyenND Date: Thu, 25 Jun 2026 16:40:10 -0400 Subject: [PATCH] June 25 - Optimize codes --- routes/admin_dashboard.py | 3 ++- routes/admin_users.py | 4 ++++ routes/admin_websites.py | 5 ++++- routes/bid_tracker.py | 4 ++++ static/js/app.js | 40 +++++++---------------------------- static/js/pw-strength.js | 31 +++++++++++++++++++++++++++ templates/admin/users.html | 11 ++++++++-- templates/base.html | 1 + templates/reset_password.html | 20 ++---------------- templates/user/dashboard.html | 22 +++++++++++++++++-- 10 files changed, 85 insertions(+), 56 deletions(-) create mode 100644 static/js/pw-strength.js diff --git a/routes/admin_dashboard.py b/routes/admin_dashboard.py index 1c273e9..8351b09 100644 --- a/routes/admin_dashboard.py +++ b/routes/admin_dashboard.py @@ -18,7 +18,8 @@ def dashboard(): stats = get_admin_dashboard_stats() except Exception as e: logger.error(f"Dashboard stats error: {e}") - stats = {"user_stats": [], "total_sites": 0, "total_users": 0, "active_today": 0} + stats = {"user_stats": [], "total_sites": 0, "total_users": 0, "active_today": 0, + "open_bids": 0, "bids_due_soon": 0, "ai_analyses_30d": 0} try: missed = get_missed_shifts_today() except Exception as e: diff --git a/routes/admin_users.py b/routes/admin_users.py index 3c0620d..af15b9f 100644 --- a/routes/admin_users.py +++ b/routes/admin_users.py @@ -7,6 +7,7 @@ from flask import Blueprint, render_template, request, redirect, url_for, flash, from models import ( get_all_users, create_user, update_user, delete_user, get_user_by_id, create_password_reset_token, log_action, + invalidate_admin_stats_cache, ) from utils.decorators import admin_required from utils.email import send_email @@ -38,6 +39,7 @@ def create(): try: create_user(admin_id, username, password, role, full_name, email) + invalidate_admin_stats_cache() flash(f"User '{username}' created successfully.", "success") logger.info(f"User '{username}' created by admin_id={admin_id}.") except Exception as e: @@ -60,6 +62,7 @@ def edit(user_id): try: update_user(admin_id, user_id, username, role, full_name, is_active, password, email) + invalidate_admin_stats_cache() flash(f"User '{username}' updated successfully.", "success") logger.info(f"User id={user_id} updated by admin_id={admin_id}.") except ValueError as e: @@ -77,6 +80,7 @@ def delete(user_id): admin_id = session["user"]["id"] try: delete_user(admin_id, user_id) + invalidate_admin_stats_cache() flash("User deleted successfully.", "success") logger.info(f"User id={user_id} deleted by admin_id={admin_id}.") except ValueError as e: diff --git a/routes/admin_websites.py b/routes/admin_websites.py index 43160ae..bef2394 100644 --- a/routes/admin_websites.py +++ b/routes/admin_websites.py @@ -7,7 +7,7 @@ from flask import Blueprint, render_template, request, redirect, url_for, flash, from models import ( get_all_websites, get_website_by_id, get_website_credentials, get_website_assigned_users, create_website, update_website, delete_website, - get_all_users, + get_all_users, invalidate_admin_stats_cache, ) from utils.decorators import admin_required @@ -78,6 +78,7 @@ def create(): try: create_website(admin_id, name, url, check_type, note, creds, visibility, assigned) + invalidate_admin_stats_cache() flash(f"Website '{name}' created successfully.", "success") logger.info(f"Website '{name}' created by admin_id={admin_id}.") except Exception as e: @@ -101,6 +102,7 @@ def edit(website_id): try: update_website(admin_id, website_id, name, url, check_type, note, creds, visibility, assigned) + invalidate_admin_stats_cache() flash(f"Website '{name}' updated successfully.", "success") logger.info(f"Website id={website_id} updated by admin_id={admin_id}.") except Exception as e: @@ -116,6 +118,7 @@ def delete(website_id): admin_id = session["user"]["id"] try: delete_website(admin_id, website_id) + invalidate_admin_stats_cache() flash("Website deleted (soft) successfully.", "success") logger.info(f"Website id={website_id} soft-deleted by admin_id={admin_id}.") except Exception as e: diff --git a/routes/bid_tracker.py b/routes/bid_tracker.py index 6f30a14..cd4056c 100644 --- a/routes/bid_tracker.py +++ b/routes/bid_tracker.py @@ -11,6 +11,7 @@ from models import ( get_all_bids, get_bid, create_bid, update_bid, delete_bid, get_bid_updates, add_bid_update, delete_bid_update, BID_STATUSES, log_action, get_bids_due_soon, get_admin_emails, + invalidate_admin_stats_cache, ) from utils.decorators import login_required, admin_required from utils.email import send_email @@ -78,6 +79,7 @@ def create(): bid_id = create_bid(user["id"], title, url, source, sol_no, status, due_date, notes) log_action(user["id"], "CREATE_BID", "bid_tracker", bid_id, f"Created bid '{title}' status='{status}'.") + invalidate_admin_stats_cache() flash(f"Bid '{title}' added.", "success") logger.info(f"Bid '{title}' created by user_id={user['id']}.") except Exception as e: @@ -103,6 +105,7 @@ def edit(bid_id): update_bid(user["id"], bid_id, title, url, source, sol_no, status, due_date, notes) log_action(user["id"], "UPDATE_BID", "bid_tracker", bid_id, f"Updated bid id={bid_id} status='{status}'.") + invalidate_admin_stats_cache() flash(f"Bid '{title}' updated.", "success") logger.info(f"Bid id={bid_id} updated by user_id={user['id']}.") except Exception as e: @@ -120,6 +123,7 @@ def delete(bid_id): delete_bid(user["id"], bid_id) log_action(user["id"], "DELETE_BID", "bid_tracker", bid_id, f"Deleted bid id={bid_id}.") + invalidate_admin_stats_cache() flash("Bid deleted.", "success") logger.info(f"Bid id={bid_id} deleted by user_id={user['id']}.") except Exception as e: diff --git a/static/js/app.js b/static/js/app.js index e6a97c3..7fef1eb 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -183,6 +183,7 @@ document.addEventListener('DOMContentLoaded', () => { const SESSION_MS = 30 * 60 * 1000; var warningTimer = null, expireTimer = null, countdownInterval = null; + var _lastPing = 0; function isWarningShowing() { var m = document.getElementById('modal-session-warning'); @@ -208,8 +209,15 @@ document.addEventListener('DOMContentLoaded', () => { } function resetTimers() { + var now = Date.now(); clearTimeout(warningTimer); clearTimeout(expireTimer); clearCountdown(); if (isWarningShowing()) closeModal('modal-session-warning'); + // Ping the server at most once per minute so the server-side session + // stays alive while the user is active on the page. + if (now - _lastPing > 60000) { + _lastPing = now; + fetch('/ping', { credentials: 'same-origin' }).catch(function(){}); + } warningTimer = setTimeout(function() { openModal('modal-session-warning'); @@ -311,38 +319,6 @@ document.addEventListener('DOMContentLoaded', function() { }); }); -/* ── Password strength meter ───────────────────────────────── */ -function _pwStrengthLevel(pw) { - var score = 0; - if (pw.length >= 8) score++; - if (pw.length >= 12) score++; - if (/[A-Z]/.test(pw)) score++; - if (/[0-9]/.test(pw)) score++; - if (/[^A-Za-z0-9]/.test(pw)) score++; - var map = ['pw-weak','pw-weak','pw-fair','pw-strong','pw-great','pw-great']; - var lbl = ['Weak','Weak','Fair','Strong','Very strong','Very strong']; - return { cls: map[score], label: lbl[score] }; -} - -function attachPasswordStrength(inputId, fillId, labelId) { - var input = document.getElementById(inputId); - var fill = document.getElementById(fillId); - var lbl = document.getElementById(labelId); - if (!input || !fill || !lbl) return; - input.addEventListener('input', function() { - if (!this.value) { - fill.className = 'pw-strength-fill'; - lbl.className = 'pw-strength-label'; - lbl.textContent = ''; - return; - } - var r = _pwStrengthLevel(this.value); - fill.className = 'pw-strength-fill ' + r.cls; - lbl.className = 'pw-strength-label ' + r.cls; - lbl.textContent = r.label; - }); -} - /* ── HTML escape (safe for attributes and text nodes) ──────── */ function esc(str) { return String(str || '').replace(/[&<>"']/g, function(c) { diff --git a/static/js/pw-strength.js b/static/js/pw-strength.js new file mode 100644 index 0000000..46b6c28 --- /dev/null +++ b/static/js/pw-strength.js @@ -0,0 +1,31 @@ +/* ── Password strength meter (shared by base.html and standalone pages) ── */ +function _pwStrengthLevel(pw) { + var score = 0; + if (pw.length >= 8) score++; + if (pw.length >= 12) score++; + if (/[A-Z]/.test(pw)) score++; + if (/[0-9]/.test(pw)) score++; + if (/[^A-Za-z0-9]/.test(pw)) score++; + var map = ['pw-weak','pw-weak','pw-fair','pw-strong','pw-great','pw-great']; + var lbl = ['Weak','Weak','Fair','Strong','Very strong','Very strong']; + return { cls: map[score], label: lbl[score] }; +} + +function attachPasswordStrength(inputId, fillId, labelId) { + var input = document.getElementById(inputId); + var fill = document.getElementById(fillId); + var lbl = document.getElementById(labelId); + if (!input || !fill || !lbl) return; + input.addEventListener('input', function() { + if (!this.value) { + fill.className = 'pw-strength-fill'; + lbl.className = 'pw-strength-label'; + lbl.textContent = ''; + return; + } + var r = _pwStrengthLevel(this.value); + fill.className = 'pw-strength-fill ' + r.cls; + lbl.className = 'pw-strength-label ' + r.cls; + lbl.textContent = r.label; + }); +} diff --git a/templates/admin/users.html b/templates/admin/users.html index 3bf7592..2c33b14 100644 --- a/templates/admin/users.html +++ b/templates/admin/users.html @@ -31,8 +31,8 @@ {{ 'Yes' if u.is_active else 'No' }} {{ u.created_at.strftime('%Y-%m-%d') if u.created_at else '—' }} - + {% if u.email %}