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

588 lines
25 KiB
HTML

{% extends 'base.html' %}
{% load static %}
{% block title %}Analytics Dashboards - Hospital Management{% 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>Analytics Dashboards</h4>
<h6>Manage and view analytics dashboards</h6>
</div>
<div class="page-btn">
<a href="{% url 'analytics:dashboard_create' %}" class="btn btn-added">
<i class="fas fa-plus me-1"></i>Add Dashboard
</a>
</div>
</div>
</div>
</div>
<!-- Dashboard Statistics -->
<div class="row">
<div class="col-lg-3 col-sm-6 col-12">
<div class="dash-widget">
<div class="dash-widgetimg">
<span><i class="fas fa-tachometer-alt"></i></span>
</div>
<div class="dash-widgetcontent">
<h5 id="total-dashboards">{{ stats.total_dashboards|default:"0" }}</h5>
<h6>Total Dashboards</h6>
</div>
</div>
</div>
<div class="col-lg-3 col-sm-6 col-12">
<div class="dash-widget">
<div class="dash-widgetimg">
<span><i class="fas fa-chart-line"></i></span>
</div>
<div class="dash-widgetcontent">
<h5 id="active-dashboards">{{ stats.active_dashboards|default:"0" }}</h5>
<h6>Active Dashboards</h6>
</div>
</div>
</div>
<div class="col-lg-3 col-sm-6 col-12">
<div class="dash-widget">
<div class="dash-widgetimg">
<span><i class="fas fa-users"></i></span>
</div>
<div class="dash-widgetcontent">
<h5 id="shared-dashboards">{{ stats.shared_dashboards|default:"0" }}</h5>
<h6>Shared Dashboards</h6>
</div>
</div>
</div>
<div class="col-lg-3 col-sm-6 col-12">
<div class="dash-widget">
<div class="dash-widgetimg">
<span><i class="fas fa-th-large"></i></span>
</div>
<div class="dash-widgetcontent">
<h5 id="total-widgets">{{ stats.total_widgets|default:"0" }}</h5>
<h6>Total Widgets</h6>
</div>
</div>
</div>
</div>
<!-- Dashboard Grid -->
<div class="card">
<div class="card-body">
<div class="table-top">
<div class="search-set">
<div class="search-input">
<a class="btn btn-searchset">
<i class="fas fa-search"></i>
</a>
<input type="text" id="searchInput" placeholder="Search dashboards...">
</div>
</div>
<div class="wordset">
<ul>
<li>
<a data-bs-toggle="tooltip" data-bs-placement="top" title="Filter" onclick="toggleFilters()">
<i class="fas fa-filter"></i>
</a>
</li>
<li>
<a data-bs-toggle="tooltip" data-bs-placement="top" title="Grid View" onclick="toggleView('grid')" id="gridViewBtn" class="active">
<i class="fas fa-th"></i>
</a>
</li>
<li>
<a data-bs-toggle="tooltip" data-bs-placement="top" title="List View" onclick="toggleView('list')" id="listViewBtn">
<i class="fas fa-list"></i>
</a>
</li>
</ul>
</div>
</div>
<!-- Filters Panel -->
<div id="filtersPanel" class="filters-panel" style="display: none;">
<div class="row">
<div class="col-md-3">
<div class="form-group">
<label for="dashboardType" class="form-label">Dashboard Type</label>
<select class="form-select" id="dashboardType">
<option value="">All Types</option>
{% for choice in dashboard_type_choices %}
<option value="{{ choice.0 }}">{{ choice.1 }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="col-md-3">
<div class="form-group">
<label for="statusFilter" class="form-label">Status</label>
<select class="form-select" id="statusFilter">
<option value="">All Status</option>
{% for choice in status_choices %}
<option value="{{ choice.0 }}">{{ choice.1 }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="col-md-3">
<div class="form-group">
<label for="createdBy" class="form-label">Created By</label>
<select class="form-select" id="createdBy">
<option value="">All Users</option>
<option value="me">My Dashboards</option>
<option value="shared">Shared with Me</option>
</select>
</div>
</div>
<div class="col-md-3">
<div class="form-group">
<label class="form-label">&nbsp;</label>
<div class="d-flex gap-2">
<button type="button" class="btn btn-primary btn-sm" onclick="applyFilters()">
<i class="fas fa-check me-1"></i>Apply
</button>
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="clearFilters()">
<i class="fas fa-times me-1"></i>Clear
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Grid View -->
<div id="gridView" class="dashboard-grid">
<div class="row">
{% for dashboard in dashboards %}
<div class="col-lg-4 col-md-6 col-sm-12 mb-4">
<div class="dashboard-card">
<div class="dashboard-header">
<div class="dashboard-type">
<span class="badge bg-{{ dashboard.get_type_color }}">{{ dashboard.get_dashboard_type_display }}</span>
<div class="dashboard-actions">
<div class="dropdown">
<a href="#" class="dropdown-toggle" data-bs-toggle="dropdown">
<i class="fas fa-ellipsis-v"></i>
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="{% url 'analytics:dashboard_detail' dashboard.pk %}"><i class="fas fa-eye me-2"></i>View</a></li>
<li><a class="dropdown-item" href="{% url 'analytics:dashboard_update' dashboard.pk %}"><i class="fas fa-edit me-2"></i>Edit</a></li>
<li><a class="dropdown-item" href="#" onclick="duplicateDashboard({{ dashboard.pk }})"><i class="fas fa-copy me-2"></i>Duplicate</a></li>
<li><a class="dropdown-item" href="#" onclick="shareDashboard({{ dashboard.pk }})"><i class="fas fa-share me-2"></i>Share</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item text-danger" href="{% url 'analytics:dashboard_delete' dashboard.pk %}"><i class="fas fa-trash me-2"></i>Delete</a></li>
</ul>
</div>
</div>
</div>
<h5 class="dashboard-title">{{ dashboard.name }}</h5>
<p class="dashboard-description">{{ dashboard.description|default:"No description available"|truncatechars:100 }}</p>
</div>
<div class="dashboard-preview">
<div class="preview-widgets">
{% for widget in dashboard.widgets.all|slice:":4" %}
<div class="widget-mini">
<i class="fas fa-{{ widget.get_icon }} text-{{ widget.get_color }}"></i>
<span>{{ widget.title|truncatechars:15 }}</span>
</div>
{% empty %}
<div class="widget-mini">
<i class="fas fa-chart-line text-muted"></i>
<span>No widgets</span>
</div>
{% endfor %}
</div>
</div>
<div class="dashboard-footer">
<div class="dashboard-meta">
<span class="meta-item">
<i class="fas fa-th-large me-1"></i>
{{ dashboard.widgets.count }} widget{{ dashboard.widgets.count|pluralize }}
</span>
<span class="meta-item">
<i class="fas fa-user me-1"></i>
{{ dashboard.created_by.get_full_name|default:dashboard.created_by.username }}
</span>
<span class="meta-item">
<i class="fas fa-clock me-1"></i>
{{ dashboard.updated_at|timesince }} ago
</span>
</div>
<div class="dashboard-status">
{% if dashboard.is_active %}
<span class="badge bg-success">Active</span>
{% else %}
<span class="badge bg-secondary">Inactive</span>
{% endif %}
{% if dashboard.is_shared %}
<span class="badge bg-info">Shared</span>
{% endif %}
</div>
</div>
</div>
</div>
{% empty %}
<div class="col-12">
<div class="empty-state text-center py-5">
<i class="fas fa-tachometer-alt fa-3x text-muted mb-3"></i>
<h5 class="text-muted">No Dashboards Found</h5>
<p class="text-muted">Create your first dashboard to get started with analytics.</p>
<a href="{% url 'analytics:dashboard_create' %}" class="btn btn-primary">
<i class="fas fa-plus me-1"></i>Create Dashboard
</a>
</div>
</div>
{% endfor %}
</div>
</div>
<!-- List View -->
<div id="listView" class="dashboard-list" style="display: none;">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>Dashboard</th>
<th>Type</th>
<th>Widgets</th>
<th>Status</th>
<th>Created By</th>
<th>Last Updated</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for dashboard in dashboards %}
<tr>
<td>
<div>
<h6 class="mb-1">{{ dashboard.name }}</h6>
<small class="text-muted">{{ dashboard.description|default:"No description"|truncatechars:50 }}</small>
</div>
</td>
<td>
<span class="badge bg-{{ dashboard.get_type_color }}">{{ dashboard.get_dashboard_type_display }}</span>
</td>
<td>
<span class="badge bg-light text-dark">{{ dashboard.widgets.count }}</span>
</td>
<td>
{% if dashboard.is_active %}
<span class="badge bg-success">Active</span>
{% else %}
<span class="badge bg-secondary">Inactive</span>
{% endif %}
{% if dashboard.is_shared %}
<span class="badge bg-info ms-1">Shared</span>
{% endif %}
</td>
<td>{{ dashboard.created_by.get_full_name|default:dashboard.created_by.username }}</td>
<td>{{ dashboard.updated_at|date:"M d, Y H:i" }}</td>
<td>
<div class="btn-group btn-group-sm">
<a href="{% url 'analytics:dashboard_detail' dashboard.pk %}" class="btn btn-outline-primary" title="View">
<i class="fas fa-eye"></i>
</a>
<a href="{% url 'analytics:dashboard_update' dashboard.pk %}" class="btn btn-outline-secondary" title="Edit">
<i class="fas fa-edit"></i>
</a>
<button class="btn btn-outline-info" onclick="duplicateDashboard({{ dashboard.pk }})" title="Duplicate">
<i class="fas fa-copy"></i>
</button>
<a href="{% url 'analytics:dashboard_delete' dashboard.pk %}" class="btn btn-outline-danger" title="Delete">
<i class="fas fa-trash"></i>
</a>
</div>
</td>
</tr>
{% empty %}
<tr>
<td colspan="7" class="text-center py-4">
<div class="empty-state">
<i class="fas fa-tachometer-alt fa-2x text-muted mb-2"></i>
<p class="text-muted">No dashboards found matching your criteria.</p>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<!-- Pagination -->
{% if is_paginated %}
<div class="pagination-wrapper mt-4">
<nav aria-label="Dashboard pagination">
<ul class="pagination justify-content-center">
{% if page_obj.has_previous %}
<li class="page-item">
<a class="page-link" href="?page=1{% if request.GET.search %}&search={{ request.GET.search }}{% endif %}">First</a>
</li>
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}{% if request.GET.search %}&search={{ request.GET.search }}{% endif %}">Previous</a>
</li>
{% endif %}
{% for num in page_obj.paginator.page_range %}
{% if page_obj.number == num %}
<li class="page-item active">
<span class="page-link">{{ num }}</span>
</li>
{% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
<li class="page-item">
<a class="page-link" href="?page={{ num }}{% if request.GET.search %}&search={{ request.GET.search }}{% endif %}">{{ num }}</a>
</li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}{% if request.GET.search %}&search={{ request.GET.search }}{% endif %}">Next</a>
</li>
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.paginator.num_pages }}{% if request.GET.search %}&search={{ request.GET.search }}{% endif %}">Last</a>
</li>
{% endif %}
</ul>
</nav>
</div>
{% endif %}
</div>
</div>
</div>
</div>
<script>
// Load dashboard statistics
{#function loadDashboardStats() {#}
{# $.ajax({#}
{# url: '{% url "analytics:dashboard_stats" %}',#}
{# success: function(data) {#}
{# $('#total-dashboards').text(data.total_dashboards);#}
{# $('#active-dashboards').text(data.active_dashboards);#}
{# $('#shared-dashboards').text(data.shared_dashboards);#}
{# $('#total-widgets').text(data.total_widgets);#}
{# },#}
{# error: function() {#}
{# console.log('Failed to load dashboard statistics');#}
{# }#}
{# });#}
{# }#}
// Toggle between grid and list view
function toggleView(view) {
if (view === 'grid') {
$('#gridView').show();
$('#listView').hide();
$('#gridViewBtn').addClass('active');
$('#listViewBtn').removeClass('active');
localStorage.setItem('dashboardView', 'grid');
} else {
$('#gridView').hide();
$('#listView').show();
$('#listViewBtn').addClass('active');
$('#gridViewBtn').removeClass('active');
localStorage.setItem('dashboardView', 'list');
}
}
// Toggle filters panel
function toggleFilters() {
$('#filtersPanel').slideToggle();
}
// Apply filters
function applyFilters() {
const type = $('#dashboardType').val();
const status = $('#statusFilter').val();
const createdBy = $('#createdBy').val();
const search = $('#searchInput').val();
let url = new URL(window.location);
url.searchParams.set('type', type);
url.searchParams.set('status', status);
url.searchParams.set('created_by', createdBy);
url.searchParams.set('search', search);
window.location.href = url.toString();
}
// Clear filters
function clearFilters() {
$('#dashboardType').val('');
$('#statusFilter').val('');
$('#createdBy').val('');
$('#searchInput').val('');
let url = new URL(window.location);
url.search = '';
window.location.href = url.toString();
}
// Duplicate dashboard
function duplicateDashboard(dashboardId) {
if (confirm('Are you sure you want to duplicate this dashboard?')) {
$.ajax({
url: `/analytics/dashboards/${dashboardId}/duplicate/`,
method: 'POST',
headers: {
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value
},
success: function(data) {
if (data.success) {
alert('Dashboard duplicated successfully!');
window.location.reload();
} else {
alert('Failed to duplicate dashboard: ' + data.error);
}
},
error: function() {
alert('Error duplicating dashboard');
}
});
}
}
// Share dashboard
function shareDashboard(dashboardId) {
// Implement share functionality
alert('Share functionality will be implemented');
}
// Search functionality
$('#searchInput').on('keyup', function(e) {
if (e.key === 'Enter') {
applyFilters();
}
});
// Initialize page
$(document).ready(function() {
// Load statistics
loadDashboardStats();
// Restore view preference
const savedView = localStorage.getItem('dashboardView') || 'grid';
toggleView(savedView);
// Initialize tooltips
$('[data-bs-toggle="tooltip"]').tooltip();
});
</script>
<style>
.dashboard-grid .dashboard-card {
border: 1px solid #e9ecef;
border-radius: 8px;
transition: all 0.3s ease;
height: 100%;
}
.dashboard-card:hover {
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
transform: translateY(-2px);
}
.dashboard-header {
padding: 20px;
border-bottom: 1px solid #f8f9fa;
}
.dashboard-type {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.dashboard-title {
margin-bottom: 8px;
font-weight: 600;
}
.dashboard-description {
color: #6c757d;
font-size: 0.875rem;
margin-bottom: 0;
}
.dashboard-preview {
padding: 15px 20px;
background-color: #f8f9fa;
}
.preview-widgets {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.widget-mini {
display: flex;
align-items: center;
gap: 5px;
font-size: 0.75rem;
color: #6c757d;
}
.dashboard-footer {
padding: 15px 20px;
display: flex;
justify-content: space-between;
align-items: center;
border-top: 1px solid #f8f9fa;
}
.dashboard-meta {
display: flex;
flex-wrap: wrap;
gap: 15px;
}
.meta-item {
font-size: 0.75rem;
color: #6c757d;
}
.dashboard-status {
display: flex;
gap: 5px;
}
.filters-panel {
background-color: #f8f9fa;
padding: 20px;
border-radius: 8px;
margin-bottom: 20px;
}
.empty-state {
padding: 60px 20px;
}
@media (max-width: 768px) {
.dashboard-meta {
flex-direction: column;
gap: 8px;
}
.dashboard-footer {
flex-direction: column;
align-items: flex-start;
gap: 10px;
}
.preview-widgets {
justify-content: center;
}
}
</style>
{% endblock %}