Update dashboard

This commit is contained in:
2025-09-01 10:41:22 -04:00
parent eb62501bbb
commit f3dbaf2b81
5 changed files with 1679 additions and 1402 deletions
+7
View File
@@ -31,6 +31,13 @@ app.config['TEMPLATES_AUTO_RELOAD'] = os.environ.get('TEMPLATES_AUTO_RELOAD')
# Initialize database # Initialize database
db = SQLAlchemy(app) db = SQLAlchemy(app)
@app.context_processor
def inject_company_name():
"""Make COMPANY_NAME available to all templates"""
return {
'COMPANY_NAME': os.environ.get('COMPANY_NAME', 'QR Code Management System')
}
def create_performance_indexes(): def create_performance_indexes():
"""Create performance optimization indexes""" """Create performance optimization indexes"""
try: try:
+161 -24
View File
@@ -4,31 +4,163 @@
*/ */
/* Dashboard Header */ /* Dashboard Header */
.dashboard-header { .header-stats {
display: flex; display: flex;
justify-content: space-between; align-items: center;
align-items: flex-start; }
margin-bottom: var(--spacing-8);
padding: var(--spacing-6); .header-stats-grid {
background: var(--white); display: grid;
border-radius: var(--radius-xl); grid-template-columns: repeat(2, 1fr);
box-shadow: var(--shadow); gap: 1rem;
min-width: 400px;
}
@media (min-width: 1200px) {
.header-stats-grid {
grid-template-columns: repeat(4, 1fr);
min-width: 500px;
}
}
.header-stat-card {
background: rgba(255, 255, 255, 0.95);
padding: 1rem;
border-radius: 0.5rem;
border: 1px solid #e2e8f0;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
display: flex;
align-items: center;
gap: 0.75rem;
transition: all 0.2s ease-in-out;
backdrop-filter: blur(10px);
position: relative; position: relative;
overflow: hidden; overflow: hidden;
} }
.dashboard-header::before { .header-stat-card::before {
content: ""; content: "";
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
right: 0; right: 0;
height: 4px; height: 2px;
background: linear-gradient( }
90deg,
var(--primary-color), .header-stat-card.primary::before {
var(--primary-hover) background: linear-gradient(90deg, #2563eb, #1d4ed8);
); }
.header-stat-card.success::before {
background: linear-gradient(90deg, #10b981, #047857);
}
.header-stat-card.danger::before {
background: linear-gradient(90deg, #ef4444, #dc2626);
}
.header-stat-card.info::before {
background: linear-gradient(90deg, #8b5cf6, #7c3aed);
}
.header-stat-card:hover {
transform: translateY(-1px);
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.15);
}
.header-stat-icon {
width: 36px;
height: 36px;
border-radius: 0.375rem;
display: flex;
align-items: center;
justify-content: center;
font-size: 1rem;
color: #ffffff;
flex-shrink: 0;
}
.header-stat-card.primary .header-stat-icon {
background: linear-gradient(135deg, #2563eb, #1d4ed8);
}
.header-stat-card.success .header-stat-icon {
background: linear-gradient(135deg, #10b981, #047857);
}
.header-stat-card.danger .header-stat-icon {
background: linear-gradient(135deg, #ef4444, #dc2626);
}
.header-stat-card.info .header-stat-icon {
background: linear-gradient(135deg, #8b5cf6, #7c3aed);
}
.header-stat-content h3 {
font-size: 1.25rem;
font-weight: 700;
color: #0f172a;
margin-bottom: 0.125rem;
line-height: 1;
}
.header-stat-content p {
color: #64748b;
font-size: 0.75rem;
margin: 0;
line-height: 1.2;
}
/* Update existing dashboard header to accommodate new layout */
.dashboard-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 2rem;
background: linear-gradient(135deg, #a0aff1 0%, #ebdef8 100%);
border-radius: 0.75rem;
color: white;
margin-bottom: 2rem;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
flex-wrap: wrap;
gap: 2rem;
}
/* Responsive Design for Header */
@media (max-width: 1024px) {
.dashboard-header {
flex-direction: column;
text-align: center;
gap: 1.5rem;
}
.header-stats-grid {
grid-template-columns: repeat(2, 1fr);
min-width: 300px;
}
}
@media (max-width: 768px) {
.dashboard-header {
padding: 1.5rem;
}
.header-stats-grid {
grid-template-columns: 1fr;
min-width: 250px;
}
.header-stat-card {
padding: 0.75rem;
}
.header-stat-content h3 {
font-size: 1.125rem;
}
.header-stat-content p {
font-size: 0.6875rem;
}
} }
.welcome-section h1 { .welcome-section h1 {
@@ -892,7 +1024,7 @@
} }
.qr-url { .qr-url {
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; font-family: "Monaco", "Menlo", "Ubuntu Mono", monospace;
background: var(--gray-100); background: var(--gray-100);
padding: var(--spacing-1) var(--spacing-2); padding: var(--spacing-1) var(--spacing-2);
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
@@ -927,7 +1059,8 @@
flex-wrap: wrap; flex-wrap: wrap;
} }
.qr-status, .qr-project { .qr-status,
.qr-project {
display: flex; display: flex;
align-items: center; align-items: center;
gap: var(--spacing-1); gap: var(--spacing-1);
@@ -1088,8 +1221,12 @@
/* Animations */ /* Animations */
@keyframes fadeIn { @keyframes fadeIn {
from { opacity: 0; } from {
to { opacity: 1; } opacity: 0;
}
to {
opacity: 1;
}
} }
@keyframes slideIn { @keyframes slideIn {
@@ -1108,26 +1245,26 @@
.detail-grid { .detail-grid {
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
.qr-actions { .qr-actions {
flex-direction: column; flex-direction: column;
} }
.qr-actions .btn { .qr-actions .btn {
width: 100%; width: 100%;
justify-content: center; justify-content: center;
} }
.bulk-actions-bar { .bulk-actions-bar {
flex-direction: column; flex-direction: column;
text-align: center; text-align: center;
} }
.bulk-actions-buttons { .bulk-actions-buttons {
width: 100%; width: 100%;
justify-content: center; justify-content: center;
} }
.modal-content { .modal-content {
margin: var(--spacing-4); margin: var(--spacing-4);
max-width: calc(100vw - 2rem); max-width: calc(100vw - 2rem);
+6 -2
View File
@@ -3,7 +3,7 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{% block title %}QR Code Management System{% endblock %}</title> <title>{% block title %}{{ COMPANY_NAME }}{% endblock %}</title>
<!-- Main CSS --> <!-- Main CSS -->
<link <link
@@ -87,7 +87,11 @@
</script> </script>
{% if turnstile_enabled %} {% if turnstile_enabled %}
<!-- Cloudflare Turnstile --> <!-- Cloudflare Turnstile -->
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script> <script
src="https://challenges.cloudflare.com/turnstile/v0/api.js"
async
defer
></script>
{% endif %} {% endif %}
</body> </body>
</html> </html>
+21 -14
View File
@@ -3,7 +3,7 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{% block title %}QR Code Management System{% endblock %}</title> <title>{% block title %}{{ COMPANY_NAME }}{% endblock %}</title>
<!-- Main CSS --> <!-- Main CSS -->
<link <link
@@ -55,8 +55,10 @@
</a> </a>
{% if session.role == 'admin' %} {% if session.role == 'admin' %}
<a href="{{ url_for('projects') }}" <a
class="menu-item {% if request.endpoint in ['projects', 'create_project', 'edit_project'] %}active{% endif %}"> href="{{ url_for('projects') }}"
class="menu-item {% if request.endpoint in ['projects', 'create_project', 'edit_project'] %}active{% endif %}"
>
<i class="fas fa-folder"></i> <i class="fas fa-folder"></i>
<span class="menu-text">Projects</span> <span class="menu-text">Projects</span>
</a> </a>
@@ -65,8 +67,8 @@
<span class="menu-text">Users</span> <span class="menu-text">Users</span>
</a> </a>
<a href="{{ url_for('employees') }}" class="menu-item"> <a href="{{ url_for('employees') }}" class="menu-item">
<i class="fas fa-user"></i> <i class="fas fa-user"></i>
<span class="menu-text">Employees</span> <span class="menu-text">Employees</span>
</a> </a>
<a href="{{ url_for('attendance_report') }}" class="menu-item"> <a href="{{ url_for('attendance_report') }}" class="menu-item">
<i class="fas fa-chart-line"></i> <i class="fas fa-chart-line"></i>
@@ -76,12 +78,17 @@
<i class="fas fa-calculator"></i> <i class="fas fa-calculator"></i>
<span class="menu-text">Payroll</span> <span class="menu-text">Payroll</span>
</a> </a>
<a href="{{ url_for('qr_statistics') }}" class="menu-item {% if request.endpoint == 'qr_statistics' %}active{% endif %}"> <a
href="{{ url_for('qr_statistics') }}"
class="menu-item {% if request.endpoint == 'qr_statistics' %}active{% endif %}"
>
<i class="fas fa-chart-pie"></i> <i class="fas fa-chart-pie"></i>
<span class="menu-text">Statistics</span> <span class="menu-text">Statistics</span>
</a> </a>
<a href="{{ url_for('admin_logs') }}" <a
class="menu-item {% if request.endpoint == 'admin_logs' %}active{% endif %}"> href="{{ url_for('admin_logs') }}"
class="menu-item {% if request.endpoint == 'admin_logs' %}active{% endif %}"
>
<i class="fas fa-clipboard-list"></i> <i class="fas fa-clipboard-list"></i>
<span class="menu-text">System Logs</span> <span class="menu-text">System Logs</span>
</a> </a>
@@ -137,9 +144,11 @@
<span class="hamburger-line"></span> <span class="hamburger-line"></span>
<span class="hamburger-line"></span> <span class="hamburger-line"></span>
</button> </button>
<h1 class="page-title">{% block page_title %}QR Code Management System{% endblock %}</h1> <h1 class="page-title">
{% block page_title %}{{ COMPANY_NAME }}{% endblock %}
</h1>
</div> </div>
<div class="header-right"> <div class="header-right">
<div class="user-info"> <div class="user-info">
<span class="user-name">{{ session.full_name or 'User' }}</span> <span class="user-name">{{ session.full_name or 'User' }}</span>
@@ -172,9 +181,7 @@
{% endif %} {% endwith %} {% endif %} {% endwith %}
<!-- Page Content --> <!-- Page Content -->
<div class="container"> <div class="container">{% block content %}{% endblock %}</div>
{% block content %}{% endblock %}
</div>
</main> </main>
<!-- Footer --> <!-- Footer -->
@@ -210,4 +217,4 @@
}; };
</script> </script>
</body> </body>
</html> </html>
+1484 -1362
View File
File diff suppressed because it is too large Load Diff