404 lines
18 KiB
HTML
404 lines
18 KiB
HTML
{% extends "base.html" %}
|
|
{% load static %}
|
|
|
|
{% block title %}Account Dashboard{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="d-flex align-items-center mb-3">
|
|
<div>
|
|
<ol class="breadcrumb">
|
|
<li class="breadcrumb-item"><a href="{% url 'core:dashboard' %}">Dashboard</a></li>
|
|
<li class="breadcrumb-item active">Account Management</li>
|
|
</ol>
|
|
<h1 class="page-header mb-0">Account Dashboard</h1>
|
|
</div>
|
|
<div class="ms-auto">
|
|
<a href="{% url 'accounts:user_form' %}" class="btn btn-primary">
|
|
<i class="fas fa-user-plus me-2"></i>Add User
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Statistics Cards -->
|
|
<div class="row mb-4">
|
|
<div class="col-xl-3 col-md-6">
|
|
<div class="card bg-primary text-white">
|
|
<div class="card-body">
|
|
<div class="d-flex align-items-center">
|
|
<div class="flex-grow-1">
|
|
<div class="fs-6 text-white-75">Total Users</div>
|
|
<div class="fs-2 fw-bold">{{ total_users|default:0 }}</div>
|
|
</div>
|
|
<div class="flex-shrink-0">
|
|
<i class="fas fa-users fa-2x text-white-50"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-xl-3 col-md-6">
|
|
<div class="card bg-success text-white">
|
|
<div class="card-body">
|
|
<div class="d-flex align-items-center">
|
|
<div class="flex-grow-1">
|
|
<div class="fs-6 text-white-75">Active Users</div>
|
|
<div class="fs-2 fw-bold">{{ active_users|default:0 }}</div>
|
|
</div>
|
|
<div class="flex-shrink-0">
|
|
<i class="fas fa-user-check fa-2x text-white-50"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-xl-3 col-md-6">
|
|
<div class="card bg-warning text-white">
|
|
<div class="card-body">
|
|
<div class="d-flex align-items-center">
|
|
<div class="flex-grow-1">
|
|
<div class="fs-6 text-white-75">Active Sessions</div>
|
|
<div class="fs-2 fw-bold">{{ active_sessions|default:0 }}</div>
|
|
</div>
|
|
<div class="flex-shrink-0">
|
|
<i class="fas fa-desktop fa-2x text-white-50"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-xl-3 col-md-6">
|
|
<div class="card bg-info text-white">
|
|
<div class="card-body">
|
|
<div class="d-flex align-items-center">
|
|
<div class="flex-grow-1">
|
|
<div class="fs-6 text-white-75">New This Month</div>
|
|
<div class="fs-2 fw-bold">{{ new_users_month|default:0 }}</div>
|
|
</div>
|
|
<div class="flex-shrink-0">
|
|
<i class="fas fa-user-plus fa-2x text-white-50"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<!-- Quick Actions -->
|
|
<div class="col-xl-4 col-lg-6">
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<h4 class="card-title">
|
|
<i class="fas fa-bolt me-2"></i>
|
|
Quick Actions
|
|
</h4>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="d-grid gap-2">
|
|
<a href="{% url 'accounts:user_form' %}" class="btn btn-outline-primary">
|
|
<i class="fas fa-user-plus me-2"></i>Create New User
|
|
</a>
|
|
<a href="{% url 'accounts:user_list' %}" class="btn btn-outline-secondary">
|
|
<i class="fas fa-users me-2"></i>Manage Users
|
|
</a>
|
|
<a href="{% url 'accounts:session_list' %}" class="btn btn-outline-info">
|
|
<i class="fas fa-desktop me-2"></i>View Sessions
|
|
</a>
|
|
<a href="{% url 'accounts:session_management' %}" class="btn btn-outline-warning">
|
|
<i class="fas fa-cogs me-2"></i>Session Management
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- User Statistics -->
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<h4 class="card-title">
|
|
<i class="fas fa-chart-pie me-2"></i>
|
|
User Statistics
|
|
</h4>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="mb-3">
|
|
<div class="d-flex justify-content-between mb-1">
|
|
<span>Active Users</span>
|
|
<span>{{ active_percentage|default:0 }}%</span>
|
|
</div>
|
|
<div class="progress">
|
|
<div class="progress-bar bg-success" style="width: {{ active_percentage|default:0 }}%"></div>
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<div class="d-flex justify-content-between mb-1">
|
|
<span>Staff Members</span>
|
|
<span>{{ staff_percentage|default:0 }}%</span>
|
|
</div>
|
|
<div class="progress">
|
|
<div class="progress-bar bg-primary" style="width: {{ staff_percentage|default:0 }}%"></div>
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<div class="d-flex justify-content-between mb-1">
|
|
<span>Administrators</span>
|
|
<span>{{ admin_percentage|default:0 }}%</span>
|
|
</div>
|
|
<div class="progress">
|
|
<div class="progress-bar bg-warning" style="width: {{ admin_percentage|default:0 }}%"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Recent Users -->
|
|
<div class="col-xl-4 col-lg-6">
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<h4 class="card-title">
|
|
<i class="fas fa-user-clock me-2"></i>
|
|
Recent Users
|
|
</h4>
|
|
<div class="card-toolbar">
|
|
<a href="{% url 'accounts:user_list' %}" class="btn btn-sm btn-outline-primary">
|
|
View All
|
|
</a>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
{% if recent_users %}
|
|
{% for user in recent_users %}
|
|
<div class="d-flex align-items-center mb-3">
|
|
<div class="flex-shrink-0">
|
|
<div class="avatar avatar-sm">
|
|
{% if user.profile.avatar %}
|
|
<img src="{{ user.profile.avatar.url }}" alt="{{ user.get_full_name }}" class="rounded-circle">
|
|
{% else %}
|
|
<div class="avatar-initial rounded-circle bg-primary">
|
|
{{ user.first_name|first|upper }}{{ user.last_name|first|upper }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="flex-grow-1 ms-3">
|
|
<div class="fw-bold">{{ user.get_full_name|default:user.username }}</div>
|
|
<div class="small text-muted">{{ user.email }}</div>
|
|
<div class="small text-muted">
|
|
Joined {{ user.date_joined|timesince }} ago
|
|
</div>
|
|
</div>
|
|
<div class="flex-shrink-0">
|
|
{% if user.is_active %}
|
|
<span class="badge bg-success">Active</span>
|
|
{% else %}
|
|
<span class="badge bg-secondary">Inactive</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
{% else %}
|
|
<div class="text-center text-muted py-4">
|
|
<i class="fas fa-users fa-3x mb-3"></i>
|
|
<div>No recent users</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Active Sessions -->
|
|
<div class="col-xl-4">
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<h4 class="card-title">
|
|
<i class="fas fa-desktop me-2"></i>
|
|
Active Sessions
|
|
</h4>
|
|
<div class="card-toolbar">
|
|
<a href="{% url 'accounts:session_list' %}" class="btn btn-sm btn-outline-primary">
|
|
View All
|
|
</a>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
{% if active_sessions_list %}
|
|
{% for session in active_sessions_list %}
|
|
<div class="d-flex align-items-center mb-3">
|
|
<div class="flex-shrink-0">
|
|
<i class="fas fa-{{ session.device_icon|default:'desktop' }} fa-lg text-primary"></i>
|
|
</div>
|
|
<div class="flex-grow-1 ms-3">
|
|
<div class="fw-bold">{{ session.user.get_full_name|default:session.user.username }}</div>
|
|
<div class="small text-muted">{{ session.ip_address }}</div>
|
|
<div class="small text-muted">
|
|
Active for {{ session.last_activity|timesince }}
|
|
</div>
|
|
</div>
|
|
<div class="flex-shrink-0">
|
|
<span class="badge bg-success">Online</span>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
{% else %}
|
|
<div class="text-center text-muted py-4">
|
|
<i class="fas fa-desktop fa-3x mb-3"></i>
|
|
<div>No active sessions</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- System Status -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h4 class="card-title">
|
|
<i class="fas fa-server me-2"></i>
|
|
System Status
|
|
</h4>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<span>Authentication Service</span>
|
|
<span class="badge bg-success">
|
|
<i class="fas fa-check me-1"></i>Online
|
|
</span>
|
|
</div>
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<span>Session Management</span>
|
|
<span class="badge bg-success">
|
|
<i class="fas fa-check me-1"></i>Online
|
|
</span>
|
|
</div>
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<span>User Directory</span>
|
|
<span class="badge bg-success">
|
|
<i class="fas fa-check me-1"></i>Online
|
|
</span>
|
|
</div>
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<span>Security Monitoring</span>
|
|
<span class="badge bg-success">
|
|
<i class="fas fa-check me-1"></i>Active
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Recent Activity -->
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h4 class="card-title">
|
|
<i class="fas fa-history me-2"></i>
|
|
Recent Activity
|
|
</h4>
|
|
<div class="card-toolbar">
|
|
<div class="btn-group btn-group-sm">
|
|
<button type="button" class="btn btn-outline-secondary active" onclick="filterActivity('all')">All</button>
|
|
<button type="button" class="btn btn-outline-secondary" onclick="filterActivity('login')">Logins</button>
|
|
<button type="button" class="btn btn-outline-secondary" onclick="filterActivity('user')">User Changes</button>
|
|
<button type="button" class="btn btn-outline-secondary" onclick="filterActivity('security')">Security</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
{% if recent_activity %}
|
|
<div class="table-responsive">
|
|
<table class="table table-hover">
|
|
<thead>
|
|
<tr>
|
|
<th>Time</th>
|
|
<th>User</th>
|
|
<th>Action</th>
|
|
<th>Details</th>
|
|
<th>IP Address</th>
|
|
<th>Status</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for activity in recent_activity %}
|
|
<tr data-activity-type="{{ activity.type }}">
|
|
<td>
|
|
<div class="small">{{ activity.timestamp|date:"M d, Y" }}</div>
|
|
<div class="small text-muted">{{ activity.timestamp|time:"H:i" }}</div>
|
|
</td>
|
|
<td>
|
|
<div class="d-flex align-items-center">
|
|
<div class="avatar avatar-xs me-2">
|
|
{% if activity.user.profile.avatar %}
|
|
<img src="{{ activity.user.profile.avatar.url }}" alt="{{ activity.user.get_full_name }}" class="rounded-circle">
|
|
{% else %}
|
|
<div class="avatar-initial rounded-circle bg-secondary">
|
|
{{ activity.user.first_name|first|upper }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
<div>
|
|
<div class="fw-bold">{{ activity.user.get_full_name|default:activity.user.username }}</div>
|
|
<div class="small text-muted">{{ activity.user.email }}</div>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<i class="fas fa-{{ activity.icon }} me-2 text-{{ activity.color }}"></i>
|
|
{{ activity.action }}
|
|
</td>
|
|
<td>{{ activity.details|default:"-" }}</td>
|
|
<td>
|
|
<code>{{ activity.ip_address|default:"-" }}</code>
|
|
</td>
|
|
<td>
|
|
<span class="badge bg-{{ activity.status_color }}">
|
|
{{ activity.status }}
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% else %}
|
|
<div class="text-center text-muted py-5">
|
|
<i class="fas fa-history fa-3x mb-3"></i>
|
|
<h5>No Recent Activity</h5>
|
|
<p>User activity will appear here once users start interacting with the system.</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Auto-refresh data every 30 seconds
|
|
setInterval(function() {
|
|
// In a real implementation, you would fetch updated data via AJAX
|
|
console.log('Refreshing dashboard data...');
|
|
}, 30000);
|
|
});
|
|
|
|
function filterActivity(type) {
|
|
const rows = document.querySelectorAll('[data-activity-type]');
|
|
const buttons = document.querySelectorAll('.btn-group .btn');
|
|
|
|
// Update button states
|
|
buttons.forEach(btn => btn.classList.remove('active'));
|
|
event.target.classList.add('active');
|
|
|
|
// Filter rows
|
|
rows.forEach(row => {
|
|
if (type === 'all' || row.dataset.activityType === type) {
|
|
row.style.display = '';
|
|
} else {
|
|
row.style.display = 'none';
|
|
}
|
|
});
|
|
}
|
|
</script>
|
|
{% endblock %}
|
|
|