718 lines
30 KiB
HTML
718 lines
30 KiB
HTML
{% extends 'base.html' %}
|
|
{% load static %}
|
|
|
|
{% block title %}{{ metric.name }} - Metric Definition{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="content">
|
|
<div class="container-fluid">
|
|
<!-- Page Header -->
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="page-header">
|
|
<div class="page-title">
|
|
<h4>{{ metric.name }}</h4>
|
|
<h6>Metric definition details and configuration</h6>
|
|
</div>
|
|
<div class="page-btn">
|
|
<a href="{% url 'analytics:metric_definition_list' %}" class="btn btn-secondary me-2">
|
|
<i class="fas fa-arrow-left me-1"></i>Back to Metrics
|
|
</a>
|
|
<a href="{% url 'analytics:metric_definition_update' metric.pk %}" class="btn btn-primary">
|
|
<i class="fas fa-edit me-1"></i>Edit Metric
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<!-- Main Content -->
|
|
<div class="col-lg-8">
|
|
<!-- Metric Overview -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title">
|
|
<i class="fas fa-chart-line me-2"></i>Metric Overview
|
|
</h5>
|
|
<div class="card-tools">
|
|
<button type="button" class="btn btn-outline-primary btn-sm" onclick="calculateMetric()">
|
|
<i class="fas fa-calculator me-1"></i>Calculate Now
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-8">
|
|
<div class="metric-info">
|
|
<h4 class="text-primary">{{ metric.name }}</h4>
|
|
<p class="text-muted mb-3">{{ metric.description|default:"No description provided" }}</p>
|
|
|
|
<div class="row">
|
|
<div class="col-sm-6">
|
|
<div class="info-item">
|
|
<label class="form-label">Category:</label>
|
|
<span class="badge bg-primary">{{ metric.get_category_display }}</span>
|
|
</div>
|
|
</div>
|
|
<div class="col-sm-6">
|
|
<div class="info-item">
|
|
<label class="form-label">Type:</label>
|
|
<span class="badge bg-info">{{ metric.get_metric_type_display }}</span>
|
|
</div>
|
|
</div>
|
|
<div class="col-sm-6">
|
|
<div class="info-item">
|
|
<label class="form-label">Unit:</label>
|
|
<span class="text-muted">{{ metric.unit|default:"N/A" }}</span>
|
|
</div>
|
|
</div>
|
|
<div class="col-sm-6">
|
|
<div class="info-item">
|
|
<label class="form-label">Format:</label>
|
|
<span class="text-muted">{{ metric.format|default:"Default" }}</span>
|
|
</div>
|
|
</div>
|
|
<div class="col-sm-6">
|
|
<div class="info-item">
|
|
<label class="form-label">Data Source:</label>
|
|
<span class="text-muted">
|
|
{% if metric.data_source %}
|
|
<a href="{% url 'analytics:data_source_detail' metric.data_source.pk %}">
|
|
{{ metric.data_source.name }}
|
|
</a>
|
|
{% else %}
|
|
Not specified
|
|
{% endif %}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div class="col-sm-6">
|
|
<div class="info-item">
|
|
<label class="form-label">Calculation Frequency:</label>
|
|
<span class="text-muted">{{ metric.get_calculation_frequency_display|default:"Manual" }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="metric-status-card">
|
|
<div class="status-header">
|
|
<div class="status-icon">
|
|
{% if metric.status == 'active' %}
|
|
<i class="fas fa-check-circle fa-2x text-success"></i>
|
|
{% elif metric.status == 'inactive' %}
|
|
<i class="fas fa-pause-circle fa-2x text-secondary"></i>
|
|
{% else %}
|
|
<i class="fas fa-edit fa-2x text-warning"></i>
|
|
{% endif %}
|
|
</div>
|
|
<h6>
|
|
{% if metric.status == 'active' %}
|
|
Active
|
|
{% elif metric.status == 'inactive' %}
|
|
Inactive
|
|
{% else %}
|
|
Draft
|
|
{% endif %}
|
|
</h6>
|
|
</div>
|
|
<div class="status-stats">
|
|
<div class="stat-item">
|
|
<span class="stat-value">{{ metric.calculation_count|default:"0" }}</span>
|
|
<span class="stat-label">Calculations</span>
|
|
</div>
|
|
<div class="stat-item">
|
|
<span class="stat-value">{{ metric.widget_count|default:"0" }}</span>
|
|
<span class="stat-label">Widgets</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Formula and Configuration -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title">
|
|
<i class="fas fa-code me-2"></i>Formula & Configuration
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="formula-section">
|
|
<h6>Calculation Formula:</h6>
|
|
{% if metric.formula %}
|
|
<div class="formula-display">
|
|
<pre class="bg-light p-3 rounded"><code>{{ metric.formula }}</code></pre>
|
|
</div>
|
|
{% else %}
|
|
<div class="alert alert-warning">
|
|
<i class="fas fa-exclamation-triangle me-2"></i>
|
|
No formula defined for this metric.
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
{% if metric.sql_query %}
|
|
<div class="sql-section mt-4">
|
|
<h6>SQL Query:</h6>
|
|
<div class="sql-display">
|
|
<pre class="bg-dark text-light p-3 rounded"><code>{{ metric.sql_query }}</code></pre>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if metric.parameters %}
|
|
<div class="parameters-section mt-4">
|
|
<h6>Parameters:</h6>
|
|
<div class="table-responsive">
|
|
<table class="table table-sm table-bordered">
|
|
<thead>
|
|
<tr>
|
|
<th>Parameter</th>
|
|
<th>Type</th>
|
|
<th>Default Value</th>
|
|
<th>Description</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for param in metric.parameters %}
|
|
<tr>
|
|
<td><code>{{ param.name }}</code></td>
|
|
<td><span class="badge bg-secondary">{{ param.type }}</span></td>
|
|
<td>{{ param.default_value|default:"N/A" }}</td>
|
|
<td>{{ param.description|default:"No description" }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Current Value and Trends -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title">
|
|
<i class="fas fa-chart-area me-2"></i>Current Value & Trends
|
|
</h5>
|
|
<div class="card-tools">
|
|
<div class="btn-group" role="group">
|
|
<button type="button" class="btn btn-outline-secondary btn-sm active" onclick="changePeriod('7d')">7D</button>
|
|
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="changePeriod('30d')">30D</button>
|
|
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="changePeriod('90d')">90D</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-4">
|
|
<div class="current-value-card">
|
|
<div class="value-header">
|
|
<h6 class="text-muted">Current Value</h6>
|
|
</div>
|
|
<div class="value-display">
|
|
<h2 class="text-primary">{{ metric.current_value|default:"N/A" }}</h2>
|
|
{% if metric.unit %}
|
|
<span class="unit">{{ metric.unit }}</span>
|
|
{% endif %}
|
|
</div>
|
|
<div class="value-change">
|
|
{% if metric.change_percentage %}
|
|
<span class="change-indicator {% if metric.change_percentage > 0 %}text-success{% else %}text-danger{% endif %}">
|
|
<i class="fas fa-arrow-{% if metric.change_percentage > 0 %}up{% else %}down{% endif %} me-1"></i>
|
|
{{ metric.change_percentage|floatformat:1 }}%
|
|
</span>
|
|
<span class="text-muted">vs last period</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-8">
|
|
<div class="trend-chart">
|
|
<canvas id="trendChart" width="400" height="200"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{% if metric.last_calculated %}
|
|
<div class="calculation-info mt-3">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<small class="text-muted">
|
|
<i class="fas fa-clock me-1"></i>
|
|
Last calculated: {{ metric.last_calculated|timesince }} ago
|
|
</small>
|
|
</div>
|
|
<div class="col-md-6 text-end">
|
|
<small class="text-muted">
|
|
<i class="fas fa-stopwatch me-1"></i>
|
|
Calculation time: {{ metric.calculation_duration|default:"N/A" }}
|
|
</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Calculation History -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title">
|
|
<i class="fas fa-history me-2"></i>Calculation History
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
{% if metric.calculation_history %}
|
|
<div class="table-responsive">
|
|
<table class="table table-striped table-sm">
|
|
<thead>
|
|
<tr>
|
|
<th>Date</th>
|
|
<th>Value</th>
|
|
<th>Status</th>
|
|
<th>Duration</th>
|
|
<th>Triggered By</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for calculation in metric.calculation_history %}
|
|
<tr>
|
|
<td>{{ calculation.calculated_at|date:"M d, Y H:i" }}</td>
|
|
<td>
|
|
<strong>{{ calculation.value }}</strong>
|
|
{% if metric.unit %}<span class="text-muted"> {{ metric.unit }}</span>{% endif %}
|
|
</td>
|
|
<td>
|
|
{% if calculation.status == 'success' %}
|
|
<span class="badge bg-success">Success</span>
|
|
{% elif calculation.status == 'error' %}
|
|
<span class="badge bg-danger">Error</span>
|
|
{% else %}
|
|
<span class="badge bg-warning">Pending</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>{{ calculation.duration|default:"N/A" }}</td>
|
|
<td>{{ calculation.triggered_by|default:"System" }}</td>
|
|
<td>
|
|
<button type="button" class="btn btn-outline-secondary btn-sm"
|
|
onclick="viewCalculationDetails('{{ calculation.id }}')">
|
|
<i class="fas fa-eye"></i>
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% else %}
|
|
<div class="text-center py-4">
|
|
<i class="fas fa-history fa-3x text-muted mb-3"></i>
|
|
<h6 class="text-muted">No Calculation History</h6>
|
|
<p class="text-muted">Calculation history will appear here after the first calculation.</p>
|
|
<button type="button" class="btn btn-primary" onclick="calculateMetric()">
|
|
<i class="fas fa-calculator me-1"></i>Calculate Now
|
|
</button>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Sidebar -->
|
|
<div class="col-lg-4">
|
|
<!-- Quick Actions -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title">
|
|
<i class="fas fa-tools me-2"></i>Quick Actions
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="d-grid gap-2">
|
|
<button type="button" class="btn btn-primary" onclick="calculateMetric()">
|
|
<i class="fas fa-calculator me-1"></i>Calculate Now
|
|
</button>
|
|
<a href="{% url 'analytics:metric_definition_update' metric.pk %}" class="btn btn-outline-primary">
|
|
<i class="fas fa-edit me-1"></i>Edit Metric
|
|
</a>
|
|
<button type="button" class="btn btn-outline-secondary" onclick="duplicateMetric()">
|
|
<i class="fas fa-copy me-1"></i>Duplicate
|
|
</button>
|
|
<button type="button" class="btn btn-outline-info" onclick="exportData()">
|
|
<i class="fas fa-download me-1"></i>Export Data
|
|
</button>
|
|
<hr>
|
|
{% if metric.status == 'active' %}
|
|
<button type="button" class="btn btn-outline-warning" onclick="toggleStatus('inactive')">
|
|
<i class="fas fa-pause me-1"></i>Deactivate
|
|
</button>
|
|
{% else %}
|
|
<button type="button" class="btn btn-outline-success" onclick="toggleStatus('active')">
|
|
<i class="fas fa-play me-1"></i>Activate
|
|
</button>
|
|
{% endif %}
|
|
<a href="{% url 'analytics:metric_definition_delete' metric.pk %}" class="btn btn-outline-danger">
|
|
<i class="fas fa-trash me-1"></i>Delete
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Metric Information -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title">
|
|
<i class="fas fa-info-circle me-2"></i>Metric Information
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="metric-details">
|
|
<div class="detail-item">
|
|
<label class="form-label">Created By:</label>
|
|
<span class="detail-value">{{ metric.created_by|default:"System" }}</span>
|
|
</div>
|
|
|
|
<div class="detail-item">
|
|
<label class="form-label">Created Date:</label>
|
|
<span class="detail-value">{{ metric.created_at|date:"M d, Y H:i" }}</span>
|
|
</div>
|
|
|
|
<div class="detail-item">
|
|
<label class="form-label">Last Updated:</label>
|
|
<span class="detail-value">{{ metric.updated_at|date:"M d, Y H:i" }}</span>
|
|
</div>
|
|
|
|
<div class="detail-item">
|
|
<label class="form-label">Version:</label>
|
|
<span class="detail-value">{{ metric.version|default:"1.0" }}</span>
|
|
</div>
|
|
|
|
{% if metric.tags %}
|
|
<div class="detail-item">
|
|
<label class="form-label">Tags:</label>
|
|
<div class="tags-container">
|
|
{% for tag in metric.tags %}
|
|
<span class="badge bg-secondary me-1">{{ tag }}</span>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Related Widgets -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title">
|
|
<i class="fas fa-chart-bar me-2"></i>Related Widgets
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
{% if metric.related_widgets %}
|
|
<div class="widgets-list">
|
|
{% for widget in metric.related_widgets %}
|
|
<div class="widget-item">
|
|
<div class="widget-info">
|
|
<h6 class="mb-1">{{ widget.name }}</h6>
|
|
<p class="text-muted mb-0 small">{{ widget.dashboard.name }}</p>
|
|
</div>
|
|
<a href="{% url 'analytics:dashboard_widget_detail' widget.pk %}" class="btn btn-outline-primary btn-sm">
|
|
<i class="fas fa-eye"></i>
|
|
</a>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% else %}
|
|
<div class="text-center py-3">
|
|
<i class="fas fa-chart-bar fa-2x text-muted mb-2"></i>
|
|
<p class="text-muted mb-0">No widgets using this metric</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Performance Stats -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title">
|
|
<i class="fas fa-tachometer-alt me-2"></i>Performance
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="performance-stats">
|
|
<div class="stat-item">
|
|
<div class="stat-value">{{ metric.avg_calculation_time|default:"N/A" }}</div>
|
|
<div class="stat-label">Avg Calculation Time</div>
|
|
</div>
|
|
<div class="stat-item">
|
|
<div class="stat-value">{{ metric.success_rate|default:"N/A" }}%</div>
|
|
<div class="stat-label">Success Rate</div>
|
|
</div>
|
|
<div class="stat-item">
|
|
<div class="stat-value">{{ metric.data_freshness|default:"N/A" }}</div>
|
|
<div class="stat-label">Data Freshness</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
function calculateMetric() {
|
|
if (confirm('Calculate this metric now? This may take some time.')) {
|
|
const btn = event.target;
|
|
const originalText = btn.innerHTML;
|
|
|
|
btn.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i>Calculating...';
|
|
btn.disabled = true;
|
|
|
|
setTimeout(() => {
|
|
btn.innerHTML = originalText;
|
|
btn.disabled = false;
|
|
alert('Metric calculated successfully!');
|
|
window.location.reload();
|
|
}, 3000);
|
|
}
|
|
}
|
|
|
|
function duplicateMetric() {
|
|
if (confirm('Create a copy of this metric?')) {
|
|
alert('Metric duplicated successfully!');
|
|
}
|
|
}
|
|
|
|
function exportData() {
|
|
alert('Exporting metric data...');
|
|
}
|
|
|
|
function toggleStatus(newStatus) {
|
|
const action = newStatus === 'active' ? 'activate' : 'deactivate';
|
|
if (confirm(`Are you sure you want to ${action} this metric?`)) {
|
|
alert(`Metric ${action}d successfully!`);
|
|
window.location.reload();
|
|
}
|
|
}
|
|
|
|
function changePeriod(period) {
|
|
const buttons = document.querySelectorAll('.btn-group button');
|
|
buttons.forEach(btn => btn.classList.remove('active'));
|
|
event.target.classList.add('active');
|
|
|
|
// Update chart with new period data
|
|
updateTrendChart(period);
|
|
}
|
|
|
|
function updateTrendChart(period) {
|
|
// Placeholder for chart update logic
|
|
console.log('Updating chart for period:', period);
|
|
}
|
|
|
|
function viewCalculationDetails(calculationId) {
|
|
alert(`Viewing details for calculation: ${calculationId}`);
|
|
}
|
|
|
|
// Initialize trend chart
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const ctx = document.getElementById('trendChart');
|
|
if (ctx) {
|
|
// Placeholder for Chart.js initialization
|
|
ctx.getContext('2d').fillText('Trend Chart Placeholder', 50, 100);
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<style>
|
|
.info-item {
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
.info-item label {
|
|
font-weight: 600;
|
|
margin-bottom: 5px;
|
|
display: block;
|
|
color: #2c3e50;
|
|
}
|
|
|
|
.metric-status-card {
|
|
border: 1px solid #e9ecef;
|
|
border-radius: 8px;
|
|
background: white;
|
|
overflow: hidden;
|
|
height: 100%;
|
|
}
|
|
|
|
.status-header {
|
|
background: #f8f9fa;
|
|
padding: 20px;
|
|
text-align: center;
|
|
border-bottom: 1px solid #e9ecef;
|
|
}
|
|
|
|
.status-icon {
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.status-header h6 {
|
|
margin: 0;
|
|
font-weight: 600;
|
|
color: #2c3e50;
|
|
}
|
|
|
|
.status-stats {
|
|
padding: 20px;
|
|
display: flex;
|
|
justify-content: space-around;
|
|
}
|
|
|
|
.stat-item {
|
|
text-align: center;
|
|
}
|
|
|
|
.stat-value {
|
|
display: block;
|
|
font-size: 1.5rem;
|
|
font-weight: 700;
|
|
color: #2c3e50;
|
|
margin-bottom: 5px;
|
|
}
|
|
|
|
.stat-label {
|
|
font-size: 0.875rem;
|
|
color: #6c757d;
|
|
}
|
|
|
|
.formula-display, .sql-display {
|
|
position: relative;
|
|
}
|
|
|
|
.formula-display pre, .sql-display pre {
|
|
margin: 0;
|
|
font-size: 0.875rem;
|
|
line-height: 1.4;
|
|
}
|
|
|
|
.current-value-card {
|
|
text-align: center;
|
|
padding: 20px;
|
|
background: #f8f9fa;
|
|
border-radius: 8px;
|
|
height: 100%;
|
|
}
|
|
|
|
.value-display h2 {
|
|
margin: 10px 0;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.unit {
|
|
font-size: 1rem;
|
|
color: #6c757d;
|
|
margin-left: 5px;
|
|
}
|
|
|
|
.change-indicator {
|
|
font-weight: 600;
|
|
margin-right: 10px;
|
|
}
|
|
|
|
.detail-item {
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
.detail-item label {
|
|
font-weight: 600;
|
|
margin-bottom: 5px;
|
|
display: block;
|
|
color: #2c3e50;
|
|
}
|
|
|
|
.detail-value {
|
|
color: #6c757d;
|
|
}
|
|
|
|
.tags-container {
|
|
margin-top: 5px;
|
|
}
|
|
|
|
.widget-item {
|
|
display: flex;
|
|
justify-content: between;
|
|
align-items: center;
|
|
padding: 10px 0;
|
|
border-bottom: 1px solid #e9ecef;
|
|
}
|
|
|
|
.widget-item:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.widget-info {
|
|
flex: 1;
|
|
}
|
|
|
|
.widget-info h6 {
|
|
margin: 0;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.performance-stats {
|
|
display: grid;
|
|
grid-template-columns: 1fr;
|
|
gap: 20px;
|
|
}
|
|
|
|
.performance-stats .stat-item {
|
|
padding: 15px;
|
|
background: #f8f9fa;
|
|
border-radius: 8px;
|
|
text-align: center;
|
|
}
|
|
|
|
.performance-stats .stat-value {
|
|
font-size: 1.25rem;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.page-btn {
|
|
margin-top: 15px;
|
|
}
|
|
|
|
.card-tools {
|
|
margin-top: 15px;
|
|
}
|
|
|
|
.current-value-card {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.status-stats {
|
|
flex-direction: column;
|
|
gap: 15px;
|
|
}
|
|
|
|
.widget-item {
|
|
flex-direction: column;
|
|
align-items: flex-start;
|
|
gap: 10px;
|
|
}
|
|
|
|
.performance-stats {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|