453 lines
22 KiB
HTML
453 lines
22 KiB
HTML
{% extends "base.html" %}
|
|
{% load static %}
|
|
{% load i18n %}
|
|
|
|
{% block title %}{% trans "User Profile" %} - ATS{% endblock %}
|
|
|
|
{% block customCSS %}
|
|
<style>
|
|
/* Profile Avatar Animation */
|
|
.profile-avatar-wrapper {
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.profile-avatar-wrapper:hover {
|
|
transform: scale(1.05);
|
|
box-shadow: 0 0 0 4px rgba(157, 34, 53, 0.2);
|
|
}
|
|
|
|
.profile-avatar {
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
/* Card Hover Effects */
|
|
.profile-card {
|
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
}
|
|
|
|
.profile-card:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
/* Button Hover Effects */
|
|
.btn-primary {
|
|
transition: all 0.2s ease;
|
|
}
|
|
|
|
.btn-primary:hover {
|
|
transform: translateY(-1px);
|
|
box-shadow: 0 4px 12px rgba(157, 34, 53, 0.4);
|
|
}
|
|
|
|
.btn-secondary {
|
|
transition: all 0.2s ease;
|
|
}
|
|
|
|
.btn-secondary:hover {
|
|
background-color: rgba(157, 34, 53, 0.05);
|
|
border-color: rgba(157, 34, 53, 0.3);
|
|
}
|
|
|
|
/* Input Focus Animation */
|
|
.form-input {
|
|
transition: all 0.2s ease;
|
|
}
|
|
|
|
.form-input:focus {
|
|
transform: translateY(-1px);
|
|
}
|
|
|
|
/* Status Indicators */
|
|
.status-dot {
|
|
animation: pulse 2s infinite;
|
|
}
|
|
|
|
@keyframes pulse {
|
|
0%, 100% {
|
|
opacity: 1;
|
|
}
|
|
50% {
|
|
opacity: 0.5;
|
|
}
|
|
}
|
|
|
|
/* Modal Animation */
|
|
.modal-backdrop {
|
|
animation: fadeIn 0.2s ease;
|
|
}
|
|
|
|
@keyframes fadeIn {
|
|
from {
|
|
opacity: 0;
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
.modal-content {
|
|
animation: slideUp 0.3s cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
|
|
@keyframes slideUp {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(20px) scale(0.95);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0) scale(1);
|
|
}
|
|
}
|
|
|
|
/* Info Item Styling */
|
|
.info-item {
|
|
transition: all 0.2s ease;
|
|
}
|
|
|
|
.info-item:hover {
|
|
background-color: rgba(157, 34, 53, 0.02);
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="max-w-7xl mx-auto py-8 px-4 sm:px-6 lg:px-8">
|
|
|
|
<!-- Profile Header -->
|
|
<div class="bg-gradient-to-r from-temple-red to-red-700 rounded-2xl shadow-2xl p-6 sm:p-8 mb-8 relative overflow-hidden">
|
|
<!-- Decorative Elements -->
|
|
<div class="absolute top-0 right-0 w-64 h-64 bg-white/5 rounded-full -translate-y-1/2 translate-x-1/2"></div>
|
|
<div class="absolute bottom-0 left-0 w-48 h-48 bg-white/5 rounded-full translate-y-1/2 -translate-x-1/2"></div>
|
|
|
|
<div class="relative flex flex-col sm:flex-row items-center sm:items-start gap-6 sm:gap-8">
|
|
<!-- Avatar -->
|
|
<div class="profile-avatar-wrapper relative">
|
|
<div class="absolute -bottom-1 -right-1 w-6 h-6 bg-temple-cream rounded-full flex items-center justify-center shadow-md border-2 border-temple-red">
|
|
<div class="w-3 h-3 bg-green-500 rounded-full status-dot"></div>
|
|
</div>
|
|
{% if user.profile_image %}
|
|
<img src="{{ user.profile_image.url }}"
|
|
alt="{{ user.get_full_name|default:user.username }}"
|
|
class="profile-avatar w-28 h-28 sm:w-32 sm:h-32 rounded-full border-4 border-temple-cream shadow-lg object-cover">
|
|
{% else %}
|
|
<div class="profile-avatar w-28 h-28 sm:w-32 sm:h-32 rounded-full border-4 border-temple-cream shadow-lg bg-temple-cream flex items-center justify-center">
|
|
<i data-lucide="user" class="w-16 h-16 text-temple-red"></i>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- User Info -->
|
|
<div class="text-center sm:text-left flex-1">
|
|
<h1 class="text-2xl sm:text-3xl font-bold text-white mb-2">
|
|
{{ user.get_full_name|default:user.username }}
|
|
</h1>
|
|
<p class="text-white/80 text-sm sm:text-base mb-3">
|
|
{{ user.email }}
|
|
</p>
|
|
<div class="flex flex-wrap items-center justify-center sm:justify-start gap-3">
|
|
<span class="inline-flex items-center gap-1.5 px-3 py-1.5 bg-white/20 backdrop-blur-sm rounded-full text-white text-sm font-medium">
|
|
<i data-lucide="shield-check" class="w-4 h-4"></i>
|
|
{% trans "Verified Account" %}
|
|
</span>
|
|
<span class="inline-flex items-center gap-1.5 px-3 py-1.5 bg-white/20 backdrop-blur-sm rounded-full text-white text-sm font-medium">
|
|
<i data-lucide="calendar" class="w-4 h-4"></i>
|
|
{{ user.date_joined|date:"F Y" }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Quick Actions -->
|
|
<div class="flex flex-col gap-2">
|
|
<button type="button"
|
|
onclick="document.getElementById('imageModal').classList.remove('hidden')"
|
|
class="btn-primary inline-flex items-center justify-center gap-2 px-5 py-2.5 bg-white text-temple-red rounded-lg font-semibold shadow-lg hover:shadow-xl">
|
|
<i data-lucide="camera" class="w-4 h-4"></i>
|
|
{% trans "Change Photo" %}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6 lg:gap-8">
|
|
|
|
<!-- Left Column - Personal Information (2/3 width) -->
|
|
<div class="lg:col-span-2 space-y-6">
|
|
<!-- Personal Info Card -->
|
|
<div class="profile-card bg-white rounded-2xl shadow-sm border border-gray-200 overflow-hidden">
|
|
<!-- Card Header -->
|
|
<div class="px-6 py-4 border-b border-gray-100 bg-gradient-to-r from-gray-50 to-white">
|
|
<div class="flex items-center gap-3">
|
|
<div class="w-10 h-10 rounded-xl bg-temple-red/10 flex items-center justify-center">
|
|
<i data-lucide="user-circle" class="w-5 h-5 text-temple-red"></i>
|
|
</div>
|
|
<div>
|
|
<h2 class="text-lg font-bold text-gray-900">{% trans "Personal Information" %}</h2>
|
|
<p class="text-sm text-gray-500">{% trans "Update your personal details" %}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Card Body -->
|
|
<div class="p-6 sm:p-8">
|
|
<form method="POST" action="{% url 'user_detail' user.pk %}" class="space-y-6">
|
|
{% csrf_token %}
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<!-- First Name -->
|
|
<div class="space-y-2">
|
|
<label for="id_first_name" class="flex items-center gap-2 text-sm font-semibold text-gray-700">
|
|
<i data-lucide="user" class="w-4 h-4 text-gray-400"></i>
|
|
{% trans "First Name" %}
|
|
</label>
|
|
<input type="text"
|
|
class="form-input w-full px-4 py-3 rounded-xl border-2 border-gray-200 bg-white focus:outline-none focus:border-temple-red focus:ring-4 focus:ring-temple-red/10 text-gray-900 placeholder-gray-400"
|
|
id="id_first_name"
|
|
name="first_name"
|
|
value="{{ user.first_name|default:'' }}"
|
|
placeholder="{% trans 'Enter your first name' %}">
|
|
</div>
|
|
|
|
<!-- Last Name -->
|
|
<div class="space-y-2">
|
|
<label for="id_last_name" class="flex items-center gap-2 text-sm font-semibold text-gray-700">
|
|
<i data-lucide="user" class="w-4 h-4 text-gray-400"></i>
|
|
{% trans "Last Name" %}
|
|
</label>
|
|
<input type="text"
|
|
class="form-input w-full px-4 py-3 rounded-xl border-2 border-gray-200 bg-white focus:outline-none focus:border-temple-red focus:ring-4 focus:ring-temple-red/10 text-gray-900 placeholder-gray-400"
|
|
id="id_last_name"
|
|
name="last_name"
|
|
value="{{ user.last_name|default:'' }}"
|
|
placeholder="{% trans 'Enter your last name' %}">
|
|
</div>
|
|
|
|
<!-- Email -->
|
|
<div class="md:col-span-2 space-y-2">
|
|
<label for="id_email" class="flex items-center gap-2 text-sm font-semibold text-gray-700">
|
|
<i data-lucide="mail" class="w-4 h-4 text-gray-400"></i>
|
|
{% trans "Email Address" %}
|
|
</label>
|
|
<div class="relative">
|
|
<input type="email"
|
|
class="form-input w-full px-4 py-3 pr-12 rounded-xl border-2 border-gray-200 bg-gray-50 text-gray-500 cursor-not-allowed"
|
|
id="id_email"
|
|
value="{{ user.email }}"
|
|
disabled>
|
|
<i data-lucide="lock" class="absolute right-4 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-400"></i>
|
|
</div>
|
|
<div class="flex items-center gap-2 text-sm text-gray-600">
|
|
<i data-lucide="info" class="w-4 h-4"></i>
|
|
<a href="{% url 'account_email' %}"
|
|
class="font-medium text-temple-red hover:text-red-700 transition-colors">
|
|
{% trans "Manage email addresses →" %}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Save Button -->
|
|
<div class="pt-4 flex items-center gap-3">
|
|
<button type="submit"
|
|
class="btn-primary inline-flex items-center gap-2 px-8 py-3 bg-temple-red text-white rounded-xl font-semibold shadow-lg hover:shadow-xl">
|
|
<i data-lucide="save" class="w-5 h-5"></i>
|
|
{% trans "Save Changes" %}
|
|
</button>
|
|
<span class="text-sm text-gray-500">
|
|
{% trans "Changes are saved automatically" %}
|
|
</span>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Right Column - Security & Status (1/3 width) -->
|
|
<div class="space-y-6">
|
|
<!-- Security Card -->
|
|
<div class="profile-card bg-white rounded-2xl shadow-sm border border-gray-200 overflow-hidden">
|
|
<!-- Card Header -->
|
|
<div class="px-6 py-4 border-b border-gray-100 bg-gradient-to-r from-gray-50 to-white">
|
|
<div class="flex items-center gap-3">
|
|
<div class="w-10 h-10 rounded-xl bg-temple-red/10 flex items-center justify-center">
|
|
<i data-lucide="shield" class="w-5 h-5 text-temple-red"></i>
|
|
</div>
|
|
<div>
|
|
<h2 class="text-lg font-bold text-gray-900">{% trans "Security" %}</h2>
|
|
<p class="text-sm text-gray-500">{% trans "Protect your account" %}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Card Body -->
|
|
<div class="p-6 space-y-3">
|
|
<a href="{% url 'account_change_password' %}"
|
|
class="btn-primary w-full inline-flex items-center justify-center gap-2 px-5 py-3.5 bg-temple-red text-white rounded-xl font-semibold shadow-md hover:shadow-lg">
|
|
<i data-lucide="lock" class="w-5 h-5"></i>
|
|
{% trans "Change Password" %}
|
|
</a>
|
|
|
|
<button type="button"
|
|
onclick="document.getElementById('imageModal').classList.remove('hidden')"
|
|
class="btn-secondary w-full inline-flex items-center justify-center gap-2 px-5 py-3.5 border-2 border-gray-200 text-gray-700 rounded-xl font-semibold hover:border-temple-red">
|
|
<i data-lucide="image" class="w-5 h-5"></i>
|
|
{% trans "Change Profile Image" %}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Account Status Card -->
|
|
<div class="profile-card bg-white rounded-2xl shadow-sm border border-gray-200 overflow-hidden">
|
|
<!-- Card Header -->
|
|
<div class="px-6 py-4 border-b border-gray-100 bg-gradient-to-r from-gray-50 to-white">
|
|
<div class="flex items-center gap-3">
|
|
<div class="w-10 h-10 rounded-xl bg-temple-red/10 flex items-center justify-center">
|
|
<i data-lucide="activity" class="w-5 h-5 text-temple-red"></i>
|
|
</div>
|
|
<div>
|
|
<h2 class="text-lg font-bold text-gray-900">{% trans "Account Details" %}</h2>
|
|
<p class="text-sm text-gray-500">{% trans "Your account info" %}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Card Body -->
|
|
<div class="p-6 space-y-4">
|
|
<div class="info-item p-3 rounded-xl bg-gray-50">
|
|
<div class="flex items-center gap-2 text-sm text-gray-500 mb-1">
|
|
<i data-lucide="user" class="w-4 h-4"></i>
|
|
{% trans "Username" %}
|
|
</div>
|
|
<div class="text-base font-bold text-gray-900">{{ user.username }}</div>
|
|
</div>
|
|
|
|
<div class="info-item p-3 rounded-xl bg-gray-50">
|
|
<div class="flex items-center gap-2 text-sm text-gray-500 mb-1">
|
|
<i data-lucide="clock" class="w-4 h-4"></i>
|
|
{% trans "Last Login" %}
|
|
</div>
|
|
<div class="text-base font-bold text-gray-900">
|
|
{% if user.last_login %}{{ user.last_login|date:"F d, Y P" }}{% else %}N/A{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="info-item p-3 rounded-xl bg-gray-50">
|
|
<div class="flex items-center gap-2 text-sm text-gray-500 mb-1">
|
|
<i data-lucide="calendar-plus" class="w-4 h-4"></i>
|
|
{% trans "Date Joined" %}
|
|
</div>
|
|
<div class="text-base font-bold text-gray-900">{{ user.date_joined|date:"F d, Y" }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Image Upload Modal -->
|
|
<div id="imageModal" class="fixed inset-0 modal-backdrop bg-black/60 backdrop-blur-sm hidden items-center justify-center z-50 p-4">
|
|
<div class="modal-content bg-white rounded-2xl shadow-2xl w-full max-w-lg mx-auto">
|
|
<!-- Modal Header -->
|
|
<div class="flex items-center justify-between px-6 py-5 border-b border-gray-100 bg-gradient-to-r from-gray-50 to-white rounded-t-2xl">
|
|
<div class="flex items-center gap-3">
|
|
<div class="w-10 h-10 rounded-xl bg-temple-red/10 flex items-center justify-center">
|
|
<i data-lucide="upload" class="w-5 h-5 text-temple-red"></i>
|
|
</div>
|
|
<h3 class="text-xl font-bold text-gray-900">{% trans "Upload Profile Image" %}</h3>
|
|
</div>
|
|
<button onclick="document.getElementById('imageModal').classList.add('hidden')"
|
|
class="w-10 h-10 rounded-xl bg-gray-100 hover:bg-gray-200 flex items-center justify-center text-gray-400 hover:text-gray-600 transition-colors">
|
|
<i data-lucide="x" class="w-5 h-5"></i>
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Modal Body -->
|
|
<div class="p-6 sm:p-8">
|
|
<form method="post" action="{% url 'user_profile_image_update' user.pk %}" enctype="multipart/form-data" class="space-y-6">
|
|
{% csrf_token %}
|
|
|
|
<div class="space-y-4">
|
|
<label for="{{ profile_form.profile_image.id_for_label }}" class="flex items-center gap-2 text-sm font-semibold text-gray-700">
|
|
<i data-lucide="image" class="w-4 h-4 text-gray-400"></i>
|
|
{% trans "Profile Image" %}
|
|
</label>
|
|
|
|
{% if profile_form.instance.profile_image %}
|
|
<!-- Current Image -->
|
|
<div class="p-4 rounded-xl bg-gray-50 border-2 border-dashed border-gray-200">
|
|
<div class="flex items-center gap-4 mb-3">
|
|
<div class="w-16 h-16 rounded-xl bg-temple-red/10 flex items-center justify-center shrink-0">
|
|
<i data-lucide="check" class="w-8 h-8 text-temple-red"></i>
|
|
</div>
|
|
<div class="flex-1 min-w-0">
|
|
<div class="text-sm font-semibold text-gray-900">{% trans "Current Image" %}</div>
|
|
<div class="text-xs text-gray-500 truncate">{{ profile_form.instance.profile_image.name }}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex items-center gap-4">
|
|
<a href="{{ profile_form.instance.profile_image.url }}" target="_blank"
|
|
class="btn-primary inline-flex items-center gap-2 px-4 py-2.5 bg-temple-red text-white rounded-lg font-semibold text-sm">
|
|
<i data-lucide="eye" class="w-4 h-4"></i>
|
|
{% trans "View Original" %}
|
|
</a>
|
|
<img src="{{ profile_form.instance.profile_image.url }}"
|
|
alt="Current Profile Image"
|
|
class="w-20 h-20 rounded-xl object-cover border-2 border-white shadow-md">
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Upload New -->
|
|
<div class="pt-4">
|
|
{{ profile_form.profile_image }}
|
|
</div>
|
|
{% else %}
|
|
<!-- No Current Image -->
|
|
<div class="p-8 rounded-xl border-2 border-dashed border-gray-200 bg-gray-50 hover:border-temple-red/50 hover:bg-temple-red/5 transition-all cursor-pointer">
|
|
<div class="flex flex-col items-center gap-3 text-center">
|
|
<div class="w-16 h-16 rounded-full bg-temple-red/10 flex items-center justify-center">
|
|
<i data-lucide="upload-cloud" class="w-8 h-8 text-temple-red"></i>
|
|
</div>
|
|
<div>
|
|
<div class="font-semibold text-gray-900">{% trans "No profile image yet" %}</div>
|
|
<div class="text-sm text-gray-500">{% trans "Upload your first profile picture" %}</div>
|
|
</div>
|
|
</div>
|
|
{{ profile_form.profile_image }}
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% for error in profile_form.profile_image.errors %}
|
|
<div class="flex items-start gap-2 p-3 bg-red-50 border border-red-200 rounded-xl text-red-600 text-sm">
|
|
<i data-lucide="alert-circle" class="w-4 h-4 shrink-0 mt-0.5"></i>
|
|
{{ error }}
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
|
|
<!-- Modal Actions -->
|
|
<div class="flex gap-3 pt-4 border-t border-gray-100">
|
|
<button type="button"
|
|
onclick="document.getElementById('imageModal').classList.add('hidden')"
|
|
class="btn-secondary flex-1 inline-flex items-center justify-center gap-2 px-6 py-3 border-2 border-gray-200 text-gray-700 rounded-xl font-semibold hover:border-gray-300">
|
|
{% trans "Cancel" %}
|
|
</button>
|
|
<button type="submit"
|
|
class="btn-primary flex-1 inline-flex items-center justify-center gap-2 px-6 py-3 bg-temple-red text-white rounded-xl font-semibold shadow-lg hover:shadow-xl">
|
|
<i data-lucide="upload" class="w-5 h-5"></i>
|
|
{% trans "Upload Image" %}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// Initialize Lucide icons
|
|
lucide.createIcons();
|
|
</script>
|
|
|
|
{% endblock %} |