From 7937cd1cf718c69dcebefbc0ed4be8a1adb364fb Mon Sep 17 00:00:00 2001 From: Nguyen Ngo Date: Wed, 13 Aug 2025 15:52:14 -0400 Subject: [PATCH] Update dashboard layout --- app.py | 20 +- static/css/dashboard.css | 328 +++++++++ templates/dashboard.html | 1443 ++++++++++++++++++++++++++++++-------- 3 files changed, 1489 insertions(+), 302 deletions(-) diff --git a/app.py b/app.py index ce4b3d5..d79105a 100644 --- a/app.py +++ b/app.py @@ -1314,23 +1314,27 @@ def logout(): @app.route('/dashboard') @login_required def dashboard(): - """Main dashboard after login - Fixed to show all QR codes""" + """Enhanced project-centric dashboard""" try: user = User.query.get(session['user_id']) - # Get ALL QR codes (both active and inactive) with proper error handling - # The frontend filtering will handle display logic + # Get ALL QR codes and projects for project-centric view qr_codes = QRCode.query.order_by(QRCode.created_date.desc()).all() + projects = Project.query.order_by(Project.name.asc()).all() - return render_template('dashboard.html', user=user, qr_codes=qr_codes) + # Log dashboard access with project info + logger_handler.logger.info(f"User {session['username']} accessed project dashboard: {len(qr_codes)} QR codes, {len(projects)} projects") + + return render_template('dashboard.html', + user=user, + qr_codes=qr_codes, + projects=projects) except Exception as e: logger_handler.log_database_error('dashboard_load', e) - print(f"Error fetching QR codes: {e}") + print(f"Error loading dashboard: {e}") flash('Error loading dashboard. Please try again.', 'error') - qr_codes = [] - user = None - return render_template('dashboard.html', user=user, qr_codes=qr_codes) + return redirect(url_for('login')) # User management routes @app.route('/profile', methods=['GET', 'POST']) diff --git a/static/css/dashboard.css b/static/css/dashboard.css index a2a5120..a5fb2db 100644 --- a/static/css/dashboard.css +++ b/static/css/dashboard.css @@ -805,3 +805,331 @@ align-items: center; } } + +/* Enhanced QR Item Animations and Interactions */ +.qr-item { + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + overflow: hidden; + position: relative; +} + +.qr-item-details { + max-height: 0; + overflow: hidden; + transition: max-height 0.3s ease-in-out; + padding: 0 var(--spacing-4); +} + +.qr-item.expanded .qr-item-details { + max-height: 500px; + padding: var(--spacing-4); + border-top: 1px solid var(--gray-200); + margin-top: var(--spacing-4); +} + +/* QR Item Actions */ +.qr-item-actions { + display: flex; + align-items: center; + gap: var(--spacing-2); +} + +.qr-checkbox { + width: 18px; + height: 18px; + cursor: pointer; + accent-color: var(--primary-color); +} + +.toggle-expand-btn { + background: none; + border: none; + color: var(--gray-500); + cursor: pointer; + padding: var(--spacing-2); + border-radius: var(--radius-md); + transition: var(--transition); + display: flex; + align-items: center; + justify-content: center; +} + +.toggle-expand-btn:hover { + background: var(--gray-100); + color: var(--primary-color); +} + +.toggle-expand-btn i { + transition: transform 0.3s ease; +} + +/* Detail Grid */ +.detail-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: var(--spacing-3); + margin-bottom: var(--spacing-4); +} + +.detail-item { + display: flex; + flex-direction: column; + gap: var(--spacing-1); +} + +.detail-item label { + font-weight: 600; + color: var(--gray-700); + font-size: var(--font-size-sm); + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.detail-item span { + color: var(--gray-900); + font-size: var(--font-size-sm); + word-break: break-word; +} + +.qr-url { + font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; + background: var(--gray-100); + padding: var(--spacing-1) var(--spacing-2); + border-radius: var(--radius-sm); + font-size: var(--font-size-xs); +} + +/* QR Item Footer */ +.qr-item-footer { + border-top: 1px solid var(--gray-200); + padding-top: var(--spacing-3); + margin-top: var(--spacing-3); +} + +.qr-actions { + display: flex; + gap: var(--spacing-2); + flex-wrap: wrap; +} + +.qr-actions .btn { + font-size: var(--font-size-xs); + padding: 0.375rem 0.75rem; + border-radius: var(--radius-md); +} + +/* Status and Project Badges */ +.qr-item-meta { + display: flex; + align-items: center; + gap: var(--spacing-3); + margin-top: var(--spacing-2); + flex-wrap: wrap; +} + +.qr-status, .qr-project { + display: flex; + align-items: center; + gap: var(--spacing-1); + font-size: var(--font-size-xs); + padding: 0.25rem 0.5rem; + border-radius: var(--radius-full); + font-weight: 500; +} + +.qr-status.active { + background: rgba(16, 185, 129, 0.1); + color: #047857; +} + +.qr-status.inactive { + background: rgba(239, 68, 68, 0.1); + color: #dc2626; +} + +.qr-project { + background: rgba(37, 99, 235, 0.1); + color: var(--primary-color); +} + +/* Animation classes */ +.fade-in { + opacity: 1; + transform: translateY(0); +} + +.fade-out { + opacity: 0; + transform: translateY(-10px); +} + +/* Empty state for search */ +.search-empty-state { + grid-column: 1 / -1; + text-align: center; + padding: 3rem 1.5rem; + color: var(--gray-600); +} + +.search-empty-state .empty-icon { + width: 80px; + height: 80px; + margin: 0 auto 1.5rem; + background: var(--gray-100); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 2rem; + color: var(--gray-400); +} + +.search-empty-state h3 { + font-size: 1.5rem; + font-weight: 600; + color: var(--gray-900); + margin-bottom: 0.75rem; +} + +.search-empty-state p { + font-size: 1rem; + margin-bottom: 1.5rem; + max-width: 400px; + margin-left: auto; + margin-right: auto; +} + +/* Bulk Actions Bar */ +.bulk-actions-bar { + background: linear-gradient(135deg, #fef3c7, #fde68a); + border: 1px solid #f59e0b; + border-radius: var(--radius-lg); + padding: var(--spacing-4); + margin-bottom: var(--spacing-4); + display: none; + align-items: center; + justify-content: space-between; + gap: var(--spacing-4); +} + +.bulk-actions-info { + display: flex; + align-items: center; + gap: var(--spacing-2); + font-weight: 600; + color: #92400e; +} + +.bulk-actions-buttons { + display: flex; + gap: var(--spacing-2); +} + +/* Modal Styles */ +.modal { + display: none; + position: fixed; + z-index: 1000; + left: 0; + top: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + align-items: center; + justify-content: center; + animation: fadeIn 0.3s ease; +} + +.modal-content { + background: var(--white); + border-radius: var(--radius-xl); + box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25); + max-width: 90vw; + max-height: 90vh; + overflow: auto; + animation: slideIn 0.3s ease; +} + +.modal-header { + padding: var(--spacing-6); + border-bottom: 1px solid var(--gray-200); + display: flex; + align-items: center; + justify-content: space-between; +} + +.modal-header h3 { + margin: 0; + font-size: var(--font-size-xl); + font-weight: 600; + color: var(--gray-900); +} + +.modal-close { + background: none; + border: none; + font-size: var(--font-size-xl); + color: var(--gray-500); + cursor: pointer; + padding: var(--spacing-2); + border-radius: var(--radius-md); + transition: var(--transition); +} + +.modal-close:hover { + background: var(--gray-100); + color: var(--gray-700); +} + +.modal-body { + padding: var(--spacing-6); + text-align: center; +} + +/* Animations */ +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +@keyframes slideIn { + from { + opacity: 0; + transform: translateY(-50px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +/* Responsive Design */ +@media (max-width: 768px) { + .detail-grid { + grid-template-columns: 1fr; + } + + .qr-actions { + flex-direction: column; + } + + .qr-actions .btn { + width: 100%; + justify-content: center; + } + + .bulk-actions-bar { + flex-direction: column; + text-align: center; + } + + .bulk-actions-buttons { + width: 100%; + justify-content: center; + } + + .modal-content { + margin: var(--spacing-4); + max-width: calc(100vw - 2rem); + } +} diff --git a/templates/dashboard.html b/templates/dashboard.html index 96e5c4b..2db14b0 100644 --- a/templates/dashboard.html +++ b/templates/dashboard.html @@ -1,29 +1,592 @@ -{% extends "base_authenticated.html" %} {% block title %}Dashboard - QR Code Management{% -endblock %} {% block extra_head %} +{% extends "base_authenticated.html" %} +{% block title %}Dashboard - QR Code Management{% endblock %} + +{% block extra_head %} - -{% endblock %} {% block content %} + + +{% endblock %} + +{% block content %}

Welcome back, {{ session.full_name }}!

-

Manage your QR codes and monitor system activity

+

Manage your QR codes organized by projects

@@ -60,325 +623,617 @@ endblock %} {% block extra_head %}
-

- {{ qr_codes|selectattr('active_status', 'equalto', True)|list|length - }} -

+

{{ qr_codes|selectattr('active_status', 'equalto', True)|list|length }}

Active QR Codes

-
-

- {{ qr_codes|selectattr('active_status', 'equalto', - False)|list|length }} -

+

{{ qr_codes|selectattr('active_status', 'equalto', False)|list|length }}

Inactive QR Codes

+
+
+ +
+
+

{{ projects|length }}

+

Total Projects

+
+
{% endif %} - -
-
-

Your QR Codes

-
-