mirror of
https://github.com/kjanat/livegraphs-django.git
synced 2026-01-16 11:42:10 +01:00
Refactor HTML templates for improved readability and consistency
- Updated search_results_table.html to enhance formatting and maintain consistent indentation. - Refined search_results.html layout for better structure and clarity. - Improved upload.html for better organization and readability of the upload form and data source table. - Removed unnecessary lines in package.json and streamlined devDependencies section.
This commit is contained in:
@ -1,31 +1,27 @@
|
||||
<!-- templates/accounts/login.html -->
|
||||
{% extends 'base.html' %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block title %}Login | Chat Analytics{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<div class="card mt-4">
|
||||
<div class="card-header">
|
||||
<h4 class="card-title mb-0">Login</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
<div class="d-grid gap-2">
|
||||
<button type="submit" class="btn btn-primary">Login</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="card-footer text-center">
|
||||
<p class="mb-0">
|
||||
Don't have an account? <a href="{% url 'register' %}">Register</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% extends 'base.html' %} {% load crispy_forms_tags %}
|
||||
{% block title %}
|
||||
Login | Chat Analytics
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<div class="card mt-4">
|
||||
<div class="card-header">
|
||||
<h4 class="card-title mb-0">Login</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="post">
|
||||
{% csrf_token %} {{ form|crispy }}
|
||||
<div class="d-grid gap-2">
|
||||
<button type="submit" class="btn btn-primary">Login</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="card-footer text-center">
|
||||
<p class="mb-0">Don't have an account? <a href="{% url 'register' %}">Register</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@ -1,37 +1,34 @@
|
||||
<!-- templates/accounts/password_change.html -->
|
||||
{% extends 'base.html' %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block title %}Change Password | Chat Analytics{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div
|
||||
class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"
|
||||
>
|
||||
<h1 class="h2">Change Password</h1>
|
||||
<div class="btn-toolbar mb-2 mb-md-0">
|
||||
<a href="{% url 'profile' %}" class="btn btn-sm btn-outline-secondary">
|
||||
<i class="fas fa-arrow-left"></i> Back to Profile
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"
|
||||
>
|
||||
<h1 class="h2">Change Password</h1>
|
||||
<div class="btn-toolbar mb-2 mb-md-0">
|
||||
<a href="{% url 'profile' %}" class="btn btn-sm btn-outline-secondary">
|
||||
<i class="fas fa-arrow-left"></i> Back to Profile
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Change Your Password</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
<div class="d-grid gap-2">
|
||||
<button type="submit" class="btn btn-primary">Change Password</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Change Your Password</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="post">
|
||||
{% csrf_token %} {{ form|crispy }}
|
||||
<div class="d-grid gap-2">
|
||||
<button type="submit" class="btn btn-primary">Change Password</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@ -1,44 +1,38 @@
|
||||
<!-- templates/accounts/password_change_done.html -->
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}Password Changed | Chat Analytics{% endblock %}
|
||||
|
||||
{% extends 'base.html' %} {% block title %}Password Changed | Chat Analytics{% endblock %}
|
||||
{% block content %}
|
||||
<div
|
||||
class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"
|
||||
>
|
||||
<h1 class="h2">Password Changed</h1>
|
||||
<div class="btn-toolbar mb-2 mb-md-0">
|
||||
<a href="{% url 'profile' %}" class="btn btn-sm btn-outline-secondary">
|
||||
<i class="fas fa-arrow-left"></i> Back to Profile
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"
|
||||
>
|
||||
<h1 class="h2">Password Changed</h1>
|
||||
<div class="btn-toolbar mb-2 mb-md-0">
|
||||
<a href="{% url 'profile' %}" class="btn btn-sm btn-outline-secondary">
|
||||
<i class="fas fa-arrow-left"></i> Back to Profile
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header bg-success text-white">
|
||||
<h5 class="card-title mb-0">Password Changed Successfully</h5>
|
||||
</div>
|
||||
<div class="card-body text-center">
|
||||
<div class="mb-4">
|
||||
<i class="fas fa-check-circle fa-4x text-success mb-3"></i>
|
||||
<h4>Your password has been changed successfully!</h4>
|
||||
<p>
|
||||
Your new password is now active. You can use it the next time you log
|
||||
in.
|
||||
</p>
|
||||
</div>
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header bg-success text-white">
|
||||
<h5 class="card-title mb-0">Password Changed Successfully</h5>
|
||||
</div>
|
||||
<div class="card-body text-center">
|
||||
<div class="mb-4">
|
||||
<i class="fas fa-check-circle fa-4x text-success mb-3"></i>
|
||||
<h4>Your password has been changed successfully!</h4>
|
||||
<p>Your new password is now active. You can use it the next time you log in.</p>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<a href="{% url 'profile' %}" class="btn btn-primary">Return to Profile</a>
|
||||
<a href="{% url 'dashboard' %}" class="btn btn-outline-secondary ms-2"
|
||||
>Go to Dashboard</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<a href="{% url 'profile' %}" class="btn btn-primary">Return to Profile</a>
|
||||
<a href="{% url 'dashboard' %}" class="btn btn-outline-secondary ms-2"
|
||||
>Go to Dashboard</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@ -1,213 +1,187 @@
|
||||
<!-- templates/accounts/profile.html -->
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}My Profile | Chat Analytics{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div
|
||||
class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"
|
||||
>
|
||||
<h1 class="h2">My Profile</h1>
|
||||
<div class="btn-toolbar mb-2 mb-md-0">
|
||||
<a href="{% url 'dashboard' %}" class="btn btn-sm btn-outline-secondary">
|
||||
<i class="fas fa-arrow-left"></i> Back to Dashboard
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"
|
||||
>
|
||||
<h1 class="h2">My Profile</h1>
|
||||
<div class="btn-toolbar mb-2 mb-md-0">
|
||||
<a href="{% url 'dashboard' %}" class="btn btn-sm btn-outline-secondary">
|
||||
<i class="fas fa-arrow-left"></i> Back to Dashboard
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Account Information</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4 fw-bold">Username:</div>
|
||||
<div class="col-md-8">{{ user.username }}</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4 fw-bold">Email:</div>
|
||||
<div class="col-md-8">{{ user.email }}</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4 fw-bold">Company:</div>
|
||||
<div class="col-md-8">
|
||||
{% if user.company %}
|
||||
{{ user.company.name }}
|
||||
{% else %}
|
||||
<span class="text-muted">Not assigned to a company</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4 fw-bold">Role:</div>
|
||||
<div class="col-md-8">
|
||||
{% if user.is_staff %}
|
||||
<span class="badge bg-danger">Admin</span>
|
||||
{% elif user.is_company_admin %}
|
||||
<span class="badge bg-primary">Company Admin</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">User</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4 fw-bold">Last Login:</div>
|
||||
<div class="col-md-8">{{ user.last_login|date:"F d, Y H:i" }}</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4 fw-bold">Date Joined:</div>
|
||||
<div class="col-md-8">{{ user.date_joined|date:"F d, Y H:i" }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<a href="{% url 'password_change' %}" class="btn btn-primary"
|
||||
>Change Password</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Account Information</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4 fw-bold">Username:</div>
|
||||
<div class="col-md-8">{{ user.username }}</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4 fw-bold">Email:</div>
|
||||
<div class="col-md-8">{{ user.email }}</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4 fw-bold">Company:</div>
|
||||
<div class="col-md-8">
|
||||
{% if user.company %}
|
||||
{{ user.company.name }}
|
||||
{% else %}
|
||||
<span class="text-muted">Not assigned to a company</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4 fw-bold">Role:</div>
|
||||
<div class="col-md-8">
|
||||
{% if user.is_staff %}
|
||||
<span class="badge bg-danger">Admin</span>
|
||||
{% elif user.is_company_admin %}
|
||||
<span class="badge bg-primary">Company Admin</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">User</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4 fw-bold">Last Login:</div>
|
||||
<div class="col-md-8">{{ user.last_login|date:"F d, Y H:i" }}</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4 fw-bold">Date Joined:</div>
|
||||
<div class="col-md-8">{{ user.date_joined|date:"F d, Y H:i" }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<a href="{% url 'password_change' %}" class="btn btn-primary">Change Password</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if user.company %}
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Company Information</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4 fw-bold">Company Name:</div>
|
||||
<div class="col-md-8">{{ user.company.name }}</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4 fw-bold">Description:</div>
|
||||
<div class="col-md-8">
|
||||
{{ user.company.description|default:"No description available." }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4 fw-bold">Created:</div>
|
||||
<div class="col-md-8">{{ user.company.created_at|date:"F d, Y" }}</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4 fw-bold">Total Employees:</div>
|
||||
<div class="col-md-8">{{ user.company.employees.count }}</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4 fw-bold">Data Sources:</div>
|
||||
<div class="col-md-8">{{ user.company.data_sources.count }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if user.company %}
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Company Information</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4 fw-bold">Company Name:</div>
|
||||
<div class="col-md-8">{{ user.company.name }}</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4 fw-bold">Description:</div>
|
||||
<div class="col-md-8">
|
||||
{{ user.company.description|default:"No description available." }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4 fw-bold">Created:</div>
|
||||
<div class="col-md-8">{{ user.company.created_at|date:"F d, Y" }}</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4 fw-bold">Total Employees:</div>
|
||||
<div class="col-md-8">{{ user.company.employees.count }}</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4 fw-bold">Data Sources:</div>
|
||||
<div class="col-md-8">{{ user.company.data_sources.count }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if user.is_company_admin or user.is_staff %}
|
||||
<div class="row mt-4">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Admin Actions</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
{% if user.is_staff %}
|
||||
<div class="col-md-4 mb-3">
|
||||
<div class="card h-100">
|
||||
<div class="card-body text-center">
|
||||
<h5 class="card-title">Manage Users</h5>
|
||||
<p class="card-text">
|
||||
Manage users and assign them to companies.
|
||||
</p>
|
||||
<a
|
||||
href="{% url 'admin:accounts_customuser_changelist' %}"
|
||||
class="btn btn-primary"
|
||||
>Manage Users</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<div class="card h-100">
|
||||
<div class="card-body text-center">
|
||||
<h5 class="card-title">Manage Companies</h5>
|
||||
<p class="card-text">
|
||||
Create and edit companies in the system.
|
||||
</p>
|
||||
<a
|
||||
href="{% url 'admin:accounts_company_changelist' %}"
|
||||
class="btn btn-primary"
|
||||
>Manage Companies</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<div class="card h-100">
|
||||
<div class="card-body text-center">
|
||||
<h5 class="card-title">Admin Dashboard</h5>
|
||||
<p class="card-text">Go to the full admin dashboard.</p>
|
||||
<a
|
||||
href="{% url 'admin:index' %}"
|
||||
class="btn btn-primary"
|
||||
>Admin Dashboard</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% elif user.is_company_admin %}
|
||||
<div class="col-md-4 mb-3">
|
||||
<div class="card h-100">
|
||||
<div class="card-body text-center">
|
||||
<h5 class="card-title">Manage Dashboards</h5>
|
||||
<p class="card-text">
|
||||
Create and edit dashboards for your company.
|
||||
</p>
|
||||
<a
|
||||
href="{% url 'create_dashboard' %}"
|
||||
class="btn btn-primary"
|
||||
>Manage Dashboards</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<div class="card h-100">
|
||||
<div class="card-body text-center">
|
||||
<h5 class="card-title">Upload Data</h5>
|
||||
<p class="card-text">
|
||||
Upload and manage data sources for analysis.
|
||||
</p>
|
||||
<a
|
||||
href="{% url 'upload_data' %}"
|
||||
class="btn btn-primary"
|
||||
>Upload Data</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<div class="card h-100">
|
||||
<div class="card-body text-center">
|
||||
<h5 class="card-title">Search Sessions</h5>
|
||||
<p class="card-text">
|
||||
Search and analyze chat sessions.
|
||||
</p>
|
||||
<a
|
||||
href="{% url 'search_chat_sessions' %}"
|
||||
class="btn btn-primary"
|
||||
>Search Sessions</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if user.is_company_admin or user.is_staff %}
|
||||
<div class="row mt-4">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Admin Actions</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
{% if user.is_staff %}
|
||||
<div class="col-md-4 mb-3">
|
||||
<div class="card h-100">
|
||||
<div class="card-body text-center">
|
||||
<h5 class="card-title">Manage Users</h5>
|
||||
<p class="card-text">Manage users and assign them to companies.</p>
|
||||
<a
|
||||
href="{% url 'admin:accounts_customuser_changelist' %}"
|
||||
class="btn btn-primary"
|
||||
>Manage Users</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<div class="card h-100">
|
||||
<div class="card-body text-center">
|
||||
<h5 class="card-title">Manage Companies</h5>
|
||||
<p class="card-text">Create and edit companies in the system.</p>
|
||||
<a
|
||||
href="{% url 'admin:accounts_company_changelist' %}"
|
||||
class="btn btn-primary"
|
||||
>Manage Companies</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<div class="card h-100">
|
||||
<div class="card-body text-center">
|
||||
<h5 class="card-title">Admin Dashboard</h5>
|
||||
<p class="card-text">Go to the full admin dashboard.</p>
|
||||
<a href="{% url 'admin:index' %}" class="btn btn-primary">Admin Dashboard</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% elif user.is_company_admin %}
|
||||
<div class="col-md-4 mb-3">
|
||||
<div class="card h-100">
|
||||
<div class="card-body text-center">
|
||||
<h5 class="card-title">Manage Dashboards</h5>
|
||||
<p class="card-text">Create and edit dashboards for your company.</p>
|
||||
<a href="{% url 'create_dashboard' %}" class="btn btn-primary"
|
||||
>Manage Dashboards</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<div class="card h-100">
|
||||
<div class="card-body text-center">
|
||||
<h5 class="card-title">Upload Data</h5>
|
||||
<p class="card-text">Upload and manage data sources for analysis.</p>
|
||||
<a href="{% url 'upload_data' %}" class="btn btn-primary">Upload Data</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<div class="card h-100">
|
||||
<div class="card-body text-center">
|
||||
<h5 class="card-title">Search Sessions</h5>
|
||||
<p class="card-text">Search and analyze chat sessions.</p>
|
||||
<a href="{% url 'search_chat_sessions' %}" class="btn btn-primary"
|
||||
>Search Sessions</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
@ -1,31 +1,27 @@
|
||||
<!-- templates/accounts/register.html -->
|
||||
{% extends 'base.html' %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block title %}Register | Chat Analytics{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<div class="card mt-4">
|
||||
<div class="card-header">
|
||||
<h4 class="card-title mb-0">Register</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
<div class="d-grid gap-2">
|
||||
<button type="submit" class="btn btn-primary">Register</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="card-footer text-center">
|
||||
<p class="mb-0">
|
||||
Already have an account? <a href="{% url 'login' %}">Login</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% extends 'base.html' %} {% load crispy_forms_tags %}
|
||||
{% block title %}
|
||||
Register | Chat Analytics
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<div class="card mt-4">
|
||||
<div class="card-header">
|
||||
<h4 class="card-title mb-0">Register</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="post">
|
||||
{% csrf_token %} {{ form|crispy }}
|
||||
<div class="d-grid gap-2">
|
||||
<button type="submit" class="btn btn-primary">Register</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="card-footer text-center">
|
||||
<p class="mb-0">Already have an account? <a href="{% url 'login' %}">Login</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@ -2,339 +2,315 @@
|
||||
{% load static %}
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>{% block title %}Chat Analytics Dashboard{% endblock %}</title>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>{% block title %}Chat Analytics Dashboard{% endblock %}</title>
|
||||
|
||||
<!-- Bootstrap CSS -->
|
||||
<link
|
||||
href="https://cdn.jsdelivr.net/npm/bootstrap@latest/dist/css/bootstrap.min.css"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<!-- Bootstrap CSS -->
|
||||
<link
|
||||
href="https://cdn.jsdelivr.net/npm/bootstrap@latest/dist/css/bootstrap.min.css"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
|
||||
<!-- Font Awesome -->
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@latest/css/all.min.css"
|
||||
/>
|
||||
<!-- Font Awesome -->
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@latest/css/all.min.css"
|
||||
/>
|
||||
|
||||
<!-- Plotly.js -->
|
||||
<script
|
||||
src="https://cdn.jsdelivr.net/npm/plotly.js@latest/dist/plotly.min.js"
|
||||
charset="utf-8"
|
||||
></script>
|
||||
<!-- Plotly.js -->
|
||||
<script
|
||||
src="https://cdn.jsdelivr.net/npm/plotly.js@latest/dist/plotly.min.js"
|
||||
charset="utf-8"
|
||||
></script>
|
||||
|
||||
<!-- Custom CSS -->
|
||||
<link rel="stylesheet" href="{% static 'css/style.css' %}" />
|
||||
<link rel="stylesheet" href="{% static 'css/dashboard.css' %}" />
|
||||
<!-- Custom CSS -->
|
||||
<link rel="stylesheet" href="{% static 'css/style.css' %}" />
|
||||
<link rel="stylesheet" href="{% static 'css/dashboard.css' %}" />
|
||||
|
||||
{% block extra_css %}{% endblock %}
|
||||
</head>
|
||||
{% block extra_css %}{% endblock %}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- Navbar -->
|
||||
<nav class="navbar navbar-expand-md navbar-dark bg-dark absolute-top">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="{% url 'dashboard' %}">Chat Analytics</a>
|
||||
<button
|
||||
class="navbar-toggler"
|
||||
type="button"
|
||||
data-bs-toggle="collapse"
|
||||
data-bs-target="#navbarCollapse"
|
||||
>
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarCollapse">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-md-0">
|
||||
<li class="nav-item">
|
||||
<a
|
||||
class="nav-link ajax-nav-link {% if request.resolver_match.url_name == 'dashboard' %}active{% endif %}"
|
||||
href="{% url 'dashboard' %}"
|
||||
>Dashboard</a
|
||||
>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a
|
||||
class="nav-link ajax-nav-link {% if request.resolver_match.url_name == 'upload_data' %}active{% endif %}"
|
||||
href="{% url 'upload_data' %}"
|
||||
>Upload Data</a
|
||||
>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a
|
||||
class="nav-link ajax-nav-link {% if request.resolver_match.url_name == 'search_chat_sessions' %}active{% endif %}"
|
||||
href="{% url 'search_chat_sessions' %}"
|
||||
>Search</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
<body>
|
||||
<!-- Navbar -->
|
||||
<nav class="navbar navbar-expand-md navbar-dark bg-dark absolute-top">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="{% url 'dashboard' %}">Chat Analytics</a>
|
||||
<button
|
||||
class="navbar-toggler"
|
||||
type="button"
|
||||
data-bs-toggle="collapse"
|
||||
data-bs-target="#navbarCollapse"
|
||||
>
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarCollapse">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-md-0">
|
||||
<li class="nav-item">
|
||||
<a
|
||||
class="nav-link ajax-nav-link {% if request.resolver_match.url_name == 'dashboard' %}active{% endif %}"
|
||||
href="{% url 'dashboard' %}"
|
||||
>Dashboard</a
|
||||
>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a
|
||||
class="nav-link ajax-nav-link {% if request.resolver_match.url_name == 'upload_data' %}active{% endif %}"
|
||||
href="{% url 'upload_data' %}"
|
||||
>Upload Data</a
|
||||
>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a
|
||||
class="nav-link ajax-nav-link {% if request.resolver_match.url_name == 'search_chat_sessions' %}active{% endif %}"
|
||||
href="{% url 'search_chat_sessions' %}"
|
||||
>Search</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="d-flex">
|
||||
{% if user.is_authenticated %}
|
||||
<div class="dropdown">
|
||||
<button
|
||||
class="btn btn-outline-light dropdown-toggle"
|
||||
type="button"
|
||||
id="userDropdown"
|
||||
data-bs-toggle="dropdown"
|
||||
aria-expanded="false"
|
||||
>
|
||||
{% if user.company %}
|
||||
<span class="badge bg-info me-1"
|
||||
>{{ user.company.name }}</span
|
||||
>
|
||||
{% endif %}
|
||||
{{ user.username }}
|
||||
</button>
|
||||
<ul
|
||||
class="dropdown-menu dropdown-menu-end"
|
||||
aria-labelledby="userDropdown"
|
||||
>
|
||||
<li>
|
||||
<a
|
||||
class="dropdown-item ajax-nav-link"
|
||||
href="{% url 'profile' %}"
|
||||
>Profile</a
|
||||
>
|
||||
</li>
|
||||
{% if user.is_staff %}
|
||||
<li>
|
||||
<a class="dropdown-item" href="{% url 'admin:index' %}"
|
||||
>Admin</a
|
||||
>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li>
|
||||
<hr class="dropdown-divider" />
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item" href="{% url 'logout' %}"
|
||||
>Logout</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{% else %}
|
||||
<a href="{% url 'login' %}" class="btn btn-outline-light me-2">Login</a>
|
||||
<a href="{% url 'register' %}" class="btn btn-light">Register</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="d-flex">
|
||||
{% if user.is_authenticated %}
|
||||
<div class="dropdown">
|
||||
<button
|
||||
class="btn btn-outline-light dropdown-toggle"
|
||||
type="button"
|
||||
id="userDropdown"
|
||||
data-bs-toggle="dropdown"
|
||||
aria-expanded="false"
|
||||
>
|
||||
{% if user.company %}
|
||||
<span class="badge bg-info me-1">{{ user.company.name }}</span>
|
||||
{% endif %}
|
||||
{{ user.username }}
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="userDropdown">
|
||||
<li>
|
||||
<a class="dropdown-item ajax-nav-link" href="{% url 'profile' %}">Profile</a>
|
||||
</li>
|
||||
{% if user.is_staff %}
|
||||
<li>
|
||||
<a class="dropdown-item" href="{% url 'admin:index' %}">Admin</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li>
|
||||
<hr class="dropdown-divider" />
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item" href="{% url 'logout' %}">Logout</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{% else %}
|
||||
<a href="{% url 'login' %}" class="btn btn-outline-light me-2">Login</a>
|
||||
<a href="{% url 'register' %}" class="btn btn-light">Register</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<!-- Sidebar -->
|
||||
<nav
|
||||
id="sidebarMenu"
|
||||
class="col-md-3 col-lg-2 d-md-block bg-light sidebar collapse sticky-top h-100 p-0"
|
||||
>
|
||||
<div class="sidebar-sticky pt-3">
|
||||
{% block sidebar %}
|
||||
<ul class="nav flex-column">
|
||||
<li class="nav-item">
|
||||
<a
|
||||
class="nav-link ajax-nav-link {% if request.resolver_match.url_name == 'dashboard' %}active{% endif %}"
|
||||
href="{% url 'dashboard' %}"
|
||||
>
|
||||
<i class="fas fa-tachometer-alt me-2"></i>
|
||||
Dashboard
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a
|
||||
class="nav-link ajax-nav-link {% if request.resolver_match.url_name == 'upload_data' %}active{% endif %}"
|
||||
href="{% url 'upload_data' %}"
|
||||
>
|
||||
<i class="fas fa-upload me-2"></i>
|
||||
Upload Data
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a
|
||||
class="nav-link ajax-nav-link {% if request.resolver_match.url_name == 'search_chat_sessions' %}active{% endif %}"
|
||||
href="{% url 'search_chat_sessions' %}"
|
||||
>
|
||||
<i class="fas fa-search me-2"></i>
|
||||
Search
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a
|
||||
class="nav-link ajax-nav-link {% if request.resolver_match.url_name == 'data_view' %}active{% endif %}"
|
||||
href="{% url 'data_view' %}"
|
||||
>
|
||||
<i class="fas fa-table me-2"></i>
|
||||
Data View
|
||||
</a>
|
||||
</li>
|
||||
{% if user.is_authenticated and user.company %}
|
||||
{% if dashboards %}
|
||||
<li class="nav-header"><strong>Dashboards</strong></li>
|
||||
{% for dashboard in dashboards %}
|
||||
<li class="nav-item">
|
||||
<a
|
||||
class="nav-link {% if selected_dashboard.id == dashboard.id %}active{% endif %}"
|
||||
href="{% url 'dashboard' %}?dashboard_id={{ dashboard.id }}"
|
||||
>
|
||||
<i class="fas fa-chart-line me-2"></i>
|
||||
{{ dashboard.name }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'create_dashboard' %}">
|
||||
<i class="fas fa-plus-circle me-2"></i>
|
||||
New Dashboard
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if data_sources %}
|
||||
<li class="nav-header"><strong>Data Sources</strong></li>
|
||||
{% for data_source in data_sources %}
|
||||
<li class="nav-item">
|
||||
<a
|
||||
class="nav-link"
|
||||
href="{% url 'data_source_detail' data_source.id %}"
|
||||
>
|
||||
<i class="fas fa-database me-2"></i>
|
||||
{{ data_source.name }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
</div>
|
||||
</nav>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<!-- Sidebar -->
|
||||
<nav
|
||||
id="sidebarMenu"
|
||||
class="col-md-3 col-lg-2 d-md-block bg-light sidebar collapse sticky-top h-100 p-0"
|
||||
>
|
||||
<div class="sidebar-sticky pt-3">
|
||||
{% block sidebar %}
|
||||
<ul class="nav flex-column">
|
||||
<li class="nav-item">
|
||||
<a
|
||||
class="nav-link ajax-nav-link {% if request.resolver_match.url_name == 'dashboard' %}active{% endif %}"
|
||||
href="{% url 'dashboard' %}"
|
||||
>
|
||||
<i class="fas fa-tachometer-alt me-2"></i>
|
||||
Dashboard
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a
|
||||
class="nav-link ajax-nav-link {% if request.resolver_match.url_name == 'upload_data' %}active{% endif %}"
|
||||
href="{% url 'upload_data' %}"
|
||||
>
|
||||
<i class="fas fa-upload me-2"></i>
|
||||
Upload Data
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a
|
||||
class="nav-link ajax-nav-link {% if request.resolver_match.url_name == 'search_chat_sessions' %}active{% endif %}"
|
||||
href="{% url 'search_chat_sessions' %}"
|
||||
>
|
||||
<i class="fas fa-search me-2"></i>
|
||||
Search
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a
|
||||
class="nav-link ajax-nav-link {% if request.resolver_match.url_name == 'data_view' %}active{% endif %}"
|
||||
href="{% url 'data_view' %}"
|
||||
>
|
||||
<i class="fas fa-table me-2"></i>
|
||||
Data View
|
||||
</a>
|
||||
</li>
|
||||
{% if user.is_authenticated and user.company %}
|
||||
{% if dashboards %}
|
||||
<li class="nav-header"><strong>Dashboards</strong></li>
|
||||
{% for dashboard in dashboards %}
|
||||
<li class="nav-item">
|
||||
<a
|
||||
class="nav-link {% if selected_dashboard.id == dashboard.id %}active{% endif %}"
|
||||
href="{% url 'dashboard' %}?dashboard_id={{ dashboard.id }}"
|
||||
>
|
||||
<i class="fas fa-chart-line me-2"></i>
|
||||
{{ dashboard.name }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'create_dashboard' %}">
|
||||
<i class="fas fa-plus-circle me-2"></i>
|
||||
New Dashboard
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if data_sources %}
|
||||
<li class="nav-header"><strong>Data Sources</strong></li>
|
||||
{% for data_source in data_sources %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'data_source_detail' data_source.id %}">
|
||||
<i class="fas fa-database me-2"></i>
|
||||
{{ data_source.name }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Main content -->
|
||||
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4 main-content">
|
||||
{# {% if messages %} #}
|
||||
{# <div class="messages mt-3"> #}
|
||||
{# {% for message in messages %} #}
|
||||
{# <div class="alert {% if message.tags == 'error' %}alert-danger{% elif message.tags == 'success' %}alert-success{% elif message.tags == 'warning' %}alert-warning{% else %}alert-info{% endif %} alert-dismissible fade show" role="alert"> #}
|
||||
{# {{ message }} #}
|
||||
{# <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> #}
|
||||
{# </div> #}
|
||||
{# {% endfor %} #}
|
||||
{# </div> #}
|
||||
{# {% endif %} #}
|
||||
<!-- Main content -->
|
||||
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4 main-content">
|
||||
{# {% if messages %} #}
|
||||
{# <div class="messages mt-3"> #}
|
||||
{# {% for message in messages %} #}
|
||||
{# <div class="alert {% if message.tags == 'error' %}alert-danger{% elif message.tags == 'success' %}alert-success{% elif message.tags == 'warning' %}alert-warning{% else %}alert-info{% endif %} alert-dismissible fade show" role="alert"> #}
|
||||
{# {{ message }} #}
|
||||
{# <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> #}
|
||||
{# </div> #}
|
||||
{# {% endfor %} #}
|
||||
{# </div> #}
|
||||
{# {% endif %} #}
|
||||
|
||||
<div id="main-content">
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
<div id="main-content">{% block content %}{% endblock %}</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<div class="container">
|
||||
<p>© {% now "Y" %} KJANAT All rights reserved. | Chat Analytics Dashboard.</p>
|
||||
</div>
|
||||
</footer>
|
||||
<footer>
|
||||
<div class="container">
|
||||
<p>© {% now "Y" %} KJANAT All rights reserved. | Chat Analytics Dashboard.</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<!-- Bootstrap JS -->
|
||||
<script
|
||||
src="https://cdn.jsdelivr.net/npm/bootstrap@latest/dist/js/bootstrap.bundle.min.js"
|
||||
crossorigin="anonymous"
|
||||
></script>
|
||||
<!-- jQuery (for Ajax) -->
|
||||
<script
|
||||
src="https://cdn.jsdelivr.net/npm/jquery@latest/dist/jquery.min.js"
|
||||
crossorigin="anonymous"
|
||||
></script>
|
||||
<!-- Bootstrap JS -->
|
||||
<script
|
||||
src="https://cdn.jsdelivr.net/npm/bootstrap@latest/dist/js/bootstrap.bundle.min.js"
|
||||
crossorigin="anonymous"
|
||||
></script>
|
||||
<!-- jQuery (for Ajax) -->
|
||||
<script
|
||||
src="https://cdn.jsdelivr.net/npm/jquery@latest/dist/jquery.min.js"
|
||||
crossorigin="anonymous"
|
||||
></script>
|
||||
|
||||
<!-- Custom JavaScript -->
|
||||
<script src="{% static 'js/main.js' %}"></script>
|
||||
<script src="{% static 'js/ajax-pagination.js' %}"></script>
|
||||
<script src="{% static 'js/ajax-navigation.js' %}"></script>
|
||||
<!-- Custom JavaScript -->
|
||||
<script src="{% static 'js/main.js' %}"></script>
|
||||
<script src="{% static 'js/ajax-pagination.js' %}"></script>
|
||||
<script src="{% static 'js/ajax-navigation.js' %}"></script>
|
||||
|
||||
<!-- Enable AJAX Navigation -->
|
||||
<script>
|
||||
// Enable AJAX navigation for the entire application
|
||||
var ENABLE_AJAX_NAVIGATION = true;
|
||||
</script>
|
||||
<!-- Enable AJAX Navigation -->
|
||||
<script>
|
||||
// Enable AJAX navigation for the entire application
|
||||
var ENABLE_AJAX_NAVIGATION = true;
|
||||
</script>
|
||||
|
||||
<!-- Check if Plotly loaded successfully -->
|
||||
<script>
|
||||
if (typeof Plotly === "undefined") {
|
||||
console.error("Plotly library failed to load. Will attempt to load fallback.");
|
||||
// Try to load Plotly from alternative source
|
||||
const script = document.createElement("script");
|
||||
script.src = "https://cdn.jsdelivr.net/npm/plotly.js@latest/dist/plotly.min.js";
|
||||
script.async = true;
|
||||
script.crossOrigin = "anonymous";
|
||||
document.head.appendChild(script);
|
||||
}
|
||||
</script>
|
||||
<!-- Check if Plotly loaded successfully -->
|
||||
<script>
|
||||
if (typeof Plotly === "undefined") {
|
||||
console.error("Plotly library failed to load. Will attempt to load fallback.");
|
||||
// Try to load Plotly from alternative source
|
||||
const script = document.createElement("script");
|
||||
script.src = "https://cdn.jsdelivr.net/npm/plotly.js@latest/dist/plotly.min.js";
|
||||
script.async = true;
|
||||
script.crossOrigin = "anonymous";
|
||||
document.head.appendChild(script);
|
||||
}
|
||||
</script>
|
||||
|
||||
{% block extra_js %}
|
||||
{{ block.super }}
|
||||
{% if messages %}
|
||||
<div class="toast-container position-fixed top-0 end-0 p-3" style="z-index: 1100;">
|
||||
<!-- Toasts will be appended here -->
|
||||
</div>
|
||||
{% block extra_js %}
|
||||
{{ block.super }}
|
||||
{% if messages %}
|
||||
<div class="toast-container position-fixed top-0 end-0 p-3" style="z-index: 1100">
|
||||
<!-- Toasts will be appended here -->
|
||||
</div>
|
||||
|
||||
{% for message in messages %}
|
||||
<!-- Pre-render message data that will be used by JavaScript -->
|
||||
<script type="application/json" id="message-data-{{ forloop.counter }}">
|
||||
{
|
||||
"message": "{{ message|escapejs }}",
|
||||
"tags": "{{ message.tags|default:'' }}"
|
||||
}
|
||||
</script>
|
||||
{% endfor %}
|
||||
{% for message in messages %}
|
||||
<!-- Pre-render message data that will be used by JavaScript -->
|
||||
<script type="application/json" id="message-data-{{ forloop.counter }}">
|
||||
{
|
||||
"message": "{{ message|escapejs }}",
|
||||
"tags": "{{ message.tags|default:'' }}"
|
||||
}
|
||||
</script>
|
||||
{% endfor %}
|
||||
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const toastContainer = document.querySelector(".toast-container");
|
||||
if (!toastContainer) return;
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const toastContainer = document.querySelector(".toast-container");
|
||||
if (!toastContainer) return;
|
||||
|
||||
// Find all message data elements
|
||||
const messageDataElements = document.querySelectorAll(
|
||||
'script[id^="message-data-"]',
|
||||
);
|
||||
messageDataElements.forEach(function (dataElement) {
|
||||
try {
|
||||
const messageData = JSON.parse(dataElement.textContent);
|
||||
createToast(messageData.message, messageData.tags);
|
||||
} catch (e) {
|
||||
console.error("Error parsing message data:", e);
|
||||
}
|
||||
});
|
||||
// Find all message data elements
|
||||
const messageDataElements = document.querySelectorAll('script[id^="message-data-"]');
|
||||
messageDataElements.forEach(function (dataElement) {
|
||||
try {
|
||||
const messageData = JSON.parse(dataElement.textContent);
|
||||
createToast(messageData.message, messageData.tags);
|
||||
} catch (e) {
|
||||
console.error("Error parsing message data:", e);
|
||||
}
|
||||
});
|
||||
|
||||
function createToast(messageText, messageTags) {
|
||||
let toastClass = "";
|
||||
let autohide = true;
|
||||
let delay = 5000;
|
||||
function createToast(messageText, messageTags) {
|
||||
let toastClass = "";
|
||||
let autohide = true;
|
||||
let delay = 5000;
|
||||
|
||||
if (messageTags.includes("debug")) {
|
||||
toastClass = "bg-secondary text-white";
|
||||
} else if (messageTags.includes("info")) {
|
||||
toastClass = "bg-info text-dark";
|
||||
} else if (messageTags.includes("success")) {
|
||||
toastClass = "bg-success text-white";
|
||||
} else if (messageTags.includes("warning")) {
|
||||
toastClass = "bg-warning text-dark";
|
||||
autohide = false;
|
||||
} else if (messageTags.includes("error")) {
|
||||
toastClass = "bg-danger text-white";
|
||||
autohide = false;
|
||||
} else {
|
||||
toastClass = "bg-light text-dark";
|
||||
}
|
||||
if (messageTags.includes("debug")) {
|
||||
toastClass = "bg-secondary text-white";
|
||||
} else if (messageTags.includes("info")) {
|
||||
toastClass = "bg-info text-dark";
|
||||
} else if (messageTags.includes("success")) {
|
||||
toastClass = "bg-success text-white";
|
||||
} else if (messageTags.includes("warning")) {
|
||||
toastClass = "bg-warning text-dark";
|
||||
autohide = false;
|
||||
} else if (messageTags.includes("error")) {
|
||||
toastClass = "bg-danger text-white";
|
||||
autohide = false;
|
||||
} else {
|
||||
toastClass = "bg-light text-dark";
|
||||
}
|
||||
|
||||
const toastId =
|
||||
"toast-" +
|
||||
Date.now() +
|
||||
"-" +
|
||||
Math.random().toString(36).substring(2, 11);
|
||||
const toastHtml = `
|
||||
const toastId =
|
||||
"toast-" + Date.now() + "-" + Math.random().toString(36).substring(2, 11);
|
||||
const toastHtml = `
|
||||
<div id="${toastId}" class="toast ${toastClass}" role="alert" aria-live="assertive" aria-atomic="true">
|
||||
<div class="toast-header">
|
||||
<strong class="me-auto">Notification</strong>
|
||||
@ -346,25 +322,25 @@
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
toastContainer.insertAdjacentHTML("beforeend", toastHtml);
|
||||
toastContainer.insertAdjacentHTML("beforeend", toastHtml);
|
||||
|
||||
const toastElement = document.getElementById(toastId);
|
||||
if (toastElement) {
|
||||
const toast = new bootstrap.Toast(toastElement, {
|
||||
autohide: autohide,
|
||||
delay: autohide ? delay : undefined,
|
||||
});
|
||||
const toastElement = document.getElementById(toastId);
|
||||
if (toastElement) {
|
||||
const toast = new bootstrap.Toast(toastElement, {
|
||||
autohide: autohide,
|
||||
delay: autohide ? delay : undefined,
|
||||
});
|
||||
|
||||
toastElement.addEventListener("hidden.bs.toast", function () {
|
||||
toastElement.remove();
|
||||
});
|
||||
toastElement.addEventListener("hidden.bs.toast", function () {
|
||||
toastElement.remove();
|
||||
});
|
||||
|
||||
toast.show();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
</body>
|
||||
toast.show();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -1,145 +1,133 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}Chat Session {{ session.session_id }} | Chat Analytics{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div
|
||||
class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"
|
||||
>
|
||||
<h1 class="h2">Chat Session: {{ session.session_id }}</h1>
|
||||
<div class="btn-toolbar mb-2 mb-md-0">
|
||||
<a
|
||||
href="{% url 'data_source_detail' session.data_source.id %}"
|
||||
class="btn btn-sm btn-outline-secondary"
|
||||
>
|
||||
<i class="fas fa-arrow-left"></i> Back to Data Source
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Session Information</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<p><strong>Session ID:</strong> {{ session.session_id }}</p>
|
||||
<p>
|
||||
<strong>Start Time:</strong>
|
||||
{{ session.start_time|date:"F d, Y H:i" }}
|
||||
</p>
|
||||
<p>
|
||||
<strong>End Time:</strong> {{ session.end_time|date:"F d, Y H:i" }}
|
||||
</p>
|
||||
<p>
|
||||
<strong>IP Address:</strong> {{ session.ip_address|default:"N/A" }}
|
||||
</p>
|
||||
<p><strong>Country:</strong> {{ session.country|default:"N/A" }}</p>
|
||||
<p><strong>Language:</strong> {{ session.language|default:"N/A" }}</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<p><strong>Messages Sent:</strong> {{ session.messages_sent }}</p>
|
||||
<p>
|
||||
<strong>Average Response Time:</strong>
|
||||
{{ session.avg_response_time|floatformat:2 }}s
|
||||
</p>
|
||||
<p><strong>Tokens:</strong> {{ session.tokens }}</p>
|
||||
<p>
|
||||
<strong>Token Cost:</strong> €{{ session.tokens_eur|floatformat:2 }}
|
||||
</p>
|
||||
<p><strong>Category:</strong> {{ session.category|default:"N/A" }}</p>
|
||||
<p>
|
||||
<strong>Sentiment:</strong>
|
||||
{% if session.sentiment %}
|
||||
{% if 'positive' in session.sentiment|lower %}
|
||||
<span class="badge bg-success"
|
||||
>{{ session.sentiment }}</span
|
||||
>
|
||||
{% elif 'negative' in session.sentiment|lower %}
|
||||
<span class="badge bg-danger">{{ session.sentiment }}</span>
|
||||
{% elif 'neutral' in session.sentiment|lower %}
|
||||
<span class="badge bg-warning"
|
||||
>{{ session.sentiment }}</span
|
||||
>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary"
|
||||
>{{ session.sentiment }}</span
|
||||
>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<span class="text-muted">N/A</span>
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-3">
|
||||
<div class="col-12">
|
||||
<p class="mb-2"><strong>Initial Message:</strong></p>
|
||||
<div class="card bg-light">
|
||||
<div class="card-body">
|
||||
<p class="mb-0">{{ session.initial_msg|default:"N/A" }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Additional Info</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>
|
||||
<strong>Escalated:</strong> {% if session.escalated %}
|
||||
<span class="badge bg-danger">Yes</span>
|
||||
{% else %}
|
||||
<span class="badge bg-success">No</span>
|
||||
{% endif %}
|
||||
</p>
|
||||
<p>
|
||||
<strong>Forwarded to HR:</strong> {% if session.forwarded_hr %}
|
||||
<span class="badge bg-danger">Yes</span>
|
||||
{% else %}
|
||||
<span class="badge bg-success">No</span>
|
||||
{% endif %}
|
||||
</p>
|
||||
<p><strong>User Rating:</strong> {{ session.user_rating|default:"N/A" }}</p>
|
||||
<hr />
|
||||
<p>
|
||||
<strong>Data Source:</strong>
|
||||
<a href="{% url 'data_source_detail' session.data_source.id %}"
|
||||
>{{ session.data_source.name }}</a
|
||||
>
|
||||
</p>
|
||||
<p><strong>Company:</strong> {{ session.data_source.company.name }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Full Transcript</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if session.full_transcript %}
|
||||
<div class="chat-transcript" style="max-height: 500px; overflow-y: auto;">
|
||||
<pre style="white-space: pre-wrap; font-family: inherit;">
|
||||
{{ session.full_transcript }}</pre
|
||||
>
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="text-center text-muted">No transcript available.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% block title %}
|
||||
Chat Session {{ session.session_id }} | Chat Analytics
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div
|
||||
class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"
|
||||
>
|
||||
<h1 class="h2">Chat Session: {{ session.session_id }}</h1>
|
||||
<div class="btn-toolbar mb-2 mb-md-0">
|
||||
<a
|
||||
href="{% url 'data_source_detail' session.data_source.id %}"
|
||||
class="btn btn-sm btn-outline-secondary"
|
||||
>
|
||||
<i class="fas fa-arrow-left"></i> Back to Data Source
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Session Information</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<p><strong>Session ID:</strong> {{ session.session_id }}</p>
|
||||
<p>
|
||||
<strong>Start Time:</strong>
|
||||
{{ session.start_time|date:"F d, Y H:i" }}
|
||||
</p>
|
||||
<p><strong>End Time:</strong> {{ session.end_time|date:"F d, Y H:i" }}</p>
|
||||
<p><strong>IP Address:</strong> {{ session.ip_address|default:"N/A" }}</p>
|
||||
<p><strong>Country:</strong> {{ session.country|default:"N/A" }}</p>
|
||||
<p><strong>Language:</strong> {{ session.language|default:"N/A" }}</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<p><strong>Messages Sent:</strong> {{ session.messages_sent }}</p>
|
||||
<p>
|
||||
<strong>Average Response Time:</strong>
|
||||
{{ session.avg_response_time|floatformat:2 }}s
|
||||
</p>
|
||||
<p><strong>Tokens:</strong> {{ session.tokens }}</p>
|
||||
<p><strong>Token Cost:</strong> €{{ session.tokens_eur|floatformat:2 }}</p>
|
||||
<p><strong>Category:</strong> {{ session.category|default:"N/A" }}</p>
|
||||
<p>
|
||||
<strong>Sentiment:</strong>
|
||||
{% if session.sentiment %}
|
||||
{% if 'positive' in session.sentiment|lower %}
|
||||
<span class="badge bg-success">{{ session.sentiment }}</span>
|
||||
{% elif 'negative' in session.sentiment|lower %}
|
||||
<span class="badge bg-danger">{{ session.sentiment }}</span>
|
||||
{% elif 'neutral' in session.sentiment|lower %}
|
||||
<span class="badge bg-warning">{{ session.sentiment }}</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">{{ session.sentiment }}</span>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<span class="text-muted">N/A</span>
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-3">
|
||||
<div class="col-12">
|
||||
<p class="mb-2"><strong>Initial Message:</strong></p>
|
||||
<div class="card bg-light">
|
||||
<div class="card-body">
|
||||
<p class="mb-0">{{ session.initial_msg|default:"N/A" }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Additional Info</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>
|
||||
<strong>Escalated:</strong> {% if session.escalated %}
|
||||
<span class="badge bg-danger">Yes</span>
|
||||
{% else %}
|
||||
<span class="badge bg-success">No</span>
|
||||
{% endif %}
|
||||
</p>
|
||||
<p>
|
||||
<strong>Forwarded to HR:</strong> {% if session.forwarded_hr %}
|
||||
<span class="badge bg-danger">Yes</span>
|
||||
{% else %}
|
||||
<span class="badge bg-success">No</span>
|
||||
{% endif %}
|
||||
</p>
|
||||
<p><strong>User Rating:</strong> {{ session.user_rating|default:"N/A" }}</p>
|
||||
<hr />
|
||||
<p>
|
||||
<strong>Data Source:</strong>
|
||||
<a href="{% url 'data_source_detail' session.data_source.id %}"
|
||||
>{{ session.data_source.name }}</a
|
||||
>
|
||||
</p>
|
||||
<p><strong>Company:</strong> {{ session.data_source.company.name }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Full Transcript</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if session.full_transcript %}
|
||||
<div class="chat-transcript" style="max-height: 500px; overflow-y: auto">
|
||||
<pre style="white-space: pre-wrap; font-family: inherit">
|
||||
{{ session.full_transcript }}</pre
|
||||
>
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="text-center text-muted">No transcript available.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@ -1,329 +1,320 @@
|
||||
<!-- templates/dashboard/dashboard.html -->
|
||||
{% extends 'base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Dashboard | Chat Analytics{% endblock %}
|
||||
|
||||
{% extends 'base.html' %} {% load static %}
|
||||
{% block title %}
|
||||
Dashboard | Chat Analytics
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div
|
||||
class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"
|
||||
>
|
||||
<h1 class="h2">{{ selected_dashboard.name }}</h1>
|
||||
<div class="btn-toolbar mb-2 mb-md-0">
|
||||
<div class="btn-group me-2">
|
||||
<a
|
||||
href="{% url 'edit_dashboard' selected_dashboard.id %}"
|
||||
class="btn btn-sm btn-outline-secondary"
|
||||
>
|
||||
<i class="fas fa-edit"></i> Edit
|
||||
</a>
|
||||
<a
|
||||
href="{% url 'delete_dashboard' selected_dashboard.id %}"
|
||||
class="btn btn-sm btn-outline-danger"
|
||||
>
|
||||
<i class="fas fa-trash"></i> Delete
|
||||
</a>
|
||||
<a
|
||||
href="{% url 'export_chats_csv' %}?dashboard_id={{ selected_dashboard.id }}"
|
||||
class="btn btn-sm btn-outline-success"
|
||||
>
|
||||
<i class="fas fa-file-csv"></i> Export CSV
|
||||
</a>
|
||||
</div>
|
||||
<div class="dropdown">
|
||||
<button
|
||||
class="btn btn-sm btn-outline-primary dropdown-toggle"
|
||||
type="button"
|
||||
id="timeRangeDropdown"
|
||||
data-bs-toggle="dropdown"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<i class="fas fa-calendar"></i> Time Range
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="timeRangeDropdown">
|
||||
<li>
|
||||
<a
|
||||
class="dropdown-item"
|
||||
href="?dashboard_id={{ selected_dashboard.id }}&time_range=7"
|
||||
>Last 7 days</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
class="dropdown-item"
|
||||
href="?dashboard_id={{ selected_dashboard.id }}&time_range=30"
|
||||
>Last 30 days</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
class="dropdown-item"
|
||||
href="?dashboard_id={{ selected_dashboard.id }}&time_range=90"
|
||||
>Last 90 days</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
class="dropdown-item"
|
||||
href="?dashboard_id={{ selected_dashboard.id }}&time_range=all"
|
||||
>All time</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"
|
||||
>
|
||||
<h1 class="h2">{{ selected_dashboard.name }}</h1>
|
||||
<div class="btn-toolbar mb-2 mb-md-0">
|
||||
<div class="btn-group me-2">
|
||||
<a
|
||||
href="{% url 'edit_dashboard' selected_dashboard.id %}"
|
||||
class="btn btn-sm btn-outline-secondary"
|
||||
>
|
||||
<i class="fas fa-edit"></i> Edit
|
||||
</a>
|
||||
<a
|
||||
href="{% url 'delete_dashboard' selected_dashboard.id %}"
|
||||
class="btn btn-sm btn-outline-danger"
|
||||
>
|
||||
<i class="fas fa-trash"></i> Delete
|
||||
</a>
|
||||
<a
|
||||
href="{% url 'export_chats_csv' %}?dashboard_id={{ selected_dashboard.id }}"
|
||||
class="btn btn-sm btn-outline-success"
|
||||
>
|
||||
<i class="fas fa-file-csv"></i> Export CSV
|
||||
</a>
|
||||
</div>
|
||||
<div class="dropdown">
|
||||
<button
|
||||
class="btn btn-sm btn-outline-primary dropdown-toggle"
|
||||
type="button"
|
||||
id="timeRangeDropdown"
|
||||
data-bs-toggle="dropdown"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<i class="fas fa-calendar"></i> Time Range
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="timeRangeDropdown">
|
||||
<li>
|
||||
<a class="dropdown-item" href="?dashboard_id={{ selected_dashboard.id }}&time_range=7"
|
||||
>Last 7 days</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item" href="?dashboard_id={{ selected_dashboard.id }}&time_range=30"
|
||||
>Last 30 days</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item" href="?dashboard_id={{ selected_dashboard.id }}&time_range=90"
|
||||
>Last 90 days</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item" href="?dashboard_id={{ selected_dashboard.id }}&time_range=all"
|
||||
>All time</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-3">
|
||||
<div class="card stats-card bg-primary text-white">
|
||||
<div class="card-body">
|
||||
<h6 class="card-title">Total Sessions</h6>
|
||||
<h3>{{ dashboard_data.total_sessions }}</h3>
|
||||
<p>Chat conversations</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card stats-card bg-success text-white">
|
||||
<div class="card-body">
|
||||
<h6 class="card-title">Avg Response Time</h6>
|
||||
<h3>{{ dashboard_data.avg_response_time }}s</h3>
|
||||
<p>Average response</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card stats-card bg-info text-white">
|
||||
<div class="card-body">
|
||||
<h6 class="card-title">Total Tokens</h6>
|
||||
<h3>{{ dashboard_data.total_tokens }}</h3>
|
||||
<p>Total usage</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card stats-card bg-warning text-white">
|
||||
<div class="card-body">
|
||||
<h6 class="card-title">Total Cost</h6>
|
||||
<h3>€{{ dashboard_data.total_cost }}</h3>
|
||||
<p>Token cost</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-3">
|
||||
<div class="card stats-card bg-primary text-white">
|
||||
<div class="card-body">
|
||||
<h6 class="card-title">Total Sessions</h6>
|
||||
<h3>{{ dashboard_data.total_sessions }}</h3>
|
||||
<p>Chat conversations</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card stats-card bg-success text-white">
|
||||
<div class="card-body">
|
||||
<h6 class="card-title">Avg Response Time</h6>
|
||||
<h3>{{ dashboard_data.avg_response_time }}s</h3>
|
||||
<p>Average response</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card stats-card bg-info text-white">
|
||||
<div class="card-body">
|
||||
<h6 class="card-title">Total Tokens</h6>
|
||||
<h3>{{ dashboard_data.total_tokens }}</h3>
|
||||
<p>Total usage</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card stats-card bg-warning text-white">
|
||||
<div class="card-body">
|
||||
<h6 class="card-title">Total Cost</h6>
|
||||
<h3>€{{ dashboard_data.total_cost }}</h3>
|
||||
<p>Token cost</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Sessions Over Time</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="sessions-time-chart" class="chart-container"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Sentiment Analysis</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="sentiment-chart" class="chart-container"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Sessions Over Time</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="sessions-time-chart" class="chart-container"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Sentiment Analysis</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="sentiment-chart" class="chart-container"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-4">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Top Countries</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="country-chart" class="chart-container"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Categories</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="category-chart" class="chart-container"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-4">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Top Countries</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="country-chart" class="chart-container"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Categories</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="category-chart" class="chart-container"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
<!-- prettier-ignore-start -->
|
||||
<!-- Store the JSON data in script tags to avoid parsing issues -->
|
||||
<script type="application/json" id="time-series-data">{{ time_series_data_json|safe }}</script>
|
||||
<script type="application/json" id="sentiment-data">{{ sentiment_data_json|safe }}</script>
|
||||
<script type="application/json" id="country-data">{{ country_data_json|safe }}</script>
|
||||
<script type="application/json" id="category-data">{{ category_data_json|safe }}</script>
|
||||
<!-- prettier-ignore-end -->
|
||||
<!-- prettier-ignore-start -->
|
||||
<!-- Store the JSON data in script tags to avoid parsing issues -->
|
||||
<script type="application/json" id="time-series-data">
|
||||
{{ time_series_data_json|safe }}
|
||||
</script>
|
||||
<script type="application/json" id="sentiment-data">
|
||||
{{ sentiment_data_json|safe }}
|
||||
</script>
|
||||
<script type="application/json" id="country-data">
|
||||
{{ country_data_json|safe }}
|
||||
</script>
|
||||
<script type="application/json" id="category-data">
|
||||
{{ category_data_json|safe }}
|
||||
</script>
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
try {
|
||||
// Parse the dashboard data components from script tags
|
||||
const timeSeriesData = JSON.parse(
|
||||
document.getElementById("time-series-data").textContent,
|
||||
);
|
||||
const sentimentData = JSON.parse(
|
||||
document.getElementById("sentiment-data").textContent,
|
||||
);
|
||||
const countryData = JSON.parse(document.getElementById("country-data").textContent);
|
||||
const categoryData = JSON.parse(
|
||||
document.getElementById("category-data").textContent,
|
||||
);
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
try {
|
||||
// Parse the dashboard data components from script tags
|
||||
const timeSeriesData = JSON.parse(document.getElementById("time-series-data").textContent);
|
||||
const sentimentData = JSON.parse(document.getElementById("sentiment-data").textContent);
|
||||
const countryData = JSON.parse(document.getElementById("country-data").textContent);
|
||||
const categoryData = JSON.parse(document.getElementById("category-data").textContent);
|
||||
|
||||
console.log("Time series data loaded:", timeSeriesData);
|
||||
console.log("Sentiment data loaded:", sentimentData);
|
||||
console.log("Country data loaded:", countryData);
|
||||
console.log("Category data loaded:", categoryData);
|
||||
console.log("Time series data loaded:", timeSeriesData);
|
||||
console.log("Sentiment data loaded:", sentimentData);
|
||||
console.log("Country data loaded:", countryData);
|
||||
console.log("Category data loaded:", categoryData);
|
||||
|
||||
// Sessions over time chart
|
||||
if (timeSeriesData && timeSeriesData.length > 0) {
|
||||
const timeSeriesX = timeSeriesData.map((item) => item.date);
|
||||
const timeSeriesY = timeSeriesData.map((item) => item.count);
|
||||
// Sessions over time chart
|
||||
if (timeSeriesData && timeSeriesData.length > 0) {
|
||||
const timeSeriesX = timeSeriesData.map((item) => item.date);
|
||||
const timeSeriesY = timeSeriesData.map((item) => item.count);
|
||||
|
||||
Plotly.newPlot(
|
||||
"sessions-time-chart",
|
||||
[
|
||||
{
|
||||
x: timeSeriesX,
|
||||
y: timeSeriesY,
|
||||
type: "scatter",
|
||||
mode: "lines+markers",
|
||||
line: {
|
||||
color: "rgb(75, 192, 192)",
|
||||
width: 2,
|
||||
},
|
||||
marker: {
|
||||
color: "rgb(75, 192, 192)",
|
||||
size: 6,
|
||||
},
|
||||
},
|
||||
],
|
||||
{
|
||||
margin: { t: 10, r: 10, b: 40, l: 40 },
|
||||
xaxis: {
|
||||
title: "Date",
|
||||
},
|
||||
yaxis: {
|
||||
title: "Number of Sessions",
|
||||
},
|
||||
},
|
||||
);
|
||||
} else {
|
||||
document.getElementById("sessions-time-chart").innerHTML =
|
||||
'<div class="text-center py-5"><p class="text-muted">No time series data available</p></div>';
|
||||
}
|
||||
Plotly.newPlot(
|
||||
"sessions-time-chart",
|
||||
[
|
||||
{
|
||||
x: timeSeriesX,
|
||||
y: timeSeriesY,
|
||||
type: "scatter",
|
||||
mode: "lines+markers",
|
||||
line: {
|
||||
color: "rgb(75, 192, 192)",
|
||||
width: 2,
|
||||
},
|
||||
marker: {
|
||||
color: "rgb(75, 192, 192)",
|
||||
size: 6,
|
||||
},
|
||||
},
|
||||
],
|
||||
{
|
||||
margin: { t: 10, r: 10, b: 40, l: 40 },
|
||||
xaxis: {
|
||||
title: "Date",
|
||||
},
|
||||
yaxis: {
|
||||
title: "Number of Sessions",
|
||||
},
|
||||
},
|
||||
);
|
||||
} else {
|
||||
document.getElementById("sessions-time-chart").innerHTML =
|
||||
'<div class="text-center py-5"><p class="text-muted">No time series data available</p></div>';
|
||||
}
|
||||
|
||||
// Sentiment analysis chart
|
||||
if (sentimentData && sentimentData.length > 0) {
|
||||
const sentimentLabels = sentimentData.map((item) => item.sentiment);
|
||||
const sentimentValues = sentimentData.map((item) => item.count);
|
||||
const sentimentColors = sentimentLabels.map((sentiment) => {
|
||||
if (sentiment.toLowerCase().includes("positive")) return "rgb(75, 192, 92)";
|
||||
if (sentiment.toLowerCase().includes("negative"))
|
||||
return "rgb(255, 99, 132)";
|
||||
if (sentiment.toLowerCase().includes("neutral")) return "rgb(255, 205, 86)";
|
||||
return "rgb(201, 203, 207)";
|
||||
});
|
||||
// Sentiment analysis chart
|
||||
if (sentimentData && sentimentData.length > 0) {
|
||||
const sentimentLabels = sentimentData.map((item) => item.sentiment);
|
||||
const sentimentValues = sentimentData.map((item) => item.count);
|
||||
const sentimentColors = sentimentLabels.map((sentiment) => {
|
||||
if (sentiment.toLowerCase().includes("positive")) return "rgb(75, 192, 92)";
|
||||
if (sentiment.toLowerCase().includes("negative")) return "rgb(255, 99, 132)";
|
||||
if (sentiment.toLowerCase().includes("neutral")) return "rgb(255, 205, 86)";
|
||||
return "rgb(201, 203, 207)";
|
||||
});
|
||||
|
||||
Plotly.newPlot(
|
||||
"sentiment-chart",
|
||||
[
|
||||
{
|
||||
values: sentimentValues,
|
||||
labels: sentimentLabels,
|
||||
type: "pie",
|
||||
marker: {
|
||||
colors: sentimentColors,
|
||||
},
|
||||
hole: 0.4,
|
||||
textinfo: "label+percent",
|
||||
insidetextorientation: "radial",
|
||||
},
|
||||
],
|
||||
{
|
||||
margin: { t: 10, r: 10, b: 10, l: 10 },
|
||||
},
|
||||
);
|
||||
} else {
|
||||
document.getElementById("sentiment-chart").innerHTML =
|
||||
'<div class="text-center py-5"><p class="text-muted">No sentiment data available</p></div>';
|
||||
}
|
||||
Plotly.newPlot(
|
||||
"sentiment-chart",
|
||||
[
|
||||
{
|
||||
values: sentimentValues,
|
||||
labels: sentimentLabels,
|
||||
type: "pie",
|
||||
marker: {
|
||||
colors: sentimentColors,
|
||||
},
|
||||
hole: 0.4,
|
||||
textinfo: "label+percent",
|
||||
insidetextorientation: "radial",
|
||||
},
|
||||
],
|
||||
{
|
||||
margin: { t: 10, r: 10, b: 10, l: 10 },
|
||||
},
|
||||
);
|
||||
} else {
|
||||
document.getElementById("sentiment-chart").innerHTML =
|
||||
'<div class="text-center py-5"><p class="text-muted">No sentiment data available</p></div>';
|
||||
}
|
||||
|
||||
// Country chart
|
||||
if (countryData && countryData.length > 0) {
|
||||
const countryLabels = countryData.map((item) => item.country);
|
||||
const countryValues = countryData.map((item) => item.count);
|
||||
// Country chart
|
||||
if (countryData && countryData.length > 0) {
|
||||
const countryLabels = countryData.map((item) => item.country);
|
||||
const countryValues = countryData.map((item) => item.count);
|
||||
|
||||
Plotly.newPlot(
|
||||
"country-chart",
|
||||
[
|
||||
{
|
||||
x: countryValues,
|
||||
y: countryLabels,
|
||||
type: "bar",
|
||||
orientation: "h",
|
||||
marker: {
|
||||
color: "rgb(54, 162, 235)",
|
||||
},
|
||||
},
|
||||
],
|
||||
{
|
||||
margin: { t: 10, r: 10, b: 40, l: 100 },
|
||||
xaxis: {
|
||||
title: "Number of Sessions",
|
||||
},
|
||||
},
|
||||
);
|
||||
} else {
|
||||
document.getElementById("country-chart").innerHTML =
|
||||
'<div class="text-center py-5"><p class="text-muted">No country data available</p></div>';
|
||||
}
|
||||
Plotly.newPlot(
|
||||
"country-chart",
|
||||
[
|
||||
{
|
||||
x: countryValues,
|
||||
y: countryLabels,
|
||||
type: "bar",
|
||||
orientation: "h",
|
||||
marker: {
|
||||
color: "rgb(54, 162, 235)",
|
||||
},
|
||||
},
|
||||
],
|
||||
{
|
||||
margin: { t: 10, r: 10, b: 40, l: 100 },
|
||||
xaxis: {
|
||||
title: "Number of Sessions",
|
||||
},
|
||||
},
|
||||
);
|
||||
} else {
|
||||
document.getElementById("country-chart").innerHTML =
|
||||
'<div class="text-center py-5"><p class="text-muted">No country data available</p></div>';
|
||||
}
|
||||
|
||||
// Category chart
|
||||
if (categoryData && categoryData.length > 0) {
|
||||
const categoryLabels = categoryData.map((item) => item.category);
|
||||
const categoryValues = categoryData.map((item) => item.count);
|
||||
// Category chart
|
||||
if (categoryData && categoryData.length > 0) {
|
||||
const categoryLabels = categoryData.map((item) => item.category);
|
||||
const categoryValues = categoryData.map((item) => item.count);
|
||||
|
||||
Plotly.newPlot(
|
||||
"category-chart",
|
||||
[
|
||||
{
|
||||
labels: categoryLabels,
|
||||
values: categoryValues,
|
||||
type: "pie",
|
||||
textinfo: "label+percent",
|
||||
insidetextorientation: "radial",
|
||||
},
|
||||
],
|
||||
{
|
||||
margin: { t: 10, r: 10, b: 10, l: 10 },
|
||||
},
|
||||
);
|
||||
} else {
|
||||
document.getElementById("category-chart").innerHTML =
|
||||
'<div class="text-center py-5"><p class="text-muted">No category data available</p></div>';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error rendering charts:", error);
|
||||
document.querySelectorAll(".chart-container").forEach((container) => {
|
||||
container.innerHTML =
|
||||
'<div class="text-center py-5"><p class="text-danger">Error loading chart data. Please refresh the page.</p></div>';
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
Plotly.newPlot(
|
||||
"category-chart",
|
||||
[
|
||||
{
|
||||
labels: categoryLabels,
|
||||
values: categoryValues,
|
||||
type: "pie",
|
||||
textinfo: "label+percent",
|
||||
insidetextorientation: "radial",
|
||||
},
|
||||
],
|
||||
{
|
||||
margin: { t: 10, r: 10, b: 10, l: 10 },
|
||||
},
|
||||
);
|
||||
} else {
|
||||
document.getElementById("category-chart").innerHTML =
|
||||
'<div class="text-center py-5"><p class="text-muted">No category data available</p></div>';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error rendering charts:", error);
|
||||
document.querySelectorAll(".chart-container").forEach((container) => {
|
||||
container.innerHTML =
|
||||
'<div class="text-center py-5"><p class="text-danger">Error loading chart data. Please refresh the page.</p></div>';
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@ -1,44 +1,40 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}Delete Dashboard | Chat Analytics{% endblock %}
|
||||
|
||||
{% extends 'base.html' %} {% block title %}Delete Dashboard | Chat Analytics{% endblock %}
|
||||
{% block content %}
|
||||
<div
|
||||
class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"
|
||||
>
|
||||
<h1 class="h2">Delete Dashboard</h1>
|
||||
<div class="btn-toolbar mb-2 mb-md-0">
|
||||
<a href="{% url 'dashboard' %}" class="btn btn-sm btn-outline-secondary">
|
||||
<i class="fas fa-arrow-left"></i> Back to Dashboard
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"
|
||||
>
|
||||
<h1 class="h2">Delete Dashboard</h1>
|
||||
<div class="btn-toolbar mb-2 mb-md-0">
|
||||
<a href="{% url 'dashboard' %}" class="btn btn-sm btn-outline-secondary">
|
||||
<i class="fas fa-arrow-left"></i> Back to Dashboard
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<div class="card border-danger">
|
||||
<div class="card-header bg-danger text-white">
|
||||
<h5 class="card-title mb-0">Confirm Deletion</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="lead">
|
||||
Are you sure you want to delete the dashboard
|
||||
"<strong>{{ dashboard.name }}</strong>"?
|
||||
</p>
|
||||
<p>
|
||||
This action cannot be undone. The dashboard will be permanently deleted, but
|
||||
the underlying data sources will remain intact.
|
||||
</p>
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<div class="card border-danger">
|
||||
<div class="card-header bg-danger text-white">
|
||||
<h5 class="card-title mb-0">Confirm Deletion</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="lead">
|
||||
Are you sure you want to delete the dashboard "<strong>{{ dashboard.name }}</strong>"?
|
||||
</p>
|
||||
<p>
|
||||
This action cannot be undone. The dashboard will be permanently deleted, but the
|
||||
underlying data sources will remain intact.
|
||||
</p>
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<div class="d-flex justify-content-between mt-4">
|
||||
<a href="{% url 'dashboard' %}" class="btn btn-secondary">Cancel</a>
|
||||
<button type="submit" class="btn btn-danger">Delete Dashboard</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<div class="d-flex justify-content-between mt-4">
|
||||
<a href="{% url 'dashboard' %}" class="btn btn-secondary">Cancel</a>
|
||||
<button type="submit" class="btn btn-danger">Delete Dashboard</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@ -1,43 +1,43 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% extends 'base.html' %} {% load crispy_forms_tags %}
|
||||
{% block title %}
|
||||
{% if is_create %}Create Dashboard{% else %}Edit Dashboard{% endif %}
|
||||
| Chat Analytics
|
||||
{% if is_create %}
|
||||
Create Dashboard
|
||||
{% else %}
|
||||
Edit Dashboard
|
||||
{% endif %}
|
||||
| Chat Analytics
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div
|
||||
class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"
|
||||
>
|
||||
<h1 class="h2">{% if is_create %}Create Dashboard{% else %}Edit Dashboard{% endif %}</h1>
|
||||
<div class="btn-toolbar mb-2 mb-md-0">
|
||||
<a href="{% url 'dashboard' %}" class="btn btn-sm btn-outline-secondary">
|
||||
<i class="fas fa-arrow-left"></i> Back to Dashboard
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"
|
||||
>
|
||||
<h1 class="h2">{% if is_create %}Create Dashboard{% else %}Edit Dashboard{% endif %}</h1>
|
||||
<div class="btn-toolbar mb-2 mb-md-0">
|
||||
<a href="{% url 'dashboard' %}" class="btn btn-sm btn-outline-secondary">
|
||||
<i class="fas fa-arrow-left"></i> Back to Dashboard
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">
|
||||
{% if is_create %}Create Dashboard{% else %}Edit Dashboard{% endif %}
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
<div class="d-grid gap-2">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
{% if is_create %}Create Dashboard{% else %}Update Dashboard{% endif %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">
|
||||
{% if is_create %}Create Dashboard{% else %}Edit Dashboard{% endif %}
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="post">
|
||||
{% csrf_token %} {{ form|crispy }}
|
||||
<div class="d-grid gap-2">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
{% if is_create %}Create Dashboard{% else %}Update Dashboard{% endif %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@ -1,53 +1,47 @@
|
||||
<!-- templates/dashboard/data_source_confirm_delete.html -->
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}Delete Data Source | Chat Analytics{% endblock %}
|
||||
|
||||
{% extends 'base.html' %} {% block title %}Delete Data Source | Chat Analytics{% endblock %}
|
||||
{% block content %}
|
||||
<div
|
||||
class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"
|
||||
>
|
||||
<h1 class="h2">Delete Data Source</h1>
|
||||
<div class="btn-toolbar mb-2 mb-md-0">
|
||||
<a
|
||||
href="{% url 'data_source_detail' data_source.id %}"
|
||||
class="btn btn-sm btn-outline-secondary"
|
||||
>
|
||||
<i class="fas fa-arrow-left"></i> Back to Data Source
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"
|
||||
>
|
||||
<h1 class="h2">Delete Data Source</h1>
|
||||
<div class="btn-toolbar mb-2 mb-md-0">
|
||||
<a
|
||||
href="{% url 'data_source_detail' data_source.id %}"
|
||||
class="btn btn-sm btn-outline-secondary"
|
||||
>
|
||||
<i class="fas fa-arrow-left"></i> Back to Data Source
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<div class="card border-danger">
|
||||
<div class="card-header bg-danger text-white">
|
||||
<h5 class="card-title mb-0">Confirm Deletion</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="lead">
|
||||
Are you sure you want to delete the data source
|
||||
"<strong>{{ data_source.name }}</strong>"?
|
||||
</p>
|
||||
<p>
|
||||
This action cannot be undone. The data source and all associated chat
|
||||
sessions ({{ data_source.chat_sessions.count }} sessions) will be
|
||||
permanently deleted.
|
||||
</p>
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<div class="card border-danger">
|
||||
<div class="card-header bg-danger text-white">
|
||||
<h5 class="card-title mb-0">Confirm Deletion</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="lead">
|
||||
Are you sure you want to delete the data source
|
||||
"<strong>{{ data_source.name }}</strong>"?
|
||||
</p>
|
||||
<p>
|
||||
This action cannot be undone. The data source and all associated chat sessions
|
||||
({{ data_source.chat_sessions.count }} sessions) will be permanently deleted.
|
||||
</p>
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<div class="d-flex justify-content-between mt-4">
|
||||
<a
|
||||
href="{% url 'data_source_detail' data_source.id %}"
|
||||
class="btn btn-secondary"
|
||||
>Cancel</a
|
||||
>
|
||||
<button type="submit" class="btn btn-danger">Delete Data Source</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<div class="d-flex justify-content-between mt-4">
|
||||
<a href="{% url 'data_source_detail' data_source.id %}" class="btn btn-secondary"
|
||||
>Cancel</a
|
||||
>
|
||||
<button type="submit" class="btn btn-danger">Delete Data Source</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@ -1,255 +1,229 @@
|
||||
<!-- templates/dashboard/data_source_detail.html -->
|
||||
{% extends 'base.html' %}
|
||||
{% load dashboard_extras %}
|
||||
|
||||
{% block title %}{{ data_source.name }} | Chat Analytics{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div
|
||||
class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"
|
||||
>
|
||||
<h1 class="h2">{{ data_source.name }}</h1>
|
||||
<div class="btn-toolbar mb-2 mb-md-0">
|
||||
<a href="{% url 'upload_data' %}" class="btn btn-sm btn-outline-secondary me-2">
|
||||
<i class="fas fa-arrow-left"></i> Back to Data Sources
|
||||
</a>
|
||||
<a
|
||||
href="{% url 'export_chats_csv' %}?data_source_id={{ data_source.id }}"
|
||||
class="btn btn-sm btn-outline-success me-2"
|
||||
>
|
||||
<i class="fas fa-file-csv"></i> Export CSV
|
||||
</a>
|
||||
<a
|
||||
href="{% url 'delete_data_source' data_source.id %}"
|
||||
class="btn btn-sm btn-outline-danger"
|
||||
>
|
||||
<i class="fas fa-trash"></i> Delete
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Data Source Details</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<p><strong>Name:</strong> {{ data_source.name }}</p>
|
||||
<p>
|
||||
<strong>Uploaded At:</strong>
|
||||
{{ data_source.uploaded_at|date:"F d, Y H:i" }}
|
||||
</p>
|
||||
<p><strong>File:</strong> {{ data_source.file.name|split:"/"|last }}</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<p><strong>Company:</strong> {{ data_source.company.name }}</p>
|
||||
<p><strong>Total Sessions:</strong> {{ page_obj.paginator.count }}</p>
|
||||
<p><strong>Description:</strong> {{ data_source.description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Filter Sessions</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="get" action="{% url 'search_chat_sessions' %}">
|
||||
<div class="input-group mb-3">
|
||||
<input
|
||||
type="text"
|
||||
name="q"
|
||||
class="form-control"
|
||||
placeholder="Search sessions..."
|
||||
aria-label="Search sessions"
|
||||
/>
|
||||
<input
|
||||
type="hidden"
|
||||
name="data_source_id"
|
||||
value="{{ data_source.id }}"
|
||||
/>
|
||||
<button class="btn btn-outline-primary" type="submit">
|
||||
<i class="fas fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Chat Sessions ({{ page_obj.paginator.count }})</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Session ID</th>
|
||||
<th>Start Time</th>
|
||||
<th>Country</th>
|
||||
<th>Language</th>
|
||||
<th>Sentiment</th>
|
||||
<th>Messages</th>
|
||||
<th>Tokens</th>
|
||||
<th>Category</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for session in page_obj %}
|
||||
<tr>
|
||||
<td>{{ session.session_id|truncatechars:10 }}</td>
|
||||
<td>{{ session.start_time|date:"M d, Y H:i" }}</td>
|
||||
<td>{{ session.country }}</td>
|
||||
<td>{{ session.language }}</td>
|
||||
<td>
|
||||
{% if session.sentiment %}
|
||||
{% if 'positive' in session.sentiment|lower %}
|
||||
<span class="badge bg-success"
|
||||
>{{ session.sentiment }}</span
|
||||
>
|
||||
{% elif 'negative' in session.sentiment|lower %}
|
||||
<span class="badge bg-danger"
|
||||
>{{ session.sentiment }}</span
|
||||
>
|
||||
{% elif 'neutral' in session.sentiment|lower %}
|
||||
<span class="badge bg-warning"
|
||||
>{{ session.sentiment }}</span
|
||||
>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary"
|
||||
>{{ session.sentiment }}</span
|
||||
>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<span class="text-muted">N/A</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ session.messages_sent }}</td>
|
||||
<td>{{ session.tokens }}</td>
|
||||
<td>{{ session.category|default:"N/A" }}</td>
|
||||
<td>
|
||||
{% if session.session_id %}
|
||||
<a
|
||||
href="{% url 'chat_session_detail' session.session_id %}"
|
||||
class="btn btn-sm btn-outline-primary"
|
||||
>
|
||||
<i class="fas fa-eye"></i>
|
||||
</a>
|
||||
{% else %}
|
||||
<button
|
||||
class="btn btn-sm btn-outline-secondary"
|
||||
disabled
|
||||
>
|
||||
<i class="fas fa-eye-slash"></i>
|
||||
</button>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="9" class="text-center">
|
||||
No chat sessions found.
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{% if page_obj.paginator.num_pages > 1 %}
|
||||
<nav aria-label="Page navigation" class="mt-4">
|
||||
<ul class="pagination justify-content-center">
|
||||
{% if page_obj.has_previous %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page=1" aria-label="First">
|
||||
<span aria-hidden="true">««</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="page-item">
|
||||
<a
|
||||
class="page-link"
|
||||
href="?page={{ page_obj.previous_page_number }}"
|
||||
aria-label="Previous"
|
||||
>
|
||||
<span aria-hidden="true">«</span>
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#" aria-label="First">
|
||||
<span aria-hidden="true">««</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#" aria-label="Previous">
|
||||
<span aria-hidden="true">«</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% for num in page_obj.paginator.page_range %}
|
||||
{% if page_obj.number == num %}
|
||||
<li class="page-item active">
|
||||
<a class="page-link" href="?page={{ num }}"
|
||||
>{{ num }}</a
|
||||
>
|
||||
</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 }}"
|
||||
>{{ 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 }}"
|
||||
aria-label="Next"
|
||||
>
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="page-item">
|
||||
<a
|
||||
class="page-link"
|
||||
href="?page={{ page_obj.paginator.num_pages }}"
|
||||
aria-label="Last"
|
||||
>
|
||||
<span aria-hidden="true">»»</span>
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#" aria-label="Next">
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#" aria-label="Last">
|
||||
<span aria-hidden="true">»»</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% extends 'base.html' %} {% load dashboard_extras %}
|
||||
{% block title %}
|
||||
{{ data_source.name }}
|
||||
| Chat Analytics
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div
|
||||
class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"
|
||||
>
|
||||
<h1 class="h2">{{ data_source.name }}</h1>
|
||||
<div class="btn-toolbar mb-2 mb-md-0">
|
||||
<a href="{% url 'upload_data' %}" class="btn btn-sm btn-outline-secondary me-2">
|
||||
<i class="fas fa-arrow-left"></i> Back to Data Sources
|
||||
</a>
|
||||
<a
|
||||
href="{% url 'export_chats_csv' %}?data_source_id={{ data_source.id }}"
|
||||
class="btn btn-sm btn-outline-success me-2"
|
||||
>
|
||||
<i class="fas fa-file-csv"></i> Export CSV
|
||||
</a>
|
||||
<a href="{% url 'delete_data_source' data_source.id %}" class="btn btn-sm btn-outline-danger">
|
||||
<i class="fas fa-trash"></i> Delete
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Data Source Details</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<p><strong>Name:</strong> {{ data_source.name }}</p>
|
||||
<p>
|
||||
<strong>Uploaded At:</strong>
|
||||
{{ data_source.uploaded_at|date:"F d, Y H:i" }}
|
||||
</p>
|
||||
<p><strong>File:</strong> {{ data_source.file.name|split:"/"|last }}</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<p><strong>Company:</strong> {{ data_source.company.name }}</p>
|
||||
<p><strong>Total Sessions:</strong> {{ page_obj.paginator.count }}</p>
|
||||
<p><strong>Description:</strong> {{ data_source.description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Filter Sessions</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="get" action="{% url 'search_chat_sessions' %}">
|
||||
<div class="input-group mb-3">
|
||||
<input
|
||||
type="text"
|
||||
name="q"
|
||||
class="form-control"
|
||||
placeholder="Search sessions..."
|
||||
aria-label="Search sessions"
|
||||
/>
|
||||
<input type="hidden" name="data_source_id" value="{{ data_source.id }}" />
|
||||
<button class="btn btn-outline-primary" type="submit">
|
||||
<i class="fas fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Chat Sessions ({{ page_obj.paginator.count }})</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Session ID</th>
|
||||
<th>Start Time</th>
|
||||
<th>Country</th>
|
||||
<th>Language</th>
|
||||
<th>Sentiment</th>
|
||||
<th>Messages</th>
|
||||
<th>Tokens</th>
|
||||
<th>Category</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for session in page_obj %}
|
||||
<tr>
|
||||
<td>{{ session.session_id|truncatechars:10 }}</td>
|
||||
<td>{{ session.start_time|date:"M d, Y H:i" }}</td>
|
||||
<td>{{ session.country }}</td>
|
||||
<td>{{ session.language }}</td>
|
||||
<td>
|
||||
{% if session.sentiment %}
|
||||
{% if 'positive' in session.sentiment|lower %}
|
||||
<span class="badge bg-success">{{ session.sentiment }}</span>
|
||||
{% elif 'negative' in session.sentiment|lower %}
|
||||
<span class="badge bg-danger">{{ session.sentiment }}</span>
|
||||
{% elif 'neutral' in session.sentiment|lower %}
|
||||
<span class="badge bg-warning">{{ session.sentiment }}</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">{{ session.sentiment }}</span>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<span class="text-muted">N/A</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ session.messages_sent }}</td>
|
||||
<td>{{ session.tokens }}</td>
|
||||
<td>{{ session.category|default:"N/A" }}</td>
|
||||
<td>
|
||||
{% if session.session_id %}
|
||||
<a
|
||||
href="{% url 'chat_session_detail' session.session_id %}"
|
||||
class="btn btn-sm btn-outline-primary"
|
||||
>
|
||||
<i class="fas fa-eye"></i>
|
||||
</a>
|
||||
{% else %}
|
||||
<button class="btn btn-sm btn-outline-secondary" disabled>
|
||||
<i class="fas fa-eye-slash"></i>
|
||||
</button>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="9" class="text-center">No chat sessions found.</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{% if page_obj.paginator.num_pages > 1 %}
|
||||
<nav aria-label="Page navigation" class="mt-4">
|
||||
<ul class="pagination justify-content-center">
|
||||
{% if page_obj.has_previous %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page=1" aria-label="First">
|
||||
<span aria-hidden="true">««</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="page-item">
|
||||
<a
|
||||
class="page-link"
|
||||
href="?page={{ page_obj.previous_page_number }}"
|
||||
aria-label="Previous"
|
||||
>
|
||||
<span aria-hidden="true">«</span>
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#" aria-label="First">
|
||||
<span aria-hidden="true">««</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#" aria-label="Previous">
|
||||
<span aria-hidden="true">«</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% for num in page_obj.paginator.page_range %}
|
||||
{% if page_obj.number == num %}
|
||||
<li class="page-item active">
|
||||
<a class="page-link" href="?page={{ num }}">{{ num }}</a>
|
||||
</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 }}">{{ 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 }}"
|
||||
aria-label="Next"
|
||||
>
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="page-item">
|
||||
<a
|
||||
class="page-link"
|
||||
href="?page={{ page_obj.paginator.num_pages }}"
|
||||
aria-label="Last"
|
||||
>
|
||||
<span aria-hidden="true">»»</span>
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#" aria-label="Next">
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#" aria-label="Last">
|
||||
<span aria-hidden="true">»»</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@ -1,320 +1,276 @@
|
||||
<!-- templates/dashboard/data_view.html -->
|
||||
{% extends 'base.html' %}
|
||||
{% load dashboard_extras %}
|
||||
|
||||
{% block title %}Data View | Chat Analytics{% endblock %}
|
||||
|
||||
{% extends 'base.html' %} {% load dashboard_extras %}
|
||||
{% block title %}
|
||||
Data View | Chat Analytics
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div
|
||||
class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"
|
||||
>
|
||||
<h1 class="h2">Data View</h1>
|
||||
<div class="btn-toolbar mb-2 mb-md-0">
|
||||
<div class="btn-group me-2">
|
||||
<a
|
||||
href="{% url 'dashboard' %}"
|
||||
class="btn btn-sm btn-outline-secondary ajax-nav-link"
|
||||
>
|
||||
<i class="fas fa-arrow-left"></i> Back to Dashboard
|
||||
</a>
|
||||
{% if selected_data_source %}
|
||||
<a
|
||||
href="{% url 'data_source_detail' selected_data_source.id %}"
|
||||
class="btn btn-sm btn-outline-secondary ajax-nav-link"
|
||||
>
|
||||
<i class="fas fa-database"></i> View Source
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="dropdown">
|
||||
<button
|
||||
class="btn btn-sm btn-outline-primary dropdown-toggle"
|
||||
type="button"
|
||||
id="dataViewDropdown"
|
||||
data-bs-toggle="dropdown"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<i class="fas fa-filter"></i> Filter
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="dataViewDropdown">
|
||||
<li>
|
||||
<a class="dropdown-item ajax-nav-link" href="?view=all">All Sessions</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item ajax-nav-link" href="?view=recent"
|
||||
>Recent Sessions</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item ajax-nav-link" href="?view=positive"
|
||||
>Positive Sentiment</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item ajax-nav-link" href="?view=negative"
|
||||
>Negative Sentiment</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item ajax-nav-link" href="?view=escalated"
|
||||
>Escalated Sessions</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"
|
||||
>
|
||||
<h1 class="h2">Data View</h1>
|
||||
<div class="btn-toolbar mb-2 mb-md-0">
|
||||
<div class="btn-group me-2">
|
||||
<a href="{% url 'dashboard' %}" class="btn btn-sm btn-outline-secondary ajax-nav-link">
|
||||
<i class="fas fa-arrow-left"></i> Back to Dashboard
|
||||
</a>
|
||||
{% if selected_data_source %}
|
||||
<a
|
||||
href="{% url 'data_source_detail' selected_data_source.id %}"
|
||||
class="btn btn-sm btn-outline-secondary ajax-nav-link"
|
||||
>
|
||||
<i class="fas fa-database"></i> View Source
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="dropdown">
|
||||
<button
|
||||
class="btn btn-sm btn-outline-primary dropdown-toggle"
|
||||
type="button"
|
||||
id="dataViewDropdown"
|
||||
data-bs-toggle="dropdown"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<i class="fas fa-filter"></i> Filter
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="dataViewDropdown">
|
||||
<li>
|
||||
<a class="dropdown-item ajax-nav-link" href="?view=all">All Sessions</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item ajax-nav-link" href="?view=recent">Recent Sessions</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item ajax-nav-link" href="?view=positive">Positive Sentiment</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item ajax-nav-link" href="?view=negative">Negative Sentiment</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item ajax-nav-link" href="?view=escalated">Escalated Sessions</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Data Source Selection -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Data Source Selection</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="get" class="row g-3 align-items-center filter-form">
|
||||
<div class="col-md-6">
|
||||
<select
|
||||
name="data_source_id"
|
||||
class="form-select"
|
||||
aria-label="Select Data Source"
|
||||
>
|
||||
<option value="">All Data Sources</option>
|
||||
{% for ds in data_sources %}
|
||||
<option
|
||||
value="{{ ds.id }}"
|
||||
{% if selected_data_source.id == ds.id %}selected{% endif %}
|
||||
>
|
||||
{{ ds.name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<select name="view" class="form-select" aria-label="Select View">
|
||||
<option value="all" {% if view == 'all' %}selected{% endif %}>
|
||||
All Sessions
|
||||
</option>
|
||||
<option value="recent" {% if view == 'recent' %}selected{% endif %}>
|
||||
Recent Sessions
|
||||
</option>
|
||||
<option
|
||||
value="positive"
|
||||
{% if view == 'positive' %}selected{% endif %}
|
||||
>
|
||||
Positive Sentiment
|
||||
</option>
|
||||
<option
|
||||
value="negative"
|
||||
{% if view == 'negative' %}selected{% endif %}
|
||||
>
|
||||
Negative Sentiment
|
||||
</option>
|
||||
<option
|
||||
value="escalated"
|
||||
{% if view == 'escalated' %}selected{% endif %}
|
||||
>
|
||||
Escalated Sessions
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button type="submit" class="btn btn-primary w-100">Apply</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Data Source Selection -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Data Source Selection</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="get" class="row g-3 align-items-center filter-form">
|
||||
<div class="col-md-6">
|
||||
<select name="data_source_id" class="form-select" aria-label="Select Data Source">
|
||||
<option value="">All Data Sources</option>
|
||||
{% for ds in data_sources %}
|
||||
<option
|
||||
value="{{ ds.id }}"
|
||||
{% if selected_data_source.id == ds.id %}
|
||||
selected
|
||||
{% endif %}
|
||||
>
|
||||
{{ ds.name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<select name="view" class="form-select" aria-label="Select View">
|
||||
<option value="all" {% if view == 'all' %}selected{% endif %}>All Sessions</option>
|
||||
<option value="recent" {% if view == 'recent' %}selected{% endif %}>
|
||||
Recent Sessions
|
||||
</option>
|
||||
<option value="positive" {% if view == 'positive' %}selected{% endif %}>
|
||||
Positive Sentiment
|
||||
</option>
|
||||
<option value="negative" {% if view == 'negative' %}selected{% endif %}>
|
||||
Negative Sentiment
|
||||
</option>
|
||||
<option value="escalated" {% if view == 'escalated' %}selected{% endif %}>
|
||||
Escalated Sessions
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button type="submit" class="btn btn-primary w-100">Apply</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Export to CSV -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Export Data</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form
|
||||
id="export-form"
|
||||
method="get"
|
||||
action="{% url 'export_chats_csv' %}"
|
||||
class="row g-3"
|
||||
>
|
||||
<!-- Pass current filters to export -->
|
||||
<input
|
||||
type="hidden"
|
||||
name="data_source_id"
|
||||
value="{{ selected_data_source.id }}"
|
||||
/>
|
||||
<input type="hidden" name="view" value="{{ view }}" />
|
||||
<!-- Export to CSV -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Export Data</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form id="export-form" method="get" action="{% url 'export_chats_csv' %}" class="row g-3">
|
||||
<!-- Pass current filters to export -->
|
||||
<input type="hidden" name="data_source_id" value="{{ selected_data_source.id }}" />
|
||||
<input type="hidden" name="view" value="{{ view }}" />
|
||||
|
||||
<div class="col-md-3">
|
||||
<label for="start_date" class="form-label">Start Date</label>
|
||||
<input
|
||||
type="date"
|
||||
name="start_date"
|
||||
id="start_date"
|
||||
class="form-control"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label for="end_date" class="form-label">End Date</label>
|
||||
<input type="date" name="end_date" id="end_date" class="form-control" />
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label for="country" class="form-label">Country</label>
|
||||
<input
|
||||
type="text"
|
||||
name="country"
|
||||
id="country"
|
||||
class="form-control"
|
||||
placeholder="Country"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label for="sentiment" class="form-label">Sentiment</label>
|
||||
<select name="sentiment" id="sentiment" class="form-select">
|
||||
<option value="">All</option>
|
||||
<option value="positive">Positive</option>
|
||||
<option value="negative">Negative</option>
|
||||
<option value="neutral">Neutral</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label for="escalated" class="form-label">Escalated</label>
|
||||
<select name="escalated" id="escalated" class="form-select">
|
||||
<option value="">All</option>
|
||||
<option value="true">Yes</option>
|
||||
<option value="false">No</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3 d-flex align-items-end">
|
||||
<button type="submit" class="btn btn-success w-100">
|
||||
<i class="fas fa-file-csv me-1"></i> Export to CSV
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label for="start_date" class="form-label">Start Date</label>
|
||||
<input type="date" name="start_date" id="start_date" class="form-control" />
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label for="end_date" class="form-label">End Date</label>
|
||||
<input type="date" name="end_date" id="end_date" class="form-control" />
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label for="country" class="form-label">Country</label>
|
||||
<input
|
||||
type="text"
|
||||
name="country"
|
||||
id="country"
|
||||
class="form-control"
|
||||
placeholder="Country"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label for="sentiment" class="form-label">Sentiment</label>
|
||||
<select name="sentiment" id="sentiment" class="form-select">
|
||||
<option value="">All</option>
|
||||
<option value="positive">Positive</option>
|
||||
<option value="negative">Negative</option>
|
||||
<option value="neutral">Neutral</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label for="escalated" class="form-label">Escalated</label>
|
||||
<select name="escalated" id="escalated" class="form-select">
|
||||
<option value="">All</option>
|
||||
<option value="true">Yes</option>
|
||||
<option value="false">No</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3 d-flex align-items-end">
|
||||
<button type="submit" class="btn btn-success w-100">
|
||||
<i class="fas fa-file-csv me-1"></i> Export to CSV
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Data Table -->
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h5 class="card-title mb-0">
|
||||
Chat Sessions
|
||||
{% if selected_data_source %}
|
||||
for {{ selected_data_source.name }}
|
||||
{% endif %}
|
||||
{% if view != 'all' %}
|
||||
({{ view|title }})
|
||||
{% endif %}
|
||||
</h5>
|
||||
<span class="badge bg-primary">{{ page_obj.paginator.count }} sessions</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- Loading spinner shown during AJAX requests -->
|
||||
<div id="ajax-loading-spinner" class="text-center py-4 d-none">
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
<p class="mt-2">Loading data...</p>
|
||||
</div>
|
||||
<!-- Data Table -->
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h5 class="card-title mb-0">
|
||||
Chat Sessions
|
||||
{% if selected_data_source %}
|
||||
for {{ selected_data_source.name }}
|
||||
{% endif %}
|
||||
{% if view != 'all' %}({{ view|title }}){% endif %}
|
||||
</h5>
|
||||
<span class="badge bg-primary">{{ page_obj.paginator.count }} sessions</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- Loading spinner shown during AJAX requests -->
|
||||
<div id="ajax-loading-spinner" class="text-center py-4 d-none">
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
<p class="mt-2">Loading data...</p>
|
||||
</div>
|
||||
|
||||
<!-- Data table container that will be updated via AJAX -->
|
||||
<div id="ajax-content-container">
|
||||
{% include "dashboard/partials/data_table.html" %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Data table container that will be updated via AJAX -->
|
||||
<div id="ajax-content-container">{% include "dashboard/partials/data_table.html" %}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Data Summary -->
|
||||
{% if page_obj %}
|
||||
<div class="row mt-4">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Summary</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<div class="card stats-card bg-light">
|
||||
<div class="card-body">
|
||||
<h6 class="card-title">Total Sessions</h6>
|
||||
<h3>{{ page_obj.paginator.count }}</h3>
|
||||
<p>Chat conversations</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card stats-card bg-light">
|
||||
<div class="card-body">
|
||||
<h6 class="card-title">Avg Response Time</h6>
|
||||
<h3>{{ avg_response_time|floatformat:2 }}s</h3>
|
||||
<p>Average response</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card stats-card bg-light">
|
||||
<div class="card-body">
|
||||
<h6 class="card-title">Avg Messages</h6>
|
||||
<h3>{{ avg_messages|floatformat:1 }}</h3>
|
||||
<p>Per conversation</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card stats-card bg-light">
|
||||
<div class="card-body">
|
||||
<h6 class="card-title">Escalation Rate</h6>
|
||||
<h3>{{ escalation_rate|floatformat:1 }}%</h3>
|
||||
<p>Escalated sessions</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<!-- Data Summary -->
|
||||
{% if page_obj %}
|
||||
<div class="row mt-4">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Summary</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<div class="card stats-card bg-light">
|
||||
<div class="card-body">
|
||||
<h6 class="card-title">Total Sessions</h6>
|
||||
<h3>{{ page_obj.paginator.count }}</h3>
|
||||
<p>Chat conversations</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card stats-card bg-light">
|
||||
<div class="card-body">
|
||||
<h6 class="card-title">Avg Response Time</h6>
|
||||
<h3>{{ avg_response_time|floatformat:2 }}s</h3>
|
||||
<p>Average response</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card stats-card bg-light">
|
||||
<div class="card-body">
|
||||
<h6 class="card-title">Avg Messages</h6>
|
||||
<h3>{{ avg_messages|floatformat:1 }}</h3>
|
||||
<p>Per conversation</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card stats-card bg-light">
|
||||
<div class="card-body">
|
||||
<h6 class="card-title">Escalation Rate</h6>
|
||||
<h3>{{ escalation_rate|floatformat:1 }}%</h3>
|
||||
<p>Escalated sessions</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
<script>
|
||||
// Function to update the summary section with new data
|
||||
function updateSummary(data) {
|
||||
if (document.querySelector(".stats-card h3:nth-of-type(1)")) {
|
||||
document.querySelector(".stats-card h3:nth-of-type(1)").textContent =
|
||||
data.page_obj.paginator.count;
|
||||
}
|
||||
if (document.querySelector(".stats-card h3:nth-of-type(2)")) {
|
||||
document.querySelector(".stats-card h3:nth-of-type(2)").textContent =
|
||||
data.avg_response_time !== null && data.avg_response_time !== undefined
|
||||
? data.avg_response_time.toFixed(2) + "s"
|
||||
: "0.00s";
|
||||
}
|
||||
if (document.querySelector(".stats-card h3:nth-of-type(3)")) {
|
||||
document.querySelector(".stats-card h3:nth-of-type(3)").textContent =
|
||||
data.avg_messages !== null && data.avg_messages !== undefined
|
||||
? data.avg_messages.toFixed(1)
|
||||
: "0.0";
|
||||
}
|
||||
if (document.querySelector(".stats-card h3:nth-of-type(4)")) {
|
||||
document.querySelector(".stats-card h3:nth-of-type(4)").textContent =
|
||||
data.escalation_rate !== null && data.escalation_rate !== undefined
|
||||
? data.escalation_rate.toFixed(1) + "%"
|
||||
: "0.0%";
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
// Function to update the summary section with new data
|
||||
function updateSummary(data) {
|
||||
if (document.querySelector(".stats-card h3:nth-of-type(1)")) {
|
||||
document.querySelector(".stats-card h3:nth-of-type(1)").textContent =
|
||||
data.page_obj.paginator.count;
|
||||
}
|
||||
if (document.querySelector(".stats-card h3:nth-of-type(2)")) {
|
||||
document.querySelector(".stats-card h3:nth-of-type(2)").textContent =
|
||||
data.avg_response_time !== null && data.avg_response_time !== undefined
|
||||
? data.avg_response_time.toFixed(2) + "s"
|
||||
: "0.00s";
|
||||
}
|
||||
if (document.querySelector(".stats-card h3:nth-of-type(3)")) {
|
||||
document.querySelector(".stats-card h3:nth-of-type(3)").textContent =
|
||||
data.avg_messages !== null && data.avg_messages !== undefined
|
||||
? data.avg_messages.toFixed(1)
|
||||
: "0.0";
|
||||
}
|
||||
if (document.querySelector(".stats-card h3:nth-of-type(4)")) {
|
||||
document.querySelector(".stats-card h3:nth-of-type(4)").textContent =
|
||||
data.escalation_rate !== null && data.escalation_rate !== undefined
|
||||
? data.escalation_rate.toFixed(1) + "%"
|
||||
: "0.0%";
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@ -1,44 +1,37 @@
|
||||
<!-- templates/dashboard/no_company.html -->
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}No Company | Chat Analytics{% endblock %}
|
||||
|
||||
{% extends 'base.html' %} {% block title %}No Company | Chat Analytics{% endblock %}
|
||||
{% block content %}
|
||||
<div
|
||||
class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"
|
||||
>
|
||||
<h1 class="h2">No Company Association</h1>
|
||||
</div>
|
||||
<div
|
||||
class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"
|
||||
>
|
||||
<h1 class="h2">No Company Association</h1>
|
||||
</div>
|
||||
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header bg-warning text-dark">
|
||||
<h5 class="card-title mb-0">Account Not Associated with a Company</h5>
|
||||
</div>
|
||||
<div class="card-body text-center">
|
||||
<div class="mb-4">
|
||||
<i class="fas fa-building fa-4x text-warning mb-3"></i>
|
||||
<h4>You are not currently associated with any company</h4>
|
||||
<p class="lead">
|
||||
You need to be associated with a company to access the dashboard.
|
||||
</p>
|
||||
</div>
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header bg-warning text-dark">
|
||||
<h5 class="card-title mb-0">Account Not Associated with a Company</h5>
|
||||
</div>
|
||||
<div class="card-body text-center">
|
||||
<div class="mb-4">
|
||||
<i class="fas fa-building fa-4x text-warning mb-3"></i>
|
||||
<h4>You are not currently associated with any company</h4>
|
||||
<p class="lead">You need to be associated with a company to access the dashboard.</p>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
Please contact an administrator to have your account assigned to a company.
|
||||
Once your account is associated with a company, you'll be able to access the
|
||||
dashboard and its features.
|
||||
</p>
|
||||
<p>
|
||||
Please contact an administrator to have your account assigned to a company. Once your
|
||||
account is associated with a company, you'll be able to access the dashboard and its
|
||||
features.
|
||||
</p>
|
||||
|
||||
<div class="mt-4">
|
||||
<a href="{% url 'profile' %}" class="btn btn-primary">View Your Profile</a>
|
||||
<a href="{% url 'logout' %}" class="btn btn-outline-secondary ms-2"
|
||||
>Logout</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<a href="{% url 'profile' %}" class="btn btn-primary">View Your Profile</a>
|
||||
<a href="{% url 'logout' %}" class="btn btn-outline-secondary ms-2">Logout</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@ -1,160 +1,156 @@
|
||||
<!-- templates/dashboard/partials/data_table.html -->
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Session ID</th>
|
||||
<th>Start Time</th>
|
||||
<th>Country</th>
|
||||
<th>Language</th>
|
||||
<th>Messages</th>
|
||||
<th>Sentiment</th>
|
||||
<th>Response Time</th>
|
||||
<th>Category</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for session in page_obj %}
|
||||
<tr>
|
||||
<td>{{ session.session_id|truncatechars:10 }}</td>
|
||||
<td>{{ session.start_time|date:"M d, Y H:i" }}</td>
|
||||
<td>{{ session.country|default:"N/A" }}</td>
|
||||
<td>{{ session.language|default:"N/A" }}</td>
|
||||
<td>{{ session.messages_sent }}</td>
|
||||
<td>
|
||||
{% if session.sentiment %}
|
||||
{% if 'positive' in session.sentiment|lower %}
|
||||
<span class="badge bg-success">{{ session.sentiment }}</span>
|
||||
{% elif 'negative' in session.sentiment|lower %}
|
||||
<span class="badge bg-danger">{{ session.sentiment }}</span>
|
||||
{% elif 'neutral' in session.sentiment|lower %}
|
||||
<span class="badge bg-warning">{{ session.sentiment }}</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">{{ session.sentiment }}</span>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<span class="text-muted">N/A</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ session.avg_response_time|floatformat:2 }}s</td>
|
||||
<td>{{ session.category|default:"N/A" }}</td>
|
||||
<td>
|
||||
{% if session.session_id %}
|
||||
<a
|
||||
href="{% url 'chat_session_detail' session.session_id %}"
|
||||
class="btn btn-sm btn-outline-primary ajax-nav-link"
|
||||
>
|
||||
<i class="fas fa-eye"></i>
|
||||
</a>
|
||||
{% else %}
|
||||
<button class="btn btn-sm btn-outline-secondary" disabled>
|
||||
<i class="fas fa-eye-slash"></i>
|
||||
</button>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="9" class="text-center">No chat sessions found.</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Session ID</th>
|
||||
<th>Start Time</th>
|
||||
<th>Country</th>
|
||||
<th>Language</th>
|
||||
<th>Messages</th>
|
||||
<th>Sentiment</th>
|
||||
<th>Response Time</th>
|
||||
<th>Category</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for session in page_obj %}
|
||||
<tr>
|
||||
<td>{{ session.session_id|truncatechars:10 }}</td>
|
||||
<td>{{ session.start_time|date:"M d, Y H:i" }}</td>
|
||||
<td>{{ session.country|default:"N/A" }}</td>
|
||||
<td>{{ session.language|default:"N/A" }}</td>
|
||||
<td>{{ session.messages_sent }}</td>
|
||||
<td>
|
||||
{% if session.sentiment %}
|
||||
{% if 'positive' in session.sentiment|lower %}
|
||||
<span class="badge bg-success">{{ session.sentiment }}</span>
|
||||
{% elif 'negative' in session.sentiment|lower %}
|
||||
<span class="badge bg-danger">{{ session.sentiment }}</span>
|
||||
{% elif 'neutral' in session.sentiment|lower %}
|
||||
<span class="badge bg-warning">{{ session.sentiment }}</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">{{ session.sentiment }}</span>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<span class="text-muted">N/A</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ session.avg_response_time|floatformat:2 }}s</td>
|
||||
<td>{{ session.category|default:"N/A" }}</td>
|
||||
<td>
|
||||
{% if session.session_id %}
|
||||
<a
|
||||
href="{% url 'chat_session_detail' session.session_id %}"
|
||||
class="btn btn-sm btn-outline-primary ajax-nav-link"
|
||||
>
|
||||
<i class="fas fa-eye"></i>
|
||||
</a>
|
||||
{% else %}
|
||||
<button class="btn btn-sm btn-outline-secondary" disabled>
|
||||
<i class="fas fa-eye-slash"></i>
|
||||
</button>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="9" class="text-center">No chat sessions found.</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{% if page_obj.paginator.num_pages > 1 %}
|
||||
<nav aria-label="Page navigation" class="mt-4" id="pagination-container">
|
||||
<ul class="pagination justify-content-center">
|
||||
{% if page_obj.has_previous %}
|
||||
<li class="page-item">
|
||||
<a
|
||||
class="page-link pagination-link"
|
||||
data-page="1"
|
||||
href="?{% if selected_data_source %}data_source_id={{ selected_data_source.id }}&{% endif %}{% if view %}view={{ view }}&{% endif %}page=1"
|
||||
aria-label="First"
|
||||
>
|
||||
<span aria-hidden="true">««</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="page-item">
|
||||
<a
|
||||
class="page-link pagination-link"
|
||||
data-page="{{ page_obj.previous_page_number }}"
|
||||
href="?{% if selected_data_source %}data_source_id={{ selected_data_source.id }}&{% endif %}{% if view %}view={{ view }}&{% endif %}page={{ page_obj.previous_page_number }}"
|
||||
aria-label="Previous"
|
||||
>
|
||||
<span aria-hidden="true">«</span>
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#" aria-label="First">
|
||||
<span aria-hidden="true">««</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#" aria-label="Previous">
|
||||
<span aria-hidden="true">«</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% for num in page_obj.paginator.page_range %}
|
||||
{% if page_obj.number == num %}
|
||||
<li class="page-item active">
|
||||
<a
|
||||
class="page-link pagination-link"
|
||||
data-page="{{ num }}"
|
||||
href="?{% if selected_data_source %}data_source_id={{ selected_data_source.id }}&{% endif %}{% if view %}view={{ view }}&{% endif %}page={{ num }}"
|
||||
>{{ num }}</a
|
||||
>
|
||||
</li>
|
||||
{% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
|
||||
<li class="page-item">
|
||||
<a
|
||||
class="page-link pagination-link"
|
||||
data-page="{{ num }}"
|
||||
href="?{% if selected_data_source %}data_source_id={{ selected_data_source.id }}&{% endif %}{% if view %}view={{ view }}&{% endif %}page={{ num }}"
|
||||
>{{ num }}</a
|
||||
>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if page_obj.has_next %}
|
||||
<li class="page-item">
|
||||
<a
|
||||
class="page-link pagination-link"
|
||||
data-page="{{ page_obj.next_page_number }}"
|
||||
href="?{% if selected_data_source %}data_source_id={{ selected_data_source.id }}&{% endif %}{% if view %}view={{ view }}&{% endif %}page={{ page_obj.next_page_number }}"
|
||||
aria-label="Next"
|
||||
>
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="page-item">
|
||||
<a
|
||||
class="page-link pagination-link"
|
||||
data-page="{{ page_obj.paginator.num_pages }}"
|
||||
href="?{% if selected_data_source %}data_source_id={{ selected_data_source.id }}&{% endif %}{% if view %}view={{ view }}&{% endif %}page={{ page_obj.paginator.num_pages }}"
|
||||
aria-label="Last"
|
||||
>
|
||||
<span aria-hidden="true">»»</span>
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#" aria-label="Next">
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#" aria-label="Last">
|
||||
<span aria-hidden="true">»»</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
<nav aria-label="Page navigation" class="mt-4" id="pagination-container">
|
||||
<ul class="pagination justify-content-center">
|
||||
{% if page_obj.has_previous %}
|
||||
<li class="page-item">
|
||||
<a
|
||||
class="page-link pagination-link"
|
||||
data-page="1"
|
||||
href="?{% if selected_data_source %}data_source_id={{ selected_data_source.id }}&{% endif %}{% if view %}view={{ view }}&{% endif %}page=1"
|
||||
aria-label="First"
|
||||
>
|
||||
<span aria-hidden="true">««</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="page-item">
|
||||
<a
|
||||
class="page-link pagination-link"
|
||||
data-page="{{ page_obj.previous_page_number }}"
|
||||
href="?{% if selected_data_source %}data_source_id={{ selected_data_source.id }}&{% endif %}{% if view %}view={{ view }}&{% endif %}page={{ page_obj.previous_page_number }}"
|
||||
aria-label="Previous"
|
||||
>
|
||||
<span aria-hidden="true">«</span>
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#" aria-label="First">
|
||||
<span aria-hidden="true">««</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#" aria-label="Previous">
|
||||
<span aria-hidden="true">«</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% for num in page_obj.paginator.page_range %}{% if page_obj.number == num %}
|
||||
<li class="page-item active">
|
||||
<a
|
||||
class="page-link pagination-link"
|
||||
data-page="{{ num }}"
|
||||
href="?{% if selected_data_source %}data_source_id={{ selected_data_source.id }}&{% endif %}{% if view %}view={{ view }}&{% endif %}page={{ num }}"
|
||||
>{{ num }}</a
|
||||
>
|
||||
</li>
|
||||
{% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
|
||||
<li class="page-item">
|
||||
<a
|
||||
class="page-link pagination-link"
|
||||
data-page="{{ num }}"
|
||||
href="?{% if selected_data_source %}data_source_id={{ selected_data_source.id }}&{% endif %}{% if view %}view={{ view }}&{% endif %}page={{ num }}"
|
||||
>{{ num }}</a
|
||||
>
|
||||
</li>
|
||||
{% endif %}{% endfor %}
|
||||
{% if page_obj.has_next %}
|
||||
<li class="page-item">
|
||||
<a
|
||||
class="page-link pagination-link"
|
||||
data-page="{{ page_obj.next_page_number }}"
|
||||
href="?{% if selected_data_source %}data_source_id={{ selected_data_source.id }}&{% endif %}{% if view %}view={{ view }}&{% endif %}page={{ page_obj.next_page_number }}"
|
||||
aria-label="Next"
|
||||
>
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="page-item">
|
||||
<a
|
||||
class="page-link pagination-link"
|
||||
data-page="{{ page_obj.paginator.num_pages }}"
|
||||
href="?{% if selected_data_source %}data_source_id={{ selected_data_source.id }}&{% endif %}{% if view %}view={{ view }}&{% endif %}page={{ page_obj.paginator.num_pages }}"
|
||||
aria-label="Last"
|
||||
>
|
||||
<span aria-hidden="true">»»</span>
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#" aria-label="Next">
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#" aria-label="Last">
|
||||
<span aria-hidden="true">»»</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
{% endif %}
|
||||
|
||||
@ -1,168 +1,160 @@
|
||||
<!-- templates/dashboard/partials/search_results_table.html -->
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Session ID</th>
|
||||
<th>Start Time</th>
|
||||
<th>Data Source</th>
|
||||
<th>Country</th>
|
||||
<th>Language</th>
|
||||
<th>Sentiment</th>
|
||||
<th>Messages</th>
|
||||
<th>Category</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for session in page_obj %}
|
||||
<tr>
|
||||
<td>{{ session.session_id|truncatechars:10 }}</td>
|
||||
<td>{{ session.start_time|date:"M d, Y H:i" }}</td>
|
||||
<td>
|
||||
<a
|
||||
href="{% url 'data_source_detail' session.data_source.id %}"
|
||||
class="ajax-nav-link"
|
||||
>{{ session.data_source.name|truncatechars:15 }}</a
|
||||
>
|
||||
</td>
|
||||
<td>{{ session.country }}</td>
|
||||
<td>{{ session.language }}</td>
|
||||
<td>
|
||||
{% if session.sentiment %}
|
||||
{% if 'positive' in session.sentiment|lower %}
|
||||
<span class="badge bg-success">{{ session.sentiment }}</span>
|
||||
{% elif 'negative' in session.sentiment|lower %}
|
||||
<span class="badge bg-danger">{{ session.sentiment }}</span>
|
||||
{% elif 'neutral' in session.sentiment|lower %}
|
||||
<span class="badge bg-warning">{{ session.sentiment }}</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">{{ session.sentiment }}</span>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<span class="text-muted">N/A</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ session.messages_sent }}</td>
|
||||
<td>{{ session.category|default:"N/A" }}</td>
|
||||
<td>
|
||||
{% if session.session_id %}
|
||||
<a
|
||||
href="{% url 'chat_session_detail' session.session_id %}"
|
||||
class="btn btn-sm btn-outline-primary"
|
||||
>
|
||||
<i class="fas fa-eye"></i>
|
||||
</a>
|
||||
{% else %}
|
||||
<button class="btn btn-sm btn-outline-secondary" disabled>
|
||||
<i class="fas fa-eye-slash"></i>
|
||||
</button>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="9" class="text-center">
|
||||
No chat sessions found matching your criteria.
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Session ID</th>
|
||||
<th>Start Time</th>
|
||||
<th>Data Source</th>
|
||||
<th>Country</th>
|
||||
<th>Language</th>
|
||||
<th>Sentiment</th>
|
||||
<th>Messages</th>
|
||||
<th>Category</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for session in page_obj %}
|
||||
<tr>
|
||||
<td>{{ session.session_id|truncatechars:10 }}</td>
|
||||
<td>{{ session.start_time|date:"M d, Y H:i" }}</td>
|
||||
<td>
|
||||
<a href="{% url 'data_source_detail' session.data_source.id %}" class="ajax-nav-link"
|
||||
>{{ session.data_source.name|truncatechars:15 }}</a
|
||||
>
|
||||
</td>
|
||||
<td>{{ session.country }}</td>
|
||||
<td>{{ session.language }}</td>
|
||||
<td>
|
||||
{% if session.sentiment %}
|
||||
{% if 'positive' in session.sentiment|lower %}
|
||||
<span class="badge bg-success">{{ session.sentiment }}</span>
|
||||
{% elif 'negative' in session.sentiment|lower %}
|
||||
<span class="badge bg-danger">{{ session.sentiment }}</span>
|
||||
{% elif 'neutral' in session.sentiment|lower %}
|
||||
<span class="badge bg-warning">{{ session.sentiment }}</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">{{ session.sentiment }}</span>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<span class="text-muted">N/A</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ session.messages_sent }}</td>
|
||||
<td>{{ session.category|default:"N/A" }}</td>
|
||||
<td>
|
||||
{% if session.session_id %}
|
||||
<a
|
||||
href="{% url 'chat_session_detail' session.session_id %}"
|
||||
class="btn btn-sm btn-outline-primary"
|
||||
>
|
||||
<i class="fas fa-eye"></i>
|
||||
</a>
|
||||
{% else %}
|
||||
<button class="btn btn-sm btn-outline-secondary" disabled>
|
||||
<i class="fas fa-eye-slash"></i>
|
||||
</button>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="9" class="text-center">No chat sessions found matching your criteria.</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{% if page_obj.paginator.num_pages > 1 %}
|
||||
<nav aria-label="Page navigation" class="mt-4" id="pagination-container">
|
||||
<ul class="pagination justify-content-center">
|
||||
{% if page_obj.has_previous %}
|
||||
<li class="page-item">
|
||||
<a
|
||||
class="page-link pagination-link"
|
||||
data-page="1"
|
||||
href="?{% if query %}q={{ query }}&{% endif %}{% if data_source %}data_source_id={{ data_source.id }}&{% endif %}page=1"
|
||||
aria-label="First"
|
||||
>
|
||||
<span aria-hidden="true">««</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="page-item">
|
||||
<a
|
||||
class="page-link pagination-link"
|
||||
data-page="{{ page_obj.previous_page_number }}"
|
||||
href="?{% if query %}q={{ query }}&{% endif %}{% if data_source %}data_source_id={{ data_source.id }}&{% endif %}page={{ page_obj.previous_page_number }}"
|
||||
aria-label="Previous"
|
||||
>
|
||||
<span aria-hidden="true">«</span>
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#" aria-label="First">
|
||||
<span aria-hidden="true">««</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#" aria-label="Previous">
|
||||
<span aria-hidden="true">«</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% for num in page_obj.paginator.page_range %}
|
||||
{% if page_obj.number == num %}
|
||||
<li class="page-item active">
|
||||
<a
|
||||
class="page-link pagination-link"
|
||||
data-page="{{ num }}"
|
||||
href="?{% if query %}q={{ query }}&{% endif %}{% if data_source %}data_source_id={{ data_source.id }}&{% endif %}page={{ num }}"
|
||||
>{{ num }}</a
|
||||
>
|
||||
</li>
|
||||
{% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
|
||||
<li class="page-item">
|
||||
<a
|
||||
class="page-link pagination-link"
|
||||
data-page="{{ num }}"
|
||||
href="?{% if query %}q={{ query }}&{% endif %}{% if data_source %}data_source_id={{ data_source.id }}&{% endif %}page={{ num }}"
|
||||
>{{ num }}</a
|
||||
>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if page_obj.has_next %}
|
||||
<li class="page-item">
|
||||
<a
|
||||
class="page-link pagination-link"
|
||||
data-page="{{ page_obj.next_page_number }}"
|
||||
href="?{% if query %}q={{ query }}&{% endif %}{% if data_source %}data_source_id={{ data_source.id }}&{% endif %}page={{ page_obj.next_page_number }}"
|
||||
aria-label="Next"
|
||||
>
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="page-item">
|
||||
<a
|
||||
class="page-link pagination-link"
|
||||
data-page="{{ page_obj.paginator.num_pages }}"
|
||||
href="?{% if query %}q={{ query }}&{% endif %}{% if data_source %}data_source_id={{ data_source.id }}&{% endif %}page={{ page_obj.paginator.num_pages }}"
|
||||
aria-label="Last"
|
||||
>
|
||||
<span aria-hidden="true">»»</span>
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#" aria-label="Next">
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#" aria-label="Last">
|
||||
<span aria-hidden="true">»»</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
<nav aria-label="Page navigation" class="mt-4" id="pagination-container">
|
||||
<ul class="pagination justify-content-center">
|
||||
{% if page_obj.has_previous %}
|
||||
<li class="page-item">
|
||||
<a
|
||||
class="page-link pagination-link"
|
||||
data-page="1"
|
||||
href="?{% if query %}q={{ query }}&{% endif %}{% if data_source %}data_source_id={{ data_source.id }}&{% endif %}page=1"
|
||||
aria-label="First"
|
||||
>
|
||||
<span aria-hidden="true">««</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="page-item">
|
||||
<a
|
||||
class="page-link pagination-link"
|
||||
data-page="{{ page_obj.previous_page_number }}"
|
||||
href="?{% if query %}q={{ query }}&{% endif %}{% if data_source %}data_source_id={{ data_source.id }}&{% endif %}page={{ page_obj.previous_page_number }}"
|
||||
aria-label="Previous"
|
||||
>
|
||||
<span aria-hidden="true">«</span>
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#" aria-label="First">
|
||||
<span aria-hidden="true">««</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#" aria-label="Previous">
|
||||
<span aria-hidden="true">«</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% for num in page_obj.paginator.page_range %}{% if page_obj.number == num %}
|
||||
<li class="page-item active">
|
||||
<a
|
||||
class="page-link pagination-link"
|
||||
data-page="{{ num }}"
|
||||
href="?{% if query %}q={{ query }}&{% endif %}{% if data_source %}data_source_id={{ data_source.id }}&{% endif %}page={{ num }}"
|
||||
>{{ num }}</a
|
||||
>
|
||||
</li>
|
||||
{% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
|
||||
<li class="page-item">
|
||||
<a
|
||||
class="page-link pagination-link"
|
||||
data-page="{{ num }}"
|
||||
href="?{% if query %}q={{ query }}&{% endif %}{% if data_source %}data_source_id={{ data_source.id }}&{% endif %}page={{ num }}"
|
||||
>{{ num }}</a
|
||||
>
|
||||
</li>
|
||||
{% endif %}{% endfor %}
|
||||
{% if page_obj.has_next %}
|
||||
<li class="page-item">
|
||||
<a
|
||||
class="page-link pagination-link"
|
||||
data-page="{{ page_obj.next_page_number }}"
|
||||
href="?{% if query %}q={{ query }}&{% endif %}{% if data_source %}data_source_id={{ data_source.id }}&{% endif %}page={{ page_obj.next_page_number }}"
|
||||
aria-label="Next"
|
||||
>
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="page-item">
|
||||
<a
|
||||
class="page-link pagination-link"
|
||||
data-page="{{ page_obj.paginator.num_pages }}"
|
||||
href="?{% if query %}q={{ query }}&{% endif %}{% if data_source %}data_source_id={{ data_source.id }}&{% endif %}page={{ page_obj.paginator.num_pages }}"
|
||||
aria-label="Last"
|
||||
>
|
||||
<span aria-hidden="true">»»</span>
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#" aria-label="Next">
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#" aria-label="Last">
|
||||
<span aria-hidden="true">»»</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
{% endif %}
|
||||
|
||||
@ -1,93 +1,81 @@
|
||||
<!-- templates/dashboard/search_results.html -->
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}Search Results | Chat Analytics{% endblock %}
|
||||
|
||||
{% extends 'base.html' %} {% block title %}Search Results | Chat Analytics{% endblock %}
|
||||
{% block content %}
|
||||
<div
|
||||
class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"
|
||||
>
|
||||
<h1 class="h2">Search Results</h1>
|
||||
<div class="btn-toolbar mb-2 mb-md-0">
|
||||
<a href="{% url 'dashboard' %}" class="btn btn-sm btn-outline-secondary ajax-nav-link">
|
||||
<i class="fas fa-arrow-left"></i> Back to Dashboard
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"
|
||||
>
|
||||
<h1 class="h2">Search Results</h1>
|
||||
<div class="btn-toolbar mb-2 mb-md-0">
|
||||
<a href="{% url 'dashboard' %}" class="btn btn-sm btn-outline-secondary ajax-nav-link">
|
||||
<i class="fas fa-arrow-left"></i> Back to Dashboard
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Search Chat Sessions</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form
|
||||
method="get"
|
||||
action="{% url 'search_chat_sessions' %}"
|
||||
class="search-form"
|
||||
>
|
||||
<div class="input-group">
|
||||
<input
|
||||
type="text"
|
||||
name="q"
|
||||
class="form-control"
|
||||
placeholder="Search sessions..."
|
||||
value="{{ query }}"
|
||||
aria-label="Search sessions"
|
||||
/>
|
||||
{% if data_source %}
|
||||
<input
|
||||
type="hidden"
|
||||
name="data_source_id"
|
||||
value="{{ data_source.id }}"
|
||||
/>
|
||||
{% endif %}
|
||||
<button class="btn btn-outline-primary" type="submit">
|
||||
<i class="fas fa-search"></i> Search
|
||||
</button>
|
||||
</div>
|
||||
<div class="mt-2 text-muted">
|
||||
<small
|
||||
>Search by session ID, country, language, sentiment, category, or
|
||||
message content.</small
|
||||
>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Search Chat Sessions</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="get" action="{% url 'search_chat_sessions' %}" class="search-form">
|
||||
<div class="input-group">
|
||||
<input
|
||||
type="text"
|
||||
name="q"
|
||||
class="form-control"
|
||||
placeholder="Search sessions..."
|
||||
value="{{ query }}"
|
||||
aria-label="Search sessions"
|
||||
/>
|
||||
{% if data_source %}
|
||||
<input type="hidden" name="data_source_id" value="{{ data_source.id }}" />
|
||||
{% endif %}
|
||||
<button class="btn btn-outline-primary" type="submit">
|
||||
<i class="fas fa-search"></i> Search
|
||||
</button>
|
||||
</div>
|
||||
<div class="mt-2 text-muted">
|
||||
<small
|
||||
>Search by session ID, country, language, sentiment, category, or message
|
||||
content.</small
|
||||
>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">
|
||||
Results {% if query %}for "{{ query }}"{% endif %}
|
||||
{% if data_source %}in {{ data_source.name }}{% endif %}
|
||||
({{ page_obj.paginator.count }})
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- Loading spinner shown during AJAX requests -->
|
||||
<div id="ajax-loading-spinner" class="text-center py-4 d-none">
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
<p class="mt-2">Loading data...</p>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">
|
||||
Results {% if query %}for "{{ query }}"{% endif %}
|
||||
{% if data_source %}in {{ data_source.name }}{% endif %}
|
||||
({{ page_obj.paginator.count }})
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- Loading spinner shown during AJAX requests -->
|
||||
<div id="ajax-loading-spinner" class="text-center py-4 d-none">
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
<p class="mt-2">Loading data...</p>
|
||||
</div>
|
||||
|
||||
<!-- Search results container that will be updated via AJAX -->
|
||||
<div id="ajax-content-container">
|
||||
{% include "dashboard/partials/search_results_table.html" %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Search results container that will be updated via AJAX -->
|
||||
<div id="ajax-content-container">
|
||||
{% include "dashboard/partials/search_results_table.html" %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
<!-- No need for extra JavaScript here, using common ajax-pagination.js -->
|
||||
<!-- No need for extra JavaScript here, using common ajax-pagination.js -->
|
||||
{% endblock %}
|
||||
|
||||
@ -1,197 +1,195 @@
|
||||
<!-- templates/dashboard/upload.html -->
|
||||
{% extends 'base.html' %}
|
||||
{% load crispy_forms_tags %}
|
||||
{% load dashboard_extras %}
|
||||
|
||||
{% block title %}Upload Data | Chat Analytics{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div
|
||||
class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"
|
||||
>
|
||||
<h1 class="h2">Upload Data</h1>
|
||||
<div class="btn-toolbar mb-2 mb-md-0">
|
||||
<a href="{% url 'dashboard' %}" class="btn btn-sm btn-outline-secondary">
|
||||
<i class="fas fa-arrow-left"></i> Back to Dashboard
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Upload CSV File</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
{% csrf_token %} {{ form|crispy }}
|
||||
<div class="d-grid gap-2">
|
||||
<button type="submit" class="btn btn-primary">Upload</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">CSV File Format</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>The CSV file should contain the following columns:</p>
|
||||
<table class="table table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Column</th>
|
||||
<th>Description</th>
|
||||
<th>Type</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>session_id</td>
|
||||
<td>Unique identifier for the chat session</td>
|
||||
<td>String</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>start_time</td>
|
||||
<td>When the session started</td>
|
||||
<td>Datetime</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>end_time</td>
|
||||
<td>When the session ended</td>
|
||||
<td>Datetime</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ip_address</td>
|
||||
<td>IP address of the user</td>
|
||||
<td>String</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>country</td>
|
||||
<td>Country of the user</td>
|
||||
<td>String</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>language</td>
|
||||
<td>Language used in the conversation</td>
|
||||
<td>String</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>messages_sent</td>
|
||||
<td>Number of messages in the conversation</td>
|
||||
<td>Integer</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>sentiment</td>
|
||||
<td>Sentiment analysis of the conversation</td>
|
||||
<td>String</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>escalated</td>
|
||||
<td>Whether the conversation was escalated</td>
|
||||
<td>Boolean</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>forwarded_hr</td>
|
||||
<td>Whether the conversation was forwarded to HR</td>
|
||||
<td>Boolean</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>full_transcript</td>
|
||||
<td>Full transcript of the conversation</td>
|
||||
<td>Text</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>avg_response_time</td>
|
||||
<td>Average response time in seconds</td>
|
||||
<td>Float</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>tokens</td>
|
||||
<td>Total number of tokens used</td>
|
||||
<td>Integer</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>tokens_eur</td>
|
||||
<td>Cost of tokens in EUR</td>
|
||||
<td>Float</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>category</td>
|
||||
<td>Category of the conversation</td>
|
||||
<td>String</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>initial_msg</td>
|
||||
<td>First message from the user</td>
|
||||
<td>Text</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>user_rating</td>
|
||||
<td>User rating of the conversation</td>
|
||||
<td>String</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if data_sources %}
|
||||
<div class="row mt-4">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Uploaded Data Sources</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
<th>Uploaded</th>
|
||||
<th>File</th>
|
||||
<th>Sessions</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for data_source in data_sources %}
|
||||
<tr>
|
||||
<td>{{ data_source.name }}</td>
|
||||
<td>{{ data_source.description|truncatechars:50 }}</td>
|
||||
<td>{{ data_source.uploaded_at|date:"M d, Y H:i" }}</td>
|
||||
<td>{{ data_source.file.name|split:"/"|last }}</td>
|
||||
<td>{{ data_source.chat_sessions.count }}</td>
|
||||
<td>
|
||||
<a
|
||||
href="{% url 'data_source_detail' data_source.id %}"
|
||||
class="btn btn-sm btn-outline-primary"
|
||||
>
|
||||
<i class="fas fa-eye"></i>
|
||||
</a>
|
||||
<a
|
||||
href="{% url 'delete_data_source' data_source.id %}"
|
||||
class="btn btn-sm btn-outline-danger"
|
||||
>
|
||||
<i class="fas fa-trash"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% extends 'base.html' %} {% load crispy_forms_tags %} {% load dashboard_extras %}
|
||||
{% block title %}
|
||||
Upload Data | Chat Analytics
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div
|
||||
class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"
|
||||
>
|
||||
<h1 class="h2">Upload Data</h1>
|
||||
<div class="btn-toolbar mb-2 mb-md-0">
|
||||
<a href="{% url 'dashboard' %}" class="btn btn-sm btn-outline-secondary">
|
||||
<i class="fas fa-arrow-left"></i> Back to Dashboard
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Upload CSV File</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
{% csrf_token %} {{ form|crispy }}
|
||||
<div class="d-grid gap-2">
|
||||
<button type="submit" class="btn btn-primary">Upload</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">CSV File Format</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>The CSV file should contain the following columns:</p>
|
||||
<table class="table table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Column</th>
|
||||
<th>Description</th>
|
||||
<th>Type</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>session_id</td>
|
||||
<td>Unique identifier for the chat session</td>
|
||||
<td>String</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>start_time</td>
|
||||
<td>When the session started</td>
|
||||
<td>Datetime</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>end_time</td>
|
||||
<td>When the session ended</td>
|
||||
<td>Datetime</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ip_address</td>
|
||||
<td>IP address of the user</td>
|
||||
<td>String</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>country</td>
|
||||
<td>Country of the user</td>
|
||||
<td>String</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>language</td>
|
||||
<td>Language used in the conversation</td>
|
||||
<td>String</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>messages_sent</td>
|
||||
<td>Number of messages in the conversation</td>
|
||||
<td>Integer</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>sentiment</td>
|
||||
<td>Sentiment analysis of the conversation</td>
|
||||
<td>String</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>escalated</td>
|
||||
<td>Whether the conversation was escalated</td>
|
||||
<td>Boolean</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>forwarded_hr</td>
|
||||
<td>Whether the conversation was forwarded to HR</td>
|
||||
<td>Boolean</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>full_transcript</td>
|
||||
<td>Full transcript of the conversation</td>
|
||||
<td>Text</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>avg_response_time</td>
|
||||
<td>Average response time in seconds</td>
|
||||
<td>Float</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>tokens</td>
|
||||
<td>Total number of tokens used</td>
|
||||
<td>Integer</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>tokens_eur</td>
|
||||
<td>Cost of tokens in EUR</td>
|
||||
<td>Float</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>category</td>
|
||||
<td>Category of the conversation</td>
|
||||
<td>String</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>initial_msg</td>
|
||||
<td>First message from the user</td>
|
||||
<td>Text</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>user_rating</td>
|
||||
<td>User rating of the conversation</td>
|
||||
<td>String</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if data_sources %}
|
||||
<div class="row mt-4">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Uploaded Data Sources</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
<th>Uploaded</th>
|
||||
<th>File</th>
|
||||
<th>Sessions</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for data_source in data_sources %}
|
||||
<tr>
|
||||
<td>{{ data_source.name }}</td>
|
||||
<td>{{ data_source.description|truncatechars:50 }}</td>
|
||||
<td>{{ data_source.uploaded_at|date:"M d, Y H:i" }}</td>
|
||||
<td>{{ data_source.file.name|split:"/"|last }}</td>
|
||||
<td>{{ data_source.chat_sessions.count }}</td>
|
||||
<td>
|
||||
<a
|
||||
href="{% url 'data_source_detail' data_source.id %}"
|
||||
class="btn btn-sm btn-outline-primary"
|
||||
>
|
||||
<i class="fas fa-eye"></i>
|
||||
</a>
|
||||
<a
|
||||
href="{% url 'delete_data_source' data_source.id %}"
|
||||
class="btn btn-sm btn-outline-danger"
|
||||
>
|
||||
<i class="fas fa-trash"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user