853 lines
19 KiB
HTML
853 lines
19 KiB
HTML
{% extends "base_authenticated.html" %}
|
|
{% block title %}Import Results - {{ COMPANY_NAME }}{% endblock %}
|
|
|
|
{% block extra_head %}
|
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/time_attendance.css') }}">
|
|
{% endblock %}
|
|
|
|
{% block page_title %}Import Results{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="time-attendance-page">
|
|
<!-- Page Header -->
|
|
<div class="time-attendance-header">
|
|
<div class="header-navigation">
|
|
<a href="{{ url_for('time_attendance.import_time_attendance') }}" class="back-button">
|
|
<i class="fas fa-arrow-left"></i>
|
|
Back to Import
|
|
</a>
|
|
</div>
|
|
|
|
<div class="header-content">
|
|
<h1>
|
|
{% if import_result.success %}
|
|
<i class="fas fa-check-circle text-success"></i>
|
|
Import Completed Successfully
|
|
{% else %}
|
|
<i class="fas fa-exclamation-circle text-danger"></i>
|
|
Import Failed
|
|
{% endif %}
|
|
</h1>
|
|
<p class="header-description">
|
|
{% if import_result.success %}
|
|
Your time attendance data has been successfully imported into the system.
|
|
{% else %}
|
|
There were issues importing your time attendance data. Please review the errors below.
|
|
{% endif %}
|
|
</p>
|
|
</div>
|
|
|
|
<div class="header-actions">
|
|
{% if import_result.success %}
|
|
<a href="{{ url_for('time_attendance.time_attendance_records', import_batch=import_result.batch_id) }}"
|
|
class="btn btn-primary">
|
|
<i class="fas fa-eye"></i>
|
|
View Imported Records
|
|
</a>
|
|
{% endif %}
|
|
<a href="{{ url_for('time_attendance.time_attendance_dashboard') }}" class="btn btn-secondary">
|
|
<i class="fas fa-tachometer-alt"></i>
|
|
Dashboard
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Import Summary -->
|
|
<div class="import-results {% if import_result.success %}success{% else %}error{% endif %}">
|
|
<div class="results-header">
|
|
<h2>
|
|
<i class="fas fa-chart-bar"></i>
|
|
Import Summary
|
|
</h2>
|
|
</div>
|
|
<div class="results-body">
|
|
<div class="results-summary">
|
|
<div class="summary-item">
|
|
<div class="summary-icon">
|
|
<i class="fas fa-file-alt"></i>
|
|
</div>
|
|
<div class="summary-content">
|
|
<div class="number">{{ import_result.total_records }}</div>
|
|
<div class="label">Total Records</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="summary-item success">
|
|
<div class="summary-icon">
|
|
<i class="fas fa-check-circle"></i>
|
|
</div>
|
|
<div class="summary-content">
|
|
<div class="number">{{ import_result.imported_records }}</div>
|
|
<div class="label">Successfully Imported</div>
|
|
</div>
|
|
</div>
|
|
|
|
{% if import_result.failed_records > 0 %}
|
|
<div class="summary-item error">
|
|
<div class="summary-icon">
|
|
<i class="fas fa-exclamation-circle"></i>
|
|
</div>
|
|
<div class="summary-content">
|
|
<div class="number">{{ import_result.failed_records }}</div>
|
|
<div class="label">Failed Records</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="summary-item">
|
|
<div class="summary-icon">
|
|
<i class="fas fa-percentage"></i>
|
|
</div>
|
|
<div class="summary-content">
|
|
<div class="number">
|
|
{{ "%.1f"|format((import_result.imported_records / import_result.total_records * 100) if import_result.total_records > 0 else 0) }}%
|
|
</div>
|
|
<div class="label">Success Rate</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Batch Information -->
|
|
{% if import_result.success %}
|
|
<div class="batch-info">
|
|
<h3>
|
|
<i class="fas fa-info-circle"></i>
|
|
Import Details
|
|
</h3>
|
|
<div class="batch-details">
|
|
<div class="detail-item">
|
|
<span class="detail-label">Batch ID:</span>
|
|
<code class="batch-id">{{ import_result.batch_id }}</code>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span class="detail-label">Import Date:</span>
|
|
<span class="detail-value">{{ import_result.import_date.strftime('%Y-%m-%d %H:%M:%S') }}</span>
|
|
</div>
|
|
<div class="detail-item">
|
|
<span class="detail-label">Processing Time:</span>
|
|
<span class="detail-value">{{ "%.2f"|format((import_result.import_date - import_result.import_date).total_seconds()) }} seconds</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Error Details -->
|
|
{% if import_result.errors %}
|
|
<div class="error-section">
|
|
<h3>
|
|
<i class="fas fa-exclamation-triangle"></i>
|
|
Error Details
|
|
</h3>
|
|
<div class="error-list">
|
|
{% for error in import_result.errors %}
|
|
<div class="error-item">
|
|
<i class="fas fa-times-circle"></i>
|
|
<span>{{ error }}</span>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Success Actions -->
|
|
{% if import_result.success %}
|
|
<div class="success-actions">
|
|
<h3>
|
|
<i class="fas fa-rocket"></i>
|
|
What's Next?
|
|
</h3>
|
|
<div class="action-grid">
|
|
<a href="{{ url_for('time_attendance.time_attendance_records', import_batch=import_result.batch_id) }}"
|
|
class="action-card">
|
|
<div class="action-icon">
|
|
<i class="fas fa-eye"></i>
|
|
</div>
|
|
<div class="action-content">
|
|
<h4>View Imported Records</h4>
|
|
<p>Review all {{ import_result.imported_records }} imported attendance records</p>
|
|
</div>
|
|
</a>
|
|
|
|
<a href="{{ url_for('time_attendance.time_attendance_records') }}"
|
|
class="action-card">
|
|
<div class="action-icon">
|
|
<i class="fas fa-search"></i>
|
|
</div>
|
|
<div class="action-content">
|
|
<h4>Search & Filter</h4>
|
|
<p>Use advanced filters to find specific records</p>
|
|
</div>
|
|
</a>
|
|
|
|
<a href="{{ url_for('time_attendance.import_time_attendance') }}"
|
|
class="action-card">
|
|
<div class="action-icon">
|
|
<i class="fas fa-plus"></i>
|
|
</div>
|
|
<div class="action-content">
|
|
<h4>Import More Data</h4>
|
|
<p>Upload additional Excel files with attendance data</p>
|
|
</div>
|
|
</a>
|
|
|
|
<a href="#" class="action-card" onclick="generateReport()">
|
|
<div class="action-icon">
|
|
<i class="fas fa-chart-line"></i>
|
|
</div>
|
|
<div class="action-content">
|
|
<h4>Generate Reports</h4>
|
|
<p>Create attendance reports and analytics</p>
|
|
</div>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Retry Actions for Failed Imports -->
|
|
{% if not import_result.success %}
|
|
<div class="retry-actions">
|
|
<h3>
|
|
<i class="fas fa-redo"></i>
|
|
Try Again
|
|
</h3>
|
|
<p>Here are some suggestions to resolve the import issues:</p>
|
|
<div class="suggestion-list">
|
|
<div class="suggestion-item">
|
|
<i class="fas fa-check"></i>
|
|
<span>Ensure your Excel file has all required columns: ID, Name, Date, Time, Location Name, Action Description</span>
|
|
</div>
|
|
<div class="suggestion-item">
|
|
<i class="fas fa-calendar"></i>
|
|
<span>Check that dates are in YYYY-MM-DD format and times are in HH:MM:SS format</span>
|
|
</div>
|
|
<div class="suggestion-item">
|
|
<i class="fas fa-file-excel"></i>
|
|
<span>Verify that your file is a valid Excel format (.xlsx or .xls)</span>
|
|
</div>
|
|
<div class="suggestion-item">
|
|
<i class="fas fa-database"></i>
|
|
<span>Make sure employee IDs and location names are properly formatted</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="retry-buttons">
|
|
<a href="{{ url_for('time_attendance.import_time_attendance') }}" class="btn btn-primary">
|
|
<i class="fas fa-upload"></i>
|
|
Try Import Again
|
|
</a>
|
|
<a href="#" class="btn btn-secondary" onclick="downloadTemplate()">
|
|
<i class="fas fa-download"></i>
|
|
Download Template
|
|
</a>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Import Statistics -->
|
|
{% if import_result.success and import_result.imported_records > 0 %}
|
|
<div class="statistics-section">
|
|
<div class="section-header">
|
|
<h2>
|
|
<i class="fas fa-chart-pie"></i>
|
|
Import Statistics
|
|
</h2>
|
|
</div>
|
|
<div class="section-body">
|
|
<div class="stats-grid">
|
|
<div class="stat-card">
|
|
<div class="stat-header">
|
|
<h4>Records by Action</h4>
|
|
</div>
|
|
<div class="stat-content">
|
|
<div class="progress-item">
|
|
<span class="progress-label">Check In</span>
|
|
<div class="progress-bar">
|
|
<div class="progress-fill" style="width: 75%"></div>
|
|
</div>
|
|
<span class="progress-value">75%</span>
|
|
</div>
|
|
<div class="progress-item">
|
|
<span class="progress-label">Check Out</span>
|
|
<div class="progress-bar">
|
|
<div class="progress-fill" style="width: 25%"></div>
|
|
</div>
|
|
<span class="progress-value">25%</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="stat-card">
|
|
<div class="stat-header">
|
|
<h4>Processing Speed</h4>
|
|
</div>
|
|
<div class="stat-content">
|
|
<div class="speed-metric">
|
|
<div class="metric-value">{{ import_result.imported_records }}</div>
|
|
<div class="metric-label">records/minute</div>
|
|
</div>
|
|
<div class="speed-info">
|
|
Fast and efficient processing of your attendance data
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<style>
|
|
.text-success {
|
|
color: #10b981;
|
|
}
|
|
|
|
.text-danger {
|
|
color: #ef4444;
|
|
}
|
|
|
|
.results-summary {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
gap: 1.5rem;
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
.summary-item {
|
|
background: #ffffff;
|
|
border: 1px solid #e2e8f0;
|
|
border-radius: 0.75rem;
|
|
padding: 1.5rem;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 1rem;
|
|
transition: all 0.2s ease-in-out;
|
|
}
|
|
|
|
.summary-item:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 10px 25px 0 rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.summary-item.success {
|
|
border-color: #10b981;
|
|
background: linear-gradient(135deg, #dcfce7, #f0fdf4);
|
|
}
|
|
|
|
.summary-item.error {
|
|
border-color: #ef4444;
|
|
background: linear-gradient(135deg, #fee2e2, #fef2f2);
|
|
}
|
|
|
|
.summary-icon {
|
|
width: 50px;
|
|
height: 50px;
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 1.5rem;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.summary-item .summary-icon {
|
|
background: linear-gradient(135deg, #8b5cf6, #7c3aed);
|
|
color: #ffffff;
|
|
}
|
|
|
|
.summary-item.success .summary-icon {
|
|
background: linear-gradient(135deg, #10b981, #059669);
|
|
color: #ffffff;
|
|
}
|
|
|
|
.summary-item.error .summary-icon {
|
|
background: linear-gradient(135deg, #ef4444, #dc2626);
|
|
color: #ffffff;
|
|
}
|
|
|
|
.summary-content .number {
|
|
font-size: 2rem;
|
|
font-weight: 700;
|
|
color: #0f172a;
|
|
margin-bottom: 0.25rem;
|
|
}
|
|
|
|
.summary-content .label {
|
|
color: #64748b;
|
|
font-size: 0.875rem;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.batch-info {
|
|
background: #f8fafc;
|
|
border: 1px solid #e2e8f0;
|
|
border-radius: 0.75rem;
|
|
padding: 1.5rem;
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
.batch-info h3 {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
font-size: 1.125rem;
|
|
font-weight: 600;
|
|
color: #0f172a;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.batch-details {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
|
gap: 1rem;
|
|
}
|
|
|
|
.detail-item {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 0.75rem;
|
|
background: #ffffff;
|
|
border-radius: 0.5rem;
|
|
border: 1px solid #e2e8f0;
|
|
}
|
|
|
|
.detail-label {
|
|
font-weight: 600;
|
|
color: #374151;
|
|
font-size: 0.875rem;
|
|
}
|
|
|
|
.detail-value {
|
|
font-family: monospace;
|
|
color: #475569;
|
|
font-size: 0.875rem;
|
|
}
|
|
|
|
.batch-id {
|
|
background: #f1f5f9;
|
|
padding: 0.5rem 0.75rem;
|
|
border-radius: 0.375rem;
|
|
font-family: monospace;
|
|
font-size: 0.875rem;
|
|
color: #475569;
|
|
border: 1px solid #cbd5e1;
|
|
}
|
|
|
|
.error-section {
|
|
background: #fef2f2;
|
|
border: 1px solid #fecaca;
|
|
border-radius: 0.75rem;
|
|
padding: 1.5rem;
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
.error-section h3 {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
color: #991b1b;
|
|
font-size: 1.125rem;
|
|
font-weight: 600;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.error-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.75rem;
|
|
}
|
|
|
|
.error-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.75rem;
|
|
padding: 0.75rem;
|
|
background: #ffffff;
|
|
border: 1px solid #fecaca;
|
|
border-radius: 0.5rem;
|
|
color: #dc2626;
|
|
font-size: 0.875rem;
|
|
}
|
|
|
|
.error-item i {
|
|
color: #ef4444;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.success-actions {
|
|
background: #f0fdf4;
|
|
border: 1px solid #bbf7d0;
|
|
border-radius: 0.75rem;
|
|
padding: 1.5rem;
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
.success-actions h3 {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
color: #166534;
|
|
font-size: 1.125rem;
|
|
font-weight: 600;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.action-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
|
gap: 1rem;
|
|
}
|
|
|
|
.action-card {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 1rem;
|
|
padding: 1rem;
|
|
background: #ffffff;
|
|
border: 1px solid #bbf7d0;
|
|
border-radius: 0.5rem;
|
|
text-decoration: none;
|
|
color: inherit;
|
|
transition: all 0.2s ease-in-out;
|
|
}
|
|
|
|
.action-card:hover {
|
|
background: #f8fafc;
|
|
border-color: #8b5cf6;
|
|
transform: translateY(-1px);
|
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
|
text-decoration: none;
|
|
color: inherit;
|
|
}
|
|
|
|
.action-icon {
|
|
width: 40px;
|
|
height: 40px;
|
|
background: linear-gradient(135deg, #10b981, #059669);
|
|
border-radius: 0.5rem;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: #ffffff;
|
|
font-size: 1rem;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.action-content h4 {
|
|
font-size: 0.875rem;
|
|
font-weight: 600;
|
|
color: #0f172a;
|
|
margin-bottom: 0.25rem;
|
|
}
|
|
|
|
.action-content p {
|
|
color: #64748b;
|
|
font-size: 0.75rem;
|
|
margin: 0;
|
|
line-height: 1.4;
|
|
}
|
|
|
|
.retry-actions {
|
|
background: #fffbeb;
|
|
border: 1px solid #fed7aa;
|
|
border-radius: 0.75rem;
|
|
padding: 1.5rem;
|
|
}
|
|
|
|
.retry-actions h3 {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
color: #92400e;
|
|
font-size: 1.125rem;
|
|
font-weight: 600;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.retry-actions p {
|
|
color: #92400e;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.suggestion-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.75rem;
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
|
|
.suggestion-item {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: 0.75rem;
|
|
padding: 0.75rem;
|
|
background: #ffffff;
|
|
border: 1px solid #fed7aa;
|
|
border-radius: 0.5rem;
|
|
font-size: 0.875rem;
|
|
}
|
|
|
|
.suggestion-item i {
|
|
color: #f59e0b;
|
|
margin-top: 0.125rem;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.retry-buttons {
|
|
display: flex;
|
|
gap: 1rem;
|
|
justify-content: center;
|
|
}
|
|
|
|
.statistics-section {
|
|
background: #ffffff;
|
|
border-radius: 0.75rem;
|
|
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.section-header {
|
|
background: #f8fafc;
|
|
padding: 1.5rem;
|
|
border-bottom: 1px solid #e2e8f0;
|
|
position: relative;
|
|
}
|
|
|
|
.section-header::before {
|
|
content: "";
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
height: 3px;
|
|
background: linear-gradient(90deg, #8b5cf6, #7c3aed);
|
|
}
|
|
|
|
.section-header h2 {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.75rem;
|
|
font-size: 1.25rem;
|
|
font-weight: 600;
|
|
color: #0f172a;
|
|
margin: 0;
|
|
}
|
|
|
|
.section-header h2 i {
|
|
color: #8b5cf6;
|
|
}
|
|
|
|
.section-body {
|
|
padding: 2rem;
|
|
}
|
|
|
|
.stats-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
gap: 1.5rem;
|
|
}
|
|
|
|
.stat-card {
|
|
background: #f8fafc;
|
|
border: 1px solid #e2e8f0;
|
|
border-radius: 0.75rem;
|
|
padding: 1.5rem;
|
|
}
|
|
|
|
.stat-header h4 {
|
|
font-size: 1rem;
|
|
font-weight: 600;
|
|
color: #0f172a;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.progress-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 1rem;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.progress-item:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.progress-label {
|
|
min-width: 80px;
|
|
font-size: 0.875rem;
|
|
color: #374151;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.progress-bar {
|
|
flex: 1;
|
|
height: 8px;
|
|
background: #e2e8f0;
|
|
border-radius: 4px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.progress-fill {
|
|
height: 100%;
|
|
background: linear-gradient(90deg, #8b5cf6, #7c3aed);
|
|
border-radius: 4px;
|
|
transition: width 0.3s ease-in-out;
|
|
}
|
|
|
|
.progress-value {
|
|
min-width: 40px;
|
|
text-align: right;
|
|
font-size: 0.875rem;
|
|
font-weight: 600;
|
|
color: #0f172a;
|
|
}
|
|
|
|
.speed-metric {
|
|
text-align: center;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.metric-value {
|
|
font-size: 2.5rem;
|
|
font-weight: 700;
|
|
color: #8b5cf6;
|
|
margin-bottom: 0.25rem;
|
|
}
|
|
|
|
.metric-label {
|
|
color: #64748b;
|
|
font-size: 0.875rem;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.speed-info {
|
|
text-align: center;
|
|
color: #64748b;
|
|
font-size: 0.875rem;
|
|
line-height: 1.4;
|
|
}
|
|
|
|
/* Responsive Design */
|
|
@media (max-width: 768px) {
|
|
.time-attendance-header {
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
gap: 1rem;
|
|
text-align: center;
|
|
}
|
|
|
|
.header-navigation {
|
|
text-align: left;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.header-actions {
|
|
justify-content: center;
|
|
}
|
|
|
|
.results-summary {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.batch-details {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.action-grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.retry-buttons {
|
|
flex-direction: column;
|
|
}
|
|
|
|
.stats-grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 480px) {
|
|
.time-attendance-page {
|
|
padding: 1rem;
|
|
}
|
|
|
|
.time-attendance-header {
|
|
padding: 1rem;
|
|
}
|
|
|
|
.header-content h1 {
|
|
font-size: 1.5rem;
|
|
}
|
|
|
|
.summary-item {
|
|
padding: 1rem;
|
|
}
|
|
|
|
.summary-icon {
|
|
width: 40px;
|
|
height: 40px;
|
|
font-size: 1.25rem;
|
|
}
|
|
|
|
.summary-content .number {
|
|
font-size: 1.5rem;
|
|
}
|
|
|
|
.action-card {
|
|
flex-direction: column;
|
|
text-align: center;
|
|
}
|
|
|
|
.action-icon {
|
|
width: 50px;
|
|
height: 50px;
|
|
font-size: 1.25rem;
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<script>
|
|
// Generate report functionality
|
|
function generateReport() {
|
|
// This would typically redirect to a reporting interface
|
|
alert('Report generation feature coming soon!');
|
|
}
|
|
|
|
// Download template functionality
|
|
function downloadTemplate() {
|
|
// This would typically generate and download an Excel template
|
|
alert('Template download feature coming soon!');
|
|
}
|
|
|
|
// Animate progress bars on page load
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const progressBars = document.querySelectorAll('.progress-fill');
|
|
progressBars.forEach(bar => {
|
|
const width = bar.style.width;
|
|
bar.style.width = '0%';
|
|
setTimeout(() => {
|
|
bar.style.width = width;
|
|
}, 500);
|
|
});
|
|
|
|
// Animate metric values
|
|
const metricValues = document.querySelectorAll('.metric-value');
|
|
metricValues.forEach(metric => {
|
|
const finalValue = parseInt(metric.textContent);
|
|
let currentValue = 0;
|
|
const increment = Math.ceil(finalValue / 30);
|
|
|
|
const counter = setInterval(() => {
|
|
currentValue += increment;
|
|
if (currentValue >= finalValue) {
|
|
currentValue = finalValue;
|
|
clearInterval(counter);
|
|
}
|
|
metric.textContent = currentValue;
|
|
}, 50);
|
|
});
|
|
});
|
|
</script>
|
|
{% endblock %} |