Marwan Alwali a710d1c4d8 update
2025-09-11 19:01:55 +03:00

397 lines
16 KiB
HTML

{% extends "base.html" %}
{% load static %}
{% block title %}{{ user_profile.get_full_name }} - User Details - {{ block.super }}{% endblock %}
{% block css %}
<style>
.user-avatar {
width: 120px;
height: 120px;
border-radius: 50%;
object-fit: cover;
border: 4px solid #fff;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
.user-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 2rem 0;
margin-bottom: 2rem;
}
.status-badge {
font-size: 0.875rem;
padding: 0.375rem 0.75rem;
}
.info-card {
background: white;
border-radius: 0.5rem;
padding: 1.5rem;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
margin-bottom: 1.5rem;
}
.info-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.75rem 0;
border-bottom: 1px solid #f1f3f4;
}
.info-item:last-child {
border-bottom: none;
}
.info-label {
font-weight: 600;
color: #495057;
min-width: 140px;
}
.info-value {
color: #212529;
text-align: right;
flex: 1;
}
.session-item {
background: #f8f9fa;
border-radius: 0.375rem;
padding: 1rem;
margin-bottom: 0.75rem;
border-left: 4px solid #28a745;
}
.session-item.expired {
border-left-color: #dc3545;
background: #fff5f5;
}
.device-item {
background: #f8f9fa;
border-radius: 0.375rem;
padding: 1rem;
margin-bottom: 0.75rem;
border-left: 4px solid #007bff;
}
.activity-timeline {
position: relative;
padding-left: 2rem;
}
.activity-timeline::before {
content: '';
position: absolute;
left: 0.75rem;
top: 0;
bottom: 0;
width: 2px;
background: #dee2e6;
}
.activity-item {
position: relative;
background: white;
border-radius: 0.375rem;
padding: 1rem;
margin-bottom: 1rem;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.activity-item::before {
content: '';
position: absolute;
left: -1.5rem;
top: 1rem;
width: 12px;
height: 12px;
border-radius: 50%;
background: #007bff;
border: 3px solid white;
box-shadow: 0 0 0 2px #dee2e6;
}
</style>
{% endblock %}
{% block content %}
<div class="user-header">
<div class="container-fluid">
<div class="row align-items-center">
<div class="col-auto">
{% if user_profile.profile_picture %}
<img src="{{ user_profile.profile_picture.url }}" alt="{{ user_profile.get_full_name }}" class="user-avatar">
{% else %}
<div class="user-avatar bg-secondary d-flex align-items-center justify-content-center">
<i class="fas fa-user fa-3x text-white"></i>
</div>
{% endif %}
</div>
<div class="col">
<h1 class="mb-2">{{ user_profile.get_full_name }}</h1>
<p class="mb-2 opacity-75">{{ user_profile.role|title }} • {{ user_profile.department|default:"No Department" }}</p>
<div class="d-flex flex-wrap gap-2">
{% if user_profile.is_active %}
<span class="badge bg-success status-badge">
<i class="fas fa-check-circle me-1"></i>Active
</span>
{% else %}
<span class="badge bg-danger status-badge">
<i class="fas fa-times-circle me-1"></i>Inactive
</span>
{% endif %}
{% if user_profile.is_approved %}
<span class="badge bg-info status-badge">
<i class="fas fa-shield-check me-1"></i>Approved
</span>
{% else %}
<span class="badge bg-warning status-badge">
<i class="fas fa-clock me-1"></i>Pending Approval
</span>
{% endif %}
{% if user_profile.two_factor_enabled %}
<span class="badge bg-primary status-badge">
<i class="fas fa-lock me-1"></i>2FA Enabled
</span>
{% endif %}
</div>
</div>
<div class="col-auto">
<div class="btn-group">
<button class="btn btn-light" onclick="window.print()">
<i class="fas fa-print me-1"></i>Print
</button>
<button class="btn btn-light" data-bs-toggle="modal" data-bs-target="#editUserModal">
<i class="fas fa-edit me-1"></i>Edit
</button>
<div class="btn-group">
<button class="btn btn-light dropdown-toggle" data-bs-toggle="dropdown">
<i class="fas fa-ellipsis-v"></i>
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#"><i class="fas fa-key me-2"></i>Reset Password</a></li>
<li><a class="dropdown-item" href="#"><i class="fas fa-ban me-2"></i>Suspend Account</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item text-danger" href="#"><i class="fas fa-trash me-2"></i>Delete Account</a></li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="container-fluid">
<div class="row">
<!-- User Information -->
<div class="col-lg-4">
<div class="info-card">
<h5 class="mb-3">
<i class="fas fa-user me-2"></i>Personal Information
</h5>
<div class="info-item">
<span class="info-label">Employee ID</span>
<span class="info-value">{{ user_profile.employee_id|default:"Not set" }}</span>
</div>
<div class="info-item">
<span class="info-label">Email</span>
<span class="info-value">{{ user_profile.email }}</span>
</div>
<div class="info-item">
<span class="info-label">Phone</span>
<span class="info-value">{{ user_profile.phone_number|default:"Not provided" }}</span>
</div>
<div class="info-item">
<span class="info-label">Mobile</span>
<span class="info-value">{{ user_profile.mobile_number|default:"Not provided" }}</span>
</div>
<div class="info-item">
<span class="info-label">Date of Birth</span>
<span class="info-value">{{ user_profile.date_of_birth|date:"M d, Y"|default:"Not provided" }}</span>
</div>
<div class="info-item">
<span class="info-label">Gender</span>
<span class="info-value">{{ user_profile.get_gender_display|default:"Not specified" }}</span>
</div>
</div>
<div class="info-card">
<h5 class="mb-3">
<i class="fas fa-briefcase me-2"></i>Professional Information
</h5>
<div class="info-item">
<span class="info-label">Role</span>
<span class="info-value">{{ user_profile.get_role_display }}</span>
</div>
<div class="info-item">
<span class="info-label">Department</span>
<span class="info-value">{{ user_profile.department|default:"Not assigned" }}</span>
</div>
<div class="info-item">
<span class="info-label">License Number</span>
<span class="info-value">{{ user_profile.license_number|default:"Not provided" }}</span>
</div>
<div class="info-item">
<span class="info-label">Specialization</span>
<span class="info-value">{{ user_profile.specialization|default:"Not specified" }}</span>
</div>
<div class="info-item">
<span class="info-label">Date Joined</span>
<span class="info-value">{{ user_profile.date_joined|date:"M d, Y" }}</span>
</div>
<div class="info-item">
<span class="info-label">Last Login</span>
<span class="info-value">{{ user_profile.last_login|date:"M d, Y H:i"|default:"Never" }}</span>
</div>
</div>
</div>
<!-- Sessions and Security -->
<div class="col-lg-8">
<div class="row">
<div class="col-12">
<div class="info-card">
<h5 class="mb-3">
<i class="fas fa-desktop me-2"></i>Active Sessions
<span class="badge bg-primary ms-2">{{ active_sessions.count }}</span>
</h5>
{% if active_sessions %}
{% for session in active_sessions %}
<div class="session-item {% if session.expires_at < now %}expired{% endif %}">
<div class="d-flex justify-content-between align-items-start">
<div>
<div class="fw-bold">{{ session.device_type|title }} • {{ session.browser_name }}</div>
<div class="text-muted small">
<i class="fas fa-globe me-1"></i>{{ session.ip_address }}
<span class="mx-2"></span>
<i class="fas fa-map-marker-alt me-1"></i>{{ session.location|default:"Unknown location" }}
</div>
<div class="text-muted small">
<i class="fas fa-clock me-1"></i>
Started: {{ session.created_at|date:"M d, Y H:i" }}
<span class="mx-2"></span>
Last activity: {{ session.last_activity_at|date:"M d, Y H:i" }}
</div>
</div>
<div>
{% if session.is_current_session %}
<span class="badge bg-success">Current</span>
{% else %}
<button class="btn btn-sm btn-outline-danger"
hx-post="{% url 'accounts:end_session' session.session_id %}"
hx-confirm="End this session?">
<i class="fas fa-times"></i>
</button>
{% endif %}
</div>
</div>
</div>
{% endfor %}
{% else %}
<div class="text-center py-3 text-muted">
<i class="fas fa-desktop fa-2x mb-2"></i>
<p>No active sessions</p>
</div>
{% endif %}
</div>
</div>
<div class="col-12">
<div class="info-card">
<h5 class="mb-3">
<i class="fas fa-shield-alt me-2"></i>Two-Factor Authentication
<span class="badge bg-primary ms-2">{{ two_factor_devices.count }}</span>
</h5>
{% if two_factor_devices %}
{% for device in two_factor_devices %}
<div class="device-item">
<div class="d-flex justify-content-between align-items-start">
<div>
<div class="fw-bold">{{ device.name }}</div>
<div class="text-muted small">
<i class="fas fa-mobile-alt me-1"></i>{{ device.get_device_type_display }}
{% if device.phone_number %}
<span class="mx-2"></span>{{ device.phone_number }}
{% endif %}
</div>
<div class="text-muted small">
<i class="fas fa-clock me-1"></i>
Added: {{ device.created_at|date:"M d, Y H:i" }}
<span class="mx-2"></span>
Last used: {{ device.last_used_at|date:"M d, Y H:i"|default:"Never" }}
</div>
</div>
<div>
{% if device.is_verified %}
<span class="badge bg-success">Verified</span>
{% else %}
<span class="badge bg-warning">Pending</span>
{% endif %}
</div>
</div>
</div>
{% endfor %}
{% else %}
<div class="text-center py-3 text-muted">
<i class="fas fa-shield-alt fa-2x mb-2"></i>
<p>No two-factor devices configured</p>
</div>
{% endif %}
</div>
</div>
<div class="col-12">
<div class="info-card">
<h5 class="mb-3">
<i class="fas fa-history me-2"></i>Recent Activity
</h5>
<div id="user-activity-log"
hx-get="{% url 'accounts:user_activity_log' user_profile.id %}"
hx-trigger="load">
<div class="text-center py-3">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block js %}
<script>
document.addEventListener('DOMContentLoaded', function() {
// Auto-refresh activity log every 60 seconds
setInterval(function() {
htmx.trigger('#user-activity-log', 'refresh');
}, 60000);
});
</script>
{% endblock %}