2025-08-12 13:33:25 +03:00

513 lines
21 KiB
HTML

{% extends "base.html" %}
{% load static %}
{% block title %}{{ object.name }} - Analytics Metrics{% endblock %}
{% block content %}
<!-- BEGIN breadcrumb -->
<ol class="breadcrumb float-xl-end">
<li class="breadcrumb-item"><a href="{% url 'core:dashboard' %}">Dashboard</a></li>
<li class="breadcrumb-item"><a href="{% url 'analytics:dashboard' %}">Analytics</a></li>
<li class="breadcrumb-item"><a href="{% url 'analytics:metric_list' %}">Metrics</a></li>
<li class="breadcrumb-item active">{{ object.name }}</li>
</ol>
<!-- END breadcrumb -->
<!-- BEGIN page-header -->
<h1 class="page-header">
{{ object.name }}
<small>{{ object.get_category_display }} Metric</small>
</h1>
<!-- END page-header -->
<div class="row">
<div class="col-xl-8">
<!-- BEGIN panel -->
<div class="panel panel-inverse">
<div class="panel-heading">
<h4 class="panel-title">Metric Information</h4>
<div class="panel-heading-btn">
<button class="btn btn-xs btn-success me-2" onclick="refreshMetric()">
<i class="fa fa-sync"></i> Refresh
</button>
<a href="{% url 'analytics:metric_update' object.pk %}" class="btn btn-xs btn-primary me-2">
<i class="fa fa-edit"></i> Edit
</a>
<a href="javascript:;" class="btn btn-xs btn-icon btn-default" data-toggle="panel-expand"><i class="fa fa-expand"></i></a>
</div>
</div>
<div class="panel-body">
<div class="row">
<div class="col-md-6">
<table class="table table-borderless">
<tr>
<td class="fw-bold" width="150">Name:</td>
<td>{{ object.name }}</td>
</tr>
<tr>
<td class="fw-bold">Description:</td>
<td>{{ object.description|default:"No description" }}</td>
</tr>
<tr>
<td class="fw-bold">Category:</td>
<td>
<span class="badge bg-primary">{{ object.get_category_display }}</span>
</td>
</tr>
<tr>
<td class="fw-bold">Current Value:</td>
<td>
{% if object.current_value is not None %}
<span class="fs-18px fw-bold text-primary">
{{ object.current_value }}{% if object.unit %} {{ object.unit }}{% endif %}
</span>
{% else %}
<span class="text-muted">No data available</span>
{% endif %}
</td>
</tr>
<tr>
<td class="fw-bold">Target:</td>
<td>
{% if object.target_value is not None %}
<span class="text-info">
{% if object.target_direction == 'HIGHER' %}↑{% else %}↓{% endif %}
{{ object.target_value }}{% if object.unit %} {{ object.unit }}{% endif %}
</span>
{% else %}
<span class="text-muted">No target set</span>
{% endif %}
</td>
</tr>
</table>
</div>
<div class="col-md-6">
<table class="table table-borderless">
<tr>
<td class="fw-bold" width="150">Status:</td>
<td>
<span class="badge bg-{% if object.status == 'ACTIVE' %}success{% elif object.status == 'DRAFT' %}warning{% else %}secondary{% endif %}">
{{ object.get_status_display }}
</span>
</td>
</tr>
<tr>
<td class="fw-bold">Frequency:</td>
<td>
<i class="fa fa-{% if object.update_frequency == 'REAL_TIME' %}bolt{% else %}clock{% endif %} me-2"></i>
{{ object.get_update_frequency_display }}
</td>
</tr>
<tr>
<td class="fw-bold">Last Updated:</td>
<td>
{% if object.last_updated %}
{{ object.last_updated|date:"M d, Y H:i" }}
<br><small class="text-muted">{{ object.last_updated|timesince }} ago</small>
{% else %}
<span class="text-muted">Never updated</span>
{% endif %}
</td>
</tr>
<tr>
<td class="fw-bold">Created:</td>
<td>{{ object.created_at|date:"M d, Y" }}</td>
</tr>
<tr>
<td class="fw-bold">Created By:</td>
<td>{{ object.created_by.get_full_name|default:"System" }}</td>
</tr>
</table>
</div>
</div>
<!-- Target Performance -->
{% if object.target_value is not None and object.current_value is not None %}
<div class="mt-4">
<h6>Target Performance</h6>
<div class="progress mb-2" style="height: 25px;">
{% with performance=object.target_performance %}
<div class="progress-bar bg-{% if performance >= 100 %}success{% elif performance >= 75 %}warning{% else %}danger{% endif %}"
role="progressbar" style="width: {{ performance|floatformat:1 }}%">
{{ performance|floatformat:1 }}%
</div>
{% endwith %}
</div>
<div class="d-flex justify-content-between text-muted small">
<span>Current: {{ object.current_value }}{% if object.unit %} {{ object.unit }}{% endif %}</span>
<span>Target: {{ object.target_value }}{% if object.unit %} {{ object.unit }}{% endif %}</span>
</div>
</div>
{% endif %}
<!-- SQL Query -->
{% if object.sql_query %}
<div class="mt-4">
<h6>Calculation Query</h6>
<div class="bg-dark p-3 rounded">
<pre class="mb-0 text-light"><code>{{ object.sql_query }}</code></pre>
</div>
</div>
{% endif %}
</div>
</div>
<!-- END panel -->
<!-- BEGIN panel -->
<div class="panel panel-inverse">
<div class="panel-heading">
<h4 class="panel-title">Historical Data</h4>
<div class="panel-heading-btn">
<div class="btn-group btn-group-xs">
<button class="btn btn-default" onclick="loadChart('7d')">7D</button>
<button class="btn btn-default" onclick="loadChart('30d')">30D</button>
<button class="btn btn-default active" onclick="loadChart('90d')">90D</button>
<button class="btn btn-default" onclick="loadChart('1y')">1Y</button>
</div>
</div>
</div>
<div class="panel-body">
<div id="metric-chart" style="height: 300px;"></div>
</div>
</div>
<!-- END panel -->
<!-- BEGIN panel -->
<div class="panel panel-inverse">
<div class="panel-heading">
<h4 class="panel-title">Recent Updates</h4>
</div>
<div class="panel-body">
{% if recent_updates %}
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>Date</th>
<th>Value</th>
<th>Change</th>
<th>Updated By</th>
</tr>
</thead>
<tbody>
{% for update in recent_updates %}
<tr>
<td>{{ update.created_at|date:"M d, Y H:i:s" }}</td>
<td>
<span class="fw-bold">
{{ update.value }}{% if object.unit %} {{ object.unit }}{% endif %}
</span>
</td>
<td>
{% if update.change %}
<span class="badge bg-{% if update.change > 0 %}success{% elif update.change < 0 %}danger{% else %}secondary{% endif %}">
{% if update.change > 0 %}+{% endif %}{{ update.change|floatformat:2 }}
</span>
{% else %}
<span class="text-muted">-</span>
{% endif %}
</td>
<td>{{ update.updated_by.get_full_name|default:"System" }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="text-center py-4 text-muted">
<i class="fa fa-history fa-3x mb-3"></i>
<p>No update history found for this metric.</p>
</div>
{% endif %}
</div>
</div>
<!-- END panel -->
</div>
<div class="col-xl-4">
<!-- BEGIN panel -->
<div class="panel panel-inverse">
<div class="panel-heading">
<h4 class="panel-title">Quick Actions</h4>
</div>
<div class="panel-body">
<div class="d-grid gap-2">
<button class="btn btn-success" onclick="refreshMetric()">
<i class="fa fa-sync me-2"></i>Refresh Metric
</button>
<a href="{% url 'analytics:metric_update' object.pk %}" class="btn btn-primary">
<i class="fa fa-edit me-2"></i>Edit Metric
</a>
<button class="btn btn-info" onclick="exportData()">
<i class="fa fa-download me-2"></i>Export Data
</button>
<button class="btn btn-warning" onclick="setAlert()">
<i class="fa fa-bell me-2"></i>Set Alert
</button>
<hr>
{% if object.status == 'ACTIVE' %}
<button class="btn btn-outline-secondary" onclick="deactivateMetric()">
<i class="fa fa-pause me-2"></i>Deactivate
</button>
{% else %}
<button class="btn btn-outline-success" onclick="activateMetric()">
<i class="fa fa-play me-2"></i>Activate
</button>
{% endif %}
<a href="{% url 'analytics:metric_delete' object.pk %}" class="btn btn-danger">
<i class="fa fa-trash me-2"></i>Delete Metric
</a>
</div>
</div>
</div>
<!-- END panel -->
<!-- BEGIN panel -->
<div class="panel panel-inverse">
<div class="panel-heading">
<h4 class="panel-title">Metric Statistics</h4>
</div>
<div class="panel-body">
<div class="row text-center">
<div class="col-6 mb-3">
<div class="fs-24px fw-bold text-primary">{{ object.update_count }}</div>
<div class="small text-muted">Total Updates</div>
</div>
<div class="col-6 mb-3">
<div class="fs-24px fw-bold text-success">{{ object.avg_value|floatformat:2 }}</div>
<div class="small text-muted">Average Value</div>
</div>
<div class="col-6 mb-3">
<div class="fs-24px fw-bold text-info">{{ object.min_value|floatformat:2 }}</div>
<div class="small text-muted">Minimum</div>
</div>
<div class="col-6 mb-3">
<div class="fs-24px fw-bold text-warning">{{ object.max_value|floatformat:2 }}</div>
<div class="small text-muted">Maximum</div>
</div>
</div>
</div>
</div>
<!-- END panel -->
<!-- BEGIN panel -->
<div class="panel panel-inverse">
<div class="panel-heading">
<h4 class="panel-title">Alert Configuration</h4>
</div>
<div class="panel-body">
{% if object.alerts.exists %}
<div class="list-group list-group-flush">
{% for alert in object.alerts.all %}
<div class="list-group-item d-flex justify-content-between align-items-center">
<div>
<div class="fw-bold">{{ alert.name }}</div>
<div class="small text-muted">
{{ alert.get_condition_display }}: {{ alert.threshold_value }}
</div>
</div>
<span class="badge bg-{{ alert.is_active|yesno:'success,secondary' }}">
{{ alert.is_active|yesno:'Active,Inactive' }}
</span>
</div>
{% endfor %}
</div>
{% else %}
<div class="text-center py-3 text-muted">
<i class="fa fa-bell fa-2x mb-2"></i>
<p class="mb-0">No alerts configured.</p>
<button class="btn btn-sm btn-outline-primary mt-2" onclick="setAlert()">
Add Alert
</button>
</div>
{% endif %}
</div>
</div>
<!-- END panel -->
</div>
</div>
{% endblock %}
{% block js %}
<script src="{% static 'assets/plugins/apexcharts/dist/apexcharts.min.js' %}"></script>
<script>
var chart;
$(document).ready(function() {
// Initialize chart
loadChart('90d');
});
function loadChart(period) {
$.ajax({
url: '{% url "analytics:metric_chart_data" object.pk %}',
data: { period: period },
success: function(response) {
if (chart) {
chart.destroy();
}
var options = {
series: [{
name: '{{ object.name }}',
data: response.data
}],
chart: {
type: 'line',
height: 300,
toolbar: {
show: true
}
},
dataLabels: {
enabled: false
},
stroke: {
curve: 'smooth',
width: 3
},
xaxis: {
type: 'datetime',
categories: response.labels
},
yaxis: {
title: {
text: '{{ object.unit|default:"Value" }}'
}
},
colors: ['#348fe2'],
grid: {
borderColor: '#e7e7e7',
row: {
colors: ['#f3f3f3', 'transparent'],
opacity: 0.5
}
},
markers: {
size: 4
},
tooltip: {
x: {
format: 'dd/MM/yy HH:mm'
}
}
};
{% if object.target_value %}
options.annotations = {
yaxis: [{
y: {{ object.target_value }},
borderColor: '#00E396',
label: {
borderColor: '#00E396',
style: {
color: '#fff',
background: '#00E396'
},
text: 'Target: {{ object.target_value }}'
}
}]
};
{% endif %}
chart = new ApexCharts(document.querySelector("#metric-chart"), options);
chart.render();
},
error: function() {
$('#metric-chart').html('<div class="text-center py-5 text-muted">Failed to load chart data</div>');
}
});
// Update active button
$('.btn-group-xs .btn').removeClass('active');
$('.btn-group-xs .btn').filter(function() {
return $(this).text() === period.toUpperCase();
}).addClass('active');
}
function refreshMetric() {
$.ajax({
url: '{% url "analytics:metric_refresh" object.pk %}',
method: 'POST',
data: {
'csrfmiddlewaretoken': '{{ csrf_token }}'
},
beforeSend: function() {
toastr.info('Refreshing metric...');
},
success: function(response) {
if (response.success) {
toastr.success('Metric refreshed successfully');
location.reload();
} else {
toastr.error('Failed to refresh metric: ' + (response.error || 'Unknown error'));
}
},
error: function() {
toastr.error('An error occurred while refreshing the metric');
}
});
}
function activateMetric() {
$.ajax({
url: '{% url "analytics:metric_activate" object.pk %}',
method: 'POST',
data: {
'csrfmiddlewaretoken': '{{ csrf_token }}'
},
success: function(response) {
if (response.success) {
toastr.success('Metric activated successfully');
location.reload();
} else {
toastr.error('Failed to activate metric');
}
},
error: function() {
toastr.error('An error occurred while activating the metric');
}
});
}
function deactivateMetric() {
if (confirm('Deactivate this metric? It will stop updating automatically.')) {
$.ajax({
url: '{% url "analytics:metric_deactivate" object.pk %}',
method: 'POST',
data: {
'csrfmiddlewaretoken': '{{ csrf_token }}'
},
success: function(response) {
if (response.success) {
toastr.success('Metric deactivated successfully');
location.reload();
} else {
toastr.error('Failed to deactivate metric');
}
},
error: function() {
toastr.error('An error occurred while deactivating the metric');
}
});
}
}
function exportData() {
window.location.href = '{% url "analytics:metric_export_data" object.pk %}';
}
function setAlert() {
// Implementation for setting alerts
toastr.info('Alert configuration functionality will be implemented');
}
</script>
{% endblock %}