Marwan Alwali 2780a2dc7c update
2025-09-16 15:10:57 +03:00

455 lines
19 KiB
HTML

{% extends "base.html" %}
{% load static %}
{% block title %}My Profile - {{ block.super }}{% endblock %}
{% block css %}
<style>
.profile-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 2rem 0;
margin-bottom: 2rem;
}
.profile-avatar {
width: 100px;
height: 100px;
border-radius: 50%;
object-fit: cover;
border: 4px solid #fff;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
.profile-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;
}
.form-section {
margin-bottom: 2rem;
}
.form-section h6 {
color: #495057;
font-weight: 600;
margin-bottom: 1rem;
padding-bottom: 0.5rem;
border-bottom: 2px solid #f1f3f4;
}
.session-card {
background: #f8f9fa;
border-radius: 0.375rem;
padding: 1rem;
margin-bottom: 0.75rem;
border-left: 4px solid #28a745;
}
.session-card.current {
border-left-color: #007bff;
background: #e3f2fd;
}
.device-card {
background: #f8f9fa;
border-radius: 0.375rem;
padding: 1rem;
margin-bottom: 0.75rem;
border-left: 4px solid #007bff;
}
.social-account-card {
background: #f8f9fa;
border-radius: 0.375rem;
padding: 1rem;
margin-bottom: 0.75rem;
border-left: 4px solid #28a745;
}
.preferences-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1rem;
}
.preference-item {
background: #f8f9fa;
padding: 1rem;
border-radius: 0.375rem;
border: 1px solid #dee2e6;
}
.avatar-upload {
position: relative;
display: inline-block;
}
.avatar-upload input[type="file"] {
position: absolute;
opacity: 0;
width: 100%;
height: 100%;
cursor: pointer;
}
.avatar-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0,0,0,0.5);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transition: opacity 0.3s ease;
cursor: pointer;
}
.avatar-upload:hover .avatar-overlay {
opacity: 1;
}
</style>
{% endblock %}
{% block content %}
<div class="profile-header">
<div class="container-fluid">
<div class="row align-items-center">
<div class="col-auto">
<div class="avatar-upload">
{% if user.employee_profile.profile_picture %}
<img src="{{ user.employee_profile.profile_picture.url }}" alt="{{ user.employee_profile.get_full_name }}" class="profile-avatar">
{% else %}
<div class="profile-avatar bg-secondary d-flex align-items-center justify-content-center">
<i class="fas fa-user fa-2x text-white"></i>
</div>
{% endif %}
<div class="avatar-overlay">
<i class="fas fa-camera text-white"></i>
</div>
<input type="file" accept="image/*" id="avatar-upload">
</div>
</div>
<div class="col">
<h1 class="mb-2">{{ user.employee_profile.get_full_name }}</h1>
<p class="mb-0 opacity-75">{{ user.employee_profile.get_role_display }} • {{ user.employee_profile.department|default:"No Department" }}</p>
</div>
</div>
</div>
</div>
<div class="container-fluid">
<div class="row">
<!-- Profile Information -->
<div class="col-lg-8">
<div class="profile-card">
<h5 class="mb-4">
<i class="fas fa-user me-2"></i>Profile Information
</h5>
<form id="profile-form"
hx-post="{% url 'accounts:user_profile_update' %}"
hx-trigger="submit"
hx-swap="none">
{% csrf_token %}
<div class="form-section">
<h6>Personal Information</h6>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="first_name" class="form-label">First Name</label>
<input type="text" class="form-control" id="first_name" name="first_name"
value="{{ user.employee_profile.first_name }}" required>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="last_name" class="form-label">Last Name</label>
<input type="text" class="form-control" id="last_name" name="last_name"
value="{{ user.employee_profile.last_name }}" required>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="email" class="form-label">Email Address</label>
<input type="email" class="form-control" id="email" name="email"
value="{{ user.employee_profile.email }}" required>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="phone" class="form-label">Phone Number</label>
<input type="tel" class="form-control" id="phone" name="phone"
value="{{ user.employee_profile.phone }}">
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="mobile_phone" class="form-label">Mobile Number</label>
<input type="tel" class="form-control" id="mobile_phone" name="mobile_phone"
value="{{ user.employee_profile.mobile_phone }}">
</div>
</div>
</div>
<div class="mb-3">
<label for="bio" class="form-label">Bio</label>
<textarea class="form-control" id="bio" name="bio" rows="3"
placeholder="Tell us about yourself...">{{ user.employee_profile.bio }}</textarea>
</div>
</div>
<div class="form-section">
<h6>Preferences</h6>
<div class="preferences-grid">
<div class="preference-item">
<label for="timezone" class="form-label">Timezone</label>
<select class="form-select" id="timezone" name="timezone">
<option value="Asia/Riyadh" {% if user.employee_profile.timezone == 'Asia/Riyadh' %}selected{% endif %}>Asia/Riyadh</option>
<option value="America/New_York" {% if user.employee_profile.timezone == 'America/New_York' %}selected{% endif %}>Eastern Time</option>
<option value="America/Chicago" {% if user.employee_profile.timezone == 'America/Chicago' %}selected{% endif %}>Central Time</option>
<option value="America/Denver" {% if user.employee_profile.timezone == 'America/Denver' %}selected{% endif %}>Mountain Time</option>
<option value="America/Los_Angeles" {% if user.employee_profile.timezone == 'America/Los_Angeles' %}selected{% endif %}>Pacific Time</option>
</select>
</div>
<div class="preference-item">
<label for="language" class="form-label">Language</label>
<select class="form-select" id="language" name="language">
<option value="en" {% if user.employee_profile.language == 'en' %}selected{% endif %}>English</option>
<option value="ar" {% if user.employee_profile.language == 'ar' %}selected{% endif %}>Arabic</option>
<option value="fr" {% if user.employee_profile.language == 'fr' %}selected{% endif %}>French</option>
</select>
</div>
<div class="preference-item">
<label for="theme" class="form-label">Theme</label>
<select class="form-select" id="theme" name="theme">
<option value="light" {% if user.employee_profile.theme == 'light' %}selected{% endif %}>Light</option>
<option value="dark" {% if user.employee_profile.theme == 'dark' %}selected{% endif %}>Dark</option>
<option value="auto" {% if user.employee_profile.theme == 'auto' %}selected{% endif %}>Auto</option>
</select>
</div>
</div>
</div>
<div class="d-flex justify-content-end gap-2">
<button type="button" class="btn btn-secondary">Cancel</button>
<button type="submit" class="btn btn-primary">
<i class="fas fa-save me-1"></i>Save Changes
</button>
</div>
</form>
</div>
</div>
<!-- Security & Sessions -->
<div class="col-lg-4">
<!-- Active Sessions -->
<div class="profile-card">
<h6 class="mb-3">
<i class="fas fa-desktop me-2"></i>Active Sessions
<span class="badge bg-primary ms-2">{{ active_sessions.count }}</span>
</h6>
{% if active_sessions %}
{% for session in active_sessions %}
<div class="session-card {% if session.is_current_session %}current{% endif %}">
<div class="d-flex justify-content-between align-items-start">
<div class="flex-grow-1">
<div class="fw-bold small">{{ session.device_type|title }}</div>
<div class="text-muted small">
{{ session.ip_address }} • {{ session.location|default:"Unknown" }}
</div>
<div class="text-muted small">
{{ session.last_activity_at|timesince }} ago
</div>
</div>
<div>
{% if session.is_current_session %}
<span class="badge bg-primary small">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 class="small">No active sessions</p>
</div>
{% endif %}
</div>
<!-- Two-Factor Authentication -->
<div class="profile-card">
<div class="d-flex justify-content-between align-items-center mb-3">
<h6 class="mb-0">
<i class="fas fa-shield-alt me-2"></i>Two-Factor Authentication
</h6>
<button class="btn btn-sm btn-outline-primary"
hx-get="{% url 'accounts:two_factor_setup' %}"
hx-target="#two-factor-modal .modal-body"
data-bs-toggle="modal"
data-bs-target="#two-factor-modal">
<i class="fas fa-plus me-1"></i>Add
</button>
</div>
{% if two_factor_devices %}
{% for device in two_factor_devices %}
<div class="device-card">
<div class="d-flex justify-content-between align-items-start">
<div class="flex-grow-1">
<div class="fw-bold small">{{ device.name }}</div>
<div class="text-muted small">
{{ device.get_device_type_display }}
{% if device.phone_number %} • {{ device.phone_number }}{% endif %}
</div>
<div class="text-muted small">
Added {{ device.created_at|date:"M d, Y" }}
</div>
</div>
<div>
{% if device.is_verified %}
<span class="badge bg-success small">Verified</span>
{% else %}
<span class="badge bg-warning small">Pending</span>
{% endif %}
<button class="btn btn-sm btn-outline-danger ms-1"
hx-delete="{% url 'accounts:remove_two_factor_device' device.device_id %}"
hx-confirm="Remove this device?">
<i class="fas fa-trash"></i>
</button>
</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 class="small">No two-factor devices configured</p>
<p class="small">Add a device to enhance your account security</p>
</div>
{% endif %}
</div>
<!-- Social Accounts -->
<div class="profile-card">
<h6 class="mb-3">
<i class="fas fa-share-alt me-2"></i>Connected Accounts
</h6>
{% if social_accounts %}
{% for account in social_accounts %}
<div class="social-account-card">
<div class="d-flex justify-content-between align-items-center">
<div>
<div class="fw-bold small">{{ account.get_provider_display }}</div>
<div class="text-muted small">{{ account.provider_user_id }}</div>
</div>
<button class="btn btn-sm btn-outline-danger">
<i class="fas fa-unlink"></i>
</button>
</div>
</div>
{% endfor %}
{% else %}
<div class="text-center py-3 text-muted">
<i class="fas fa-share-alt fa-2x mb-2"></i>
<p class="small">No connected accounts</p>
</div>
{% endif %}
</div>
</div>
</div>
</div>
<!-- Two-Factor Setup Modal -->
<div class="modal fade" id="two-factor-modal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Setup Two-Factor Authentication</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<!-- Content loaded via HTMX -->
</div>
</div>
</div>
</div>
{% endblock %}
{% block js %}
<script>
document.addEventListener('DOMContentLoaded', function() {
// Handle avatar upload
document.getElementById('avatar-upload').addEventListener('change', function(e) {
const file = e.target.files[0];
if (file) {
const formData = new FormData();
formData.append('avatar', file);
// Upload avatar via fetch
fetch('{% url "accounts:upload_avatar" %}', {
method: 'POST',
body: formData,
headers: {
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
location.reload();
} else {
alert('Failed to upload avatar');
}
});
}
});
// Handle form submission success
document.body.addEventListener('htmx:afterRequest', function(event) {
if (event.detail.target.id === 'profile-form' && event.detail.xhr.status === 200) {
// Show success message
const alert = document.createElement('div');
alert.className = 'alert alert-success alert-dismissible fade show';
alert.innerHTML = `
<i class="fas fa-check-circle me-2"></i>Profile updated successfully!
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
document.querySelector('.profile-card').insertBefore(alert, document.querySelector('#profile-form'));
// Auto-dismiss after 3 seconds
setTimeout(() => {
alert.remove();
}, 3000);
}
});
});
</script>
{% endblock %}