HH/templates/social/social_analytics.html
2026-01-12 12:27:29 +03:00

488 lines
23 KiB
HTML

{% extends "layouts/base.html" %}
{% load i18n %}
{% load static %}
{% load social_filters %}
{% block title %}{% trans "Analytics Dashboard" %} - {% trans "Social Media Monitoring" %} - PX360{% endblock %}
{% block content %}
<div class="container-fluid py-4">
<!-- Page Header -->
<div class="d-flex flex-wrap justify-content-between align-items-center mb-4 gap-3">
<div>
<h2 class="mb-1 fw-bold">
<i class="bi bi-graph-up-arrow text-primary me-2"></i>
{% trans "Analytics Dashboard" %}
</h2>
<p class="text-muted mb-0 small">{% trans "Social media insights and trends" %}</p>
</div>
<div class="d-flex flex-wrap gap-2">
<a href="{% url 'social:social_comment_list' %}" class="btn btn-outline-primary">
<i class="bi bi-grid-fill me-1"></i> {% trans "Dashboard" %}
</a>
<form class="d-inline-flex" method="get">
<div class="input-group shadow-sm">
<span class="input-group-text bg-light border-end-0">
<i class="bi bi-calendar-range text-muted"></i>
</span>
<input type="date"
name="start_date"
class="form-control border-start-0 border-end-0"
value="{{ start_date|default:'' }}"
placeholder="{% trans 'Date from' %}">
<span class="input-group-text bg-light border-end-0 border-start-0">-</span>
<input type="date"
name="end_date"
class="form-control border-start-0"
value="{{ end_date|default:'' }}"
placeholder="{% trans 'Date to' %}">
<button type="submit" class="btn btn-primary px-4">
<i class="bi bi-funnel-fill me-1"></i> {% trans "Filter" %}
</button>
</div>
</form>
</div>
</div>
<!-- Overview Cards -->
<div class="row g-3 mb-4">
<!-- Total Comments Card -->
<div class="col-lg-3 col-md-6 mb-3">
<div class="card border-0 shadow-sm h-100">
<div class="card-body p-4">
<div class="d-flex justify-content-between align-items-start mb-3">
<div>
<h6 class="text-muted text-uppercase small fw-bold mb-2">{% trans "Total Comments" %}</h6>
<h2 class="mb-0 fw-bold text-primary">{{ total_comments }}</h2>
</div>
<div class="bg-primary bg-opacity-10 rounded-3 p-3">
<i class="bi bi-chat-dots text-primary fs-4"></i>
</div>
</div>
<div class="d-flex align-items-center gap-2">
<span class="badge bg-success-subtle text-success">
<i class="bi bi-check-circle me-1"></i>{{ analyzed_comments }}
</span>
<span class="text-muted small">{% trans "analyzed" %}</span>
</div>
</div>
</div>
</div>
<!-- Positive Comments Card -->
<div class="col-lg-3 col-md-6 mb-3">
<div class="card border-0 shadow-sm h-100">
<div class="card-body p-4">
<div class="d-flex justify-content-between align-items-start mb-3">
<div>
<h6 class="text-muted text-uppercase small fw-bold mb-2">{% trans "Positive" %}</h6>
<h2 class="mb-0 fw-bold text-success">{{ sentiment_distribution|get_sentiment_count:'positive' }}</h2>
</div>
<div class="bg-success bg-opacity-10 rounded-3 p-3">
<i class="bi bi-emoji-smile text-success fs-4"></i>
</div>
</div>
<div class="progress mb-2" style="height: 6px;">
<div class="progress-bar bg-success"
role="progressbar"
style="width: {% widthratio sentiment_distribution|get_sentiment_count:'positive' total_comments 100 %}%"></div>
</div>
<small class="text-muted">{% widthratio sentiment_distribution|get_sentiment_count:'positive' total_comments 100 %}% {% trans "of total" %}</small>
</div>
</div>
</div>
<!-- Negative Comments Card -->
<div class="col-lg-3 col-md-6 mb-3">
<div class="card border-0 shadow-sm h-100">
<div class="card-body p-4">
<div class="d-flex justify-content-between align-items-start mb-3">
<div>
<h6 class="text-muted text-uppercase small fw-bold mb-2">{% trans "Negative" %}</h6>
<h2 class="mb-0 fw-bold text-danger">{{ sentiment_distribution|get_sentiment_count:'negative' }}</h2>
</div>
<div class="bg-danger bg-opacity-10 rounded-3 p-3">
<i class="bi bi-emoji-frown text-danger fs-4"></i>
</div>
</div>
<div class="progress mb-2" style="height: 6px;">
<div class="progress-bar bg-danger"
role="progressbar"
style="width: {% widthratio sentiment_distribution|get_sentiment_count:'negative' total_comments 100 %}%"></div>
</div>
<small class="text-muted">{% widthratio sentiment_distribution|get_sentiment_count:'negative' total_comments 100 %}% {% trans "of total" %}</small>
</div>
</div>
</div>
<!-- Avg Engagement Card -->
<div class="col-lg-3 col-md-6 mb-3">
<div class="card border-0 shadow-sm h-100">
<div class="card-body p-4">
<div class="d-flex justify-content-between align-items-start mb-3">
<div>
<h6 class="text-muted text-uppercase small fw-bold mb-2">{% trans "Avg Engagement" %}</h6>
<h2 class="mb-0 fw-bold text-info">{{ engagement_metrics.avg_likes|add:engagement_metrics.avg_replies|floatformat:1 }}</h2>
</div>
<div class="bg-info bg-opacity-10 rounded-3 p-3">
<i class="bi bi-heart-pulse text-info fs-4"></i>
</div>
</div>
<small class="text-muted"><i class="bi bi-lightning me-1"></i>{% trans "likes + replies" %}</small>
</div>
</div>
</div>
</div>
<!-- Charts Row -->
<div class="row g-3 mb-4">
<!-- Sentiment Distribution -->
<div class="col-lg-6 mb-4">
<div class="card border-0 shadow-sm h-100">
<div class="card-header bg-white border-0 py-3">
<div class="d-flex align-items-center">
<div class="bg-primary bg-opacity-10 rounded-2 p-2 me-3">
<i class="bi bi-pie-chart-fill text-primary"></i>
</div>
<h6 class="mb-0 fw-bold">{% trans "Sentiment Distribution" %}</h6>
</div>
</div>
<div class="card-body p-4">
<canvas id="sentimentChart" height="220"></canvas>
</div>
</div>
</div>
<!-- Platform Distribution -->
<div class="col-lg-6 mb-4">
<div class="card border-0 shadow-sm h-100">
<div class="card-header bg-white border-0 py-3">
<div class="d-flex align-items-center">
<div class="bg-info bg-opacity-10 rounded-2 p-2 me-3">
<i class="bi bi-bar-chart-fill text-info"></i>
</div>
<h6 class="mb-0 fw-bold">{% trans "Platform Distribution" %}</h6>
</div>
</div>
<div class="card-body p-4">
<canvas id="platformChart" height="220"></canvas>
</div>
</div>
</div>
</div>
<!-- Daily Trends -->
<div class="row g-3 mb-4">
<div class="col-12">
<div class="card border-0 shadow-sm">
<div class="card-header bg-white border-0 py-3">
<div class="d-flex align-items-center">
<div class="bg-success bg-opacity-10 rounded-2 p-2 me-3">
<i class="bi bi-graph-up-arrow text-success"></i>
</div>
<h6 class="mb-0 fw-bold">{% trans "Daily Trends" %}</h6>
</div>
</div>
<div class="card-body p-4">
<canvas id="trendsChart" height="120"></canvas>
</div>
</div>
</div>
</div>
<!-- Keywords & Topics -->
<div class="row g-3 mb-4">
<!-- Top Keywords -->
<div class="col-lg-6 mb-4">
<div class="card border-0 shadow-sm h-100">
<div class="card-header bg-white border-0 py-3">
<div class="d-flex align-items-center">
<div class="bg-warning bg-opacity-10 rounded-2 p-2 me-3">
<i class="bi bi-key-fill text-warning"></i>
</div>
<h6 class="mb-0 fw-bold">{% trans "Top Keywords" %}</h6>
</div>
</div>
<div class="card-body p-4">
{% if top_keywords %}
<div class="row g-2">
{% for keyword in top_keywords|slice:":12" %}
<div class="col-md-6 col-sm-12 mb-2">
<div class="card border-0 shadow-sm p-3">
<div class="d-flex justify-content-between align-items-center mb-2">
<span class="fw-semibold">{{ keyword.keyword }}</span>
<span class="badge bg-primary rounded-pill">{{ keyword.count }}</span>
</div>
<div class="progress mb-0" style="height: 8px;">
<div class="progress-bar bg-warning"
role="progressbar"
style="width: {% widthratio keyword.count top_keywords.0.count 100 %}%;
background: linear-gradient(90deg, #ffc107 0%, #ffca2c 100%);"></div>
</div>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="text-center py-5">
<i class="bi bi-key text-muted fs-1"></i>
<p class="text-muted mt-2">{% trans "No keywords found" %}</p>
</div>
{% endif %}
</div>
</div>
</div>
<!-- Top Topics -->
<div class="col-lg-6 mb-4">
<div class="card border-0 shadow-sm h-100">
<div class="card-header bg-white border-0 py-3">
<div class="d-flex align-items-center">
<div class="bg-primary bg-opacity-10 rounded-2 p-2 me-3">
<i class="bi bi-collection-fill text-primary"></i>
</div>
<h6 class="mb-0 fw-bold">{% trans "Top Topics" %}</h6>
</div>
</div>
<div class="card-body p-4">
{% if top_topics %}
<div class="list-group list-group-flush">
{% for topic in top_topics|slice:":10" %}
<div class="list-group-item d-flex justify-content-between align-items-center px-0 py-3 border-0">
<div class="d-flex align-items-center">
<div class="bg-primary bg-opacity-10 rounded-circle p-2 me-3">
<i class="bi bi-hash text-primary small"></i>
</div>
<span class="fw-medium">{{ topic.topic }}</span>
</div>
<span class="badge bg-info rounded-pill px-3 py-2">{{ topic.count }}</span>
</div>
{% endfor %}
</div>
{% else %}
<div class="text-center py-5">
<i class="bi bi-collection text-muted fs-1"></i>
<p class="text-muted mt-2">{% trans "No topics found" %}</p>
</div>
{% endif %}
</div>
</div>
</div>
</div>
<!-- Platform Breakdown -->
<div class="row g-3 mb-4">
<div class="col-12">
<div class="card border-0 shadow-sm">
<div class="card-header bg-white border-0 py-3">
<div class="d-flex align-items-center">
<div class="bg-secondary bg-opacity-10 rounded-2 p-2 me-3">
<i class="bi bi-grid-fill text-secondary"></i>
</div>
<h6 class="mb-0 fw-bold">{% trans "Platform Breakdown" %}</h6>
</div>
</div>
<div class="card-body p-4">
<div class="table-responsive">
<table class="table table-hover table-striped border-0">
<thead>
<tr class="table-light">
<th class="fw-bold text-muted small text-uppercase ps-3">{% trans "Platform" %}</th>
<th class="fw-bold text-muted small text-uppercase">{% trans "Comments" %}</th>
<th class="fw-bold text-muted small text-uppercase">{% trans "Avg Sentiment" %}</th>
<th class="fw-bold text-muted small text-uppercase">{% trans "Total Likes" %}</th>
<th class="fw-bold text-muted small text-uppercase">{% trans "Total Replies" %}</th>
<th class="fw-bold text-muted small text-uppercase pe-3">{% trans "Actions" %}</th>
</tr>
</thead>
<tbody>
{% for platform in platform_distribution %}
<tr class="align-middle">
<td class="ps-3">
<a href="{% url 'social:social_platform' platform.platform %}" class="text-decoration-none fw-semibold text-primary">
{{ platform.platform_display }}
</a>
</td>
<td>
<span class="badge bg-light text-dark">{{ platform.count }}</span>
</td>
<td>
<span class="{% if platform.avg_sentiment > 0.5 %}badge bg-success-subtle text-success{% elif platform.avg_sentiment < 0.5 %}badge bg-danger-subtle text-danger{% else %}badge bg-secondary-subtle text-secondary{% endif %} px-3 py-2 rounded-pill">
{{ platform.avg_sentiment|floatformat:2 }}
</span>
</td>
<td>
<span class="d-inline-flex align-items-center gap-1">
<i class="bi bi-hand-thumbs-up text-muted small"></i>
<span>{{ platform.total_likes }}</span>
</span>
</td>
<td>
<span class="d-inline-flex align-items-center gap-1">
<i class="bi bi-chat text-muted small"></i>
<span>{{ platform.total_replies }}</span>
</span>
</td>
<td class="pe-3">
<a href="{% url 'social:social_platform' platform.platform %}" class="btn btn-sm btn-primary rounded-pill px-3">
<i class="bi bi-arrow-right me-1"></i>{% trans "View" %}
</a>
</td>
</tr>
{% empty %}
<tr>
<td colspan="6" class="text-center py-5">
<i class="bi bi-grid text-muted fs-1 mb-2"></i>
<p class="text-muted mb-0">{% trans "No data available" %}</p>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- Top Entities -->
<div class="row g-3">
<div class="col-12">
<div class="card border-0 shadow-sm">
<div class="card-header bg-white border-0 py-3">
<div class="d-flex align-items-center">
<div class="bg-purple bg-opacity-10 rounded-2 p-2 me-3">
<i class="bi bi-tags-fill text-purple"></i>
</div>
<h6 class="mb-0 fw-bold">{% trans "Top Entities" %}</h6>
</div>
</div>
<div class="card-body p-4">
{% if top_entities %}
<div class="row g-3">
{% for entity in top_entities|slice:":20" %}
<div class="col-lg-2 col-md-3 col-sm-4 col-6 mb-3">
<div class="card border-0 shadow-sm h-100 hover-lift">
<div class="card-body text-center p-3">
<div class="bg-purple bg-opacity-10 rounded-circle d-inline-flex align-items-center justify-content-center mb-2" style="width: 60px; height: 60px;">
<i class="bi bi-tag text-purple fs-5"></i>
</div>
<h6 class="mb-2 fw-semibold text-truncate" style="max-width: 120px; margin: 0 auto;">{{ entity.entity }}</h6>
<span class="badge bg-purple text-white rounded-pill px-3 py-2">{{ entity.count }}</span>
</div>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="text-center py-5">
<i class="bi bi-tags text-muted fs-1 mb-2"></i>
<p class="text-muted mb-0">{% trans "No entities found" %}</p>
</div>
{% endif %}
</div>
</div>
</div>
</div>
</div>
{% block extra_js %}
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
// Sentiment Distribution Chart
const sentimentCtx = document.getElementById('sentimentChart').getContext('2d');
new Chart(sentimentCtx, {
type: 'doughnut',
data: {
labels: ['Positive', 'Neutral', 'Negative'],
datasets: [{
data: [
{% for item in sentiment_distribution %}{% if item.sentiment == 'positive' %}{{ item.count }}{% endif %}{% endfor %},
{% for item in sentiment_distribution %}{% if item.sentiment == 'neutral' %}{{ item.count }}{% endif %}{% endfor %},
{% for item in sentiment_distribution %}{% if item.sentiment == 'negative' %}{{ item.count }}{% endif %}{% endfor %}
],
backgroundColor: ['#198754', '#6c757d', '#dc3545']
}]
},
options: {
responsive: true,
plugins: {
legend: {
position: 'bottom'
}
}
}
});
// Platform Distribution Chart
const platformCtx = document.getElementById('platformChart').getContext('2d');
new Chart(platformCtx, {
type: 'bar',
data: {
labels: [{% for item in platform_distribution %}'{{ item.platform_display }}',{% endfor %}],
datasets: [{
label: 'Comments',
data: [{% for item in platform_distribution %}{{ item.count }},{% endfor %}],
backgroundColor: '#0d6efd'
}]
},
options: {
responsive: true,
plugins: {
legend: {
display: false
}
},
scales: {
y: {
beginAtZero: true
}
}
}
});
// Daily Trends Chart
const trendsCtx = document.getElementById('trendsChart').getContext('2d');
new Chart(trendsCtx, {
type: 'line',
data: {
labels: [{% for item in daily_trends %}'{{ item.day }}',{% endfor %}],
datasets: [
{
label: 'Total',
data: [{% for item in daily_trends %}{{ item.count }},{% endfor %}],
borderColor: '#0d6efd',
tension: 0.1
},
{
label: 'Positive',
data: [{% for item in daily_trends %}{{ item.positive }},{% endfor %}],
borderColor: '#198754',
tension: 0.1
},
{
label: 'Negative',
data: [{% for item in daily_trends %}{{ item.negative }},{% endfor %}],
borderColor: '#dc3545',
tension: 0.1
}
]
},
options: {
responsive: true,
plugins: {
legend: {
position: 'top'
}
},
scales: {
y: {
beginAtZero: true
}
}
}
});
</script>
{% endblock %}
{% endblock %}