Files
GOV_QR_Codes_Management/templates/profile.html
T
2025-07-29 17:02:08 -04:00

1183 lines
33 KiB
HTML

{% extends "base.html" %} {% block title %}My Profile - QR Code Management{%
endblock %} {% block content %}
<div class="profile-page">
<div class="profile-header">
<div class="profile-avatar">
<div class="avatar-circle">
<i class="fas fa-user"></i>
</div>
<div class="avatar-status {{ user.role }}">
<i
class="fas {{ 'fa-crown' if user.role == 'admin' else 'fa-user-tie' }}"
></i>
</div>
</div>
<div class="profile-info">
<h1>{{ user.full_name }}</h1>
<div class="profile-meta">
<div class="meta-item">
<i class="fas fa-envelope"></i>
<span>{{ user.email }}</span>
</div>
<div class="meta-item">
<i class="fas fa-user"></i>
<span>@{{ user.username }}</span>
</div>
<div class="meta-item">
<i class="fas fa-shield-alt"></i>
<span class="role-badge {{ user.role }}">
{{ user.role.title() }}
</span>
</div>
<div class="meta-item">
<i class="fas fa-calendar-plus"></i>
<span>Member since {{ user.created_date.strftime('%B %Y') }}</span>
</div>
{% if user.last_login_date %}
<div class="meta-item">
<i class="fas fa-clock"></i>
<span
>Last login {{ user.last_login_date.strftime('%Y-%m-%d %H:%M')
}}</span
>
</div>
{% endif %}
</div>
</div>
</div>
<div class="profile-content">
<!-- Profile Statistics -->
<div class="profile-stats">
<div class="stat-card">
<div class="stat-icon">
<i class="fas fa-qrcode"></i>
</div>
<div class="stat-info">
<h3>{{ user.created_qr_codes.count() }}</h3>
<p>QR Codes Created</p>
</div>
</div>
<div class="stat-card">
<div class="stat-icon">
<i class="fas fa-check-circle"></i>
</div>
<div class="stat-info">
<h3>
{{ user.created_qr_codes.filter_by(active_status=True).count() }}
</h3>
<p>Active QR Codes</p>
</div>
</div>
<div class="stat-card">
<div class="stat-icon">
<i class="fas fa-calendar-alt"></i>
</div>
<div class="stat-info">
<h3>{{ user.created_date|days_since }}</h3>
<p>Days as Member</p>
</div>
</div>
</div>
<!-- Profile Management Tabs -->
<div class="profile-tabs">
<div class="tab-headers">
<button class="tab-header active" data-tab="edit-profile">
<i class="fas fa-edit"></i>
Edit Profile
</button>
<button class="tab-header" data-tab="change-password">
<i class="fas fa-key"></i>
Change Password
</button>
<button class="tab-header" data-tab="account-settings">
<i class="fas fa-cog"></i>
Account Settings
</button>
</div>
<!-- Edit Profile Tab -->
<div class="tab-content active" id="edit-profile">
<div class="tab-card">
<div class="tab-header-section">
<h3>
<i class="fas fa-user-edit"></i>
Update Profile Information
</h3>
<p>
Keep your profile information up to date for better collaboration
</p>
</div>
<form method="POST" class="profile-form" id="profileForm">
<input type="hidden" name="form_type" value="profile" />
<div class="form-row">
<div class="form-group">
<label for="full_name">
<i class="fas fa-id-card"></i>
Full Name
</label>
<input
type="text"
id="full_name"
name="full_name"
value="{{ user.full_name }}"
required
/>
<small class="form-help">Your display name in the system</small>
</div>
<div class="form-group">
<label for="email">
<i class="fas fa-envelope"></i>
Email Address
</label>
<input
type="email"
id="email"
name="email"
value="{{ user.email }}"
required
/>
<small class="form-help"
>Used for notifications and account recovery</small
>
</div>
</div>
<div class="form-group">
<label for="username_display">
<i class="fas fa-user"></i>
Username
</label>
<input
type="text"
id="username_display"
value="{{ user.username }}"
disabled
/>
<small class="form-help"
>Username cannot be changed. Contact admin if needed.</small
>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">
<i class="fas fa-save"></i>
Save Changes
</button>
<button type="reset" class="btn btn-secondary">
<i class="fas fa-undo"></i>
Reset
</button>
</div>
</form>
</div>
</div>
<!-- Change Password Tab -->
<div class="tab-content" id="change-password">
<div class="tab-card">
<div class="tab-header-section">
<h3>
<i class="fas fa-shield-alt"></i>
Change Password
</h3>
<p>Ensure your account security with a strong password</p>
</div>
<form method="POST" class="profile-form" id="passwordForm">
<input type="hidden" name="form_type" value="password" />
<div class="form-group">
<label for="current_password">
<i class="fas fa-lock"></i>
Current Password
</label>
<input
type="password"
id="current_password"
name="current_password"
required
/>
<small class="form-help"
>Enter your current password for verification</small
>
</div>
<div class="form-group">
<label for="new_password">
<i class="fas fa-key"></i>
New Password
</label>
<input
type="password"
id="new_password"
name="new_password"
required
minlength="6"
/>
<div class="password-strength" id="passwordStrength"></div>
<small class="form-help"
>Minimum 6 characters with mixed case, numbers, and
symbols</small
>
</div>
<div class="form-group">
<label for="confirm_password">
<i class="fas fa-check-circle"></i>
Confirm New Password
</label>
<input
type="password"
id="confirm_password"
name="confirm_password"
required
/>
<div class="password-match" id="passwordMatch"></div>
</div>
<div class="security-tips">
<h4>
<i class="fas fa-lightbulb"></i>
Password Security Tips
</h4>
<ul>
<li>Use at least 8 characters</li>
<li>Include uppercase and lowercase letters</li>
<li>Add numbers and special characters</li>
<li>Avoid common words or personal information</li>
<li>Don't reuse passwords from other accounts</li>
</ul>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">
<i class="fas fa-shield-alt"></i>
Update Password
</button>
<button type="reset" class="btn btn-secondary">
<i class="fas fa-times"></i>
Cancel
</button>
</div>
</form>
</div>
</div>
<!-- Account Settings Tab -->
<div class="tab-content" id="account-settings">
<div class="tab-card">
<div class="tab-header-section">
<h3>
<i class="fas fa-cog"></i>
Account Information
</h3>
<p>View your account details and system information</p>
</div>
<div class="settings-grid">
<div class="setting-item">
<div class="setting-label">
<i class="fas fa-id-badge"></i>
Account ID
</div>
<div class="setting-value">{{ user.id }}</div>
</div>
<div class="setting-item">
<div class="setting-label">
<i class="fas fa-user-shield"></i>
Role
</div>
<div class="setting-value">
<span class="role-badge {{ user.role }}">
{{ user.role.title() }}
</span>
</div>
</div>
<div class="setting-item">
<div class="setting-label">
<i class="fas fa-calendar-plus"></i>
Account Created
</div>
<div class="setting-value">
{{ user.created_date.strftime('%B %d, %Y at %H:%M') }}
</div>
</div>
{% if user.creator %}
<div class="setting-item">
<div class="setting-label">
<i class="fas fa-user-plus"></i>
Created By
</div>
<div class="setting-value">{{ user.creator.full_name }}</div>
</div>
{% endif %}
<div class="setting-item">
<div class="setting-label">
<i class="fas fa-toggle-on"></i>
Account Status
</div>
<div class="setting-value">
<span
class="status-badge {{ 'active' if user.active_status else 'inactive' }}"
>
<i
class="fas {{ 'fa-check-circle' if user.active_status else 'fa-times-circle' }}"
></i>
{{ 'Active' if user.active_status else 'Inactive' }}
</span>
</div>
</div>
{% if user.last_login_date %}
<div class="setting-item">
<div class="setting-label">
<i class="fas fa-clock"></i>
Last Login
</div>
<div class="setting-value">
{{ user.last_login_date.strftime('%B %d, %Y at %H:%M') }} ({{
user.last_login_date|time_ago }})
</div>
</div>
{% endif %}
</div>
<!-- Account Actions -->
<div class="account-actions">
<h4>
<i class="fas fa-tools"></i>
Account Actions
</h4>
<div class="action-buttons">
<button type="button" class="btn btn-info" onclick="exportData()">
<i class="fas fa-download"></i>
Export My Data
</button>
{% if user.role != 'admin' %}
<button
type="button"
class="btn btn-warning"
onclick="requestAccountDeletion()"
>
<i class="fas fa-exclamation-triangle"></i>
Request Account Deletion
</button>
{% endif %}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %} {% block extra_scripts %}
<script>
document.addEventListener('DOMContentLoaded', function() {
// Tab functionality
const tabHeaders = document.querySelectorAll('.tab-header');
const tabContents = document.querySelectorAll('.tab-content');
tabHeaders.forEach(header => {
header.addEventListener('click', function() {
const tabName = this.dataset.tab;
// Remove active class from all tabs
tabHeaders.forEach(h => h.classList.remove('active'));
tabContents.forEach(c => c.classList.remove('active'));
// Add active class to clicked tab
this.classList.add('active');
document.getElementById(tabName).classList.add('active');
});
});
// Password form handling
const passwordForm = document.getElementById('passwordForm');
const newPasswordInput = document.getElementById('new_password');
const confirmPasswordInput = document.getElementById('confirm_password');
const passwordStrength = document.getElementById('passwordStrength');
const passwordMatch = document.getElementById('passwordMatch');
// Password strength checker
if (newPasswordInput) {
newPasswordInput.addEventListener('input', function() {
const password = this.value;
let strength = 0;
let feedback = [];
// Check password criteria
if (password.length >= 6) strength++;
else feedback.push('At least 6 characters');
if (password.length >= 8) strength++;
else if (password.length >= 6) feedback.push('8+ characters recommended');
if (/[A-Z]/.test(password)) strength++;
else feedback.push('One uppercase letter');
if (/[a-z]/.test(password)) strength++;
else feedback.push('One lowercase letter');
if (/[0-9]/.test(password)) strength++;
else feedback.push('One number');
if (/[^A-Za-z0-9]/.test(password)) strength++;
else feedback.push('One special character');
// Update strength display
let strengthText = '';
let strengthClass = '';
if (strength < 3) {
strengthText = 'Weak';
strengthClass = 'weak';
} else if (strength < 5) {
strengthText = 'Medium';
strengthClass = 'medium';
} else {
strengthText = 'Strong';
strengthClass = 'strong';
}
passwordStrength.className = `password-strength ${strengthClass}`;
passwordStrength.textContent = `Strength: ${strengthText}`;
if (feedback.length > 0 && password.length > 0) {
passwordStrength.textContent += ` (Missing: ${feedback.join(', ')})`;
}
// Check password match
checkPasswordMatch();
});
}
// Password match checker
function checkPasswordMatch() {
if (confirmPasswordInput && confirmPasswordInput.value) {
if (newPasswordInput.value === confirmPasswordInput.value) {
passwordMatch.textContent = '✓ Passwords match';
passwordMatch.className = 'password-match match';
} else {
passwordMatch.textContent = '✗ Passwords do not match';
passwordMatch.className = 'password-match no-match';
}
} else {
passwordMatch.textContent = '';
passwordMatch.className = 'password-match';
}
}
if (confirmPasswordInput) {
confirmPasswordInput.addEventListener('input', checkPasswordMatch);
}
// Form validation
if (passwordForm) {
passwordForm.addEventListener('submit', function(e) {
const currentPassword = document.getElementById('current_password').value;
const newPassword = newPasswordInput.value;
const confirmPassword = confirmPasswordInput.value;
if (!currentPassword) {
e.preventDefault();
alert('Please enter your current password.');
return;
}
if (newPassword.length < 6) {
e.preventDefault();
alert('New password must be at least 6 characters long.');
return;
}
if (newPassword !== confirmPassword) {
e.preventDefault();
alert('New passwords do not match.');
return;
}
if (currentPassword === newPassword) {
e.preventDefault();
alert('New password must be different from current password.');
return;
}
// Show loading state
const submitBtn = passwordForm.querySelector('button[type="submit"]');
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Updating Password...';
submitBtn.disabled = true;
});
}
// Profile form handling
const profileForm = document.getElementById('profileForm');
if (profileForm) {
profileForm.addEventListener('submit', function(e) {
const fullName = document.getElementById('full_name').value.trim();
const email = document.getElementById('email').value.trim();
if (!fullName || fullName.length < 2) {
e.preventDefault();
alert('Full name must be at least 2 characters long.');
return;
}
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!email || !emailRegex.test(email)) {
e.preventDefault();
alert('Please enter a valid email address.');
return;
}
// Show loading state
const submitBtn = profileForm.querySelector('button[type="submit"]');
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Saving Changes...';
submitBtn.disabled = true;
});
}
// Auto-capitalize full name
const fullNameInput = document.getElementById('full_name');
if (fullNameInput) {
fullNameInput.addEventListener('input', function() {
this.value = this.value.replace(/\b\w/g, l => l.toUpperCase());
});
}
// Form reset handling
const resetButtons = document.querySelectorAll('button[type="reset"]');
resetButtons.forEach(button => {
button.addEventListener('click', function(e) {
if (!confirm('Are you sure you want to reset all changes?')) {
e.preventDefault();
}
});
});
});
// Export user data
function exportData() {
const userData = {
id: {{ user.id }},
full_name: "{{ user.full_name }}",
email: "{{ user.email }}",
username: "{{ user.username }}",
role: "{{ user.role }}",
created_date: "{{ user.created_date.isoformat() }}",
last_login_date: "{{ user.last_login_date.isoformat() if user.last_login_date else 'Never' }}",
qr_codes_created: {{ user.created_qr_codes.count() }},
active_qr_codes: {{ user.created_qr_codes.filter_by(active_status=True).count() }}
};
const dataStr = JSON.stringify(userData, null, 2);
const dataBlob = new Blob([dataStr], { type: 'application/json' });
const link = document.createElement('a');
link.href = URL.createObjectURL(dataBlob);
link.download = 'my_account_data.json';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
// Show success message
showToast('Account data exported successfully!', 'success');
}
// Request account deletion
function requestAccountDeletion() {
const confirmed = confirm(
'Are you sure you want to request account deletion?\n\n' +
'This action will:\n' +
'• Deactivate your account\n' +
'• Remove access to QR codes you created\n' +
'• Require admin approval for deletion\n\n' +
'This action cannot be undone easily.'
);
if (confirmed) {
const reason = prompt('Please provide a reason for account deletion (optional):');
// In a real application, this would send a request to the server
alert('Account deletion request submitted. An administrator will review your request.');
showToast('Account deletion request submitted', 'info');
}
}
// Toast notification function
function showToast(message, type = 'info') {
const toast = document.createElement('div');
toast.className = `toast toast-${type}`;
toast.innerHTML = `
<div class="toast-content">
<i class="fas ${getToastIcon(type)}"></i>
<span>${message}</span>
</div>
`;
document.body.appendChild(toast);
// Animate in
setTimeout(() => toast.classList.add('show'), 100);
// Auto remove
setTimeout(() => {
toast.classList.remove('show');
setTimeout(() => {
if (document.body.contains(toast)) {
document.body.removeChild(toast);
}
}, 300);
}, 3000);
}
function getToastIcon(type) {
const icons = {
success: 'fa-check-circle',
error: 'fa-exclamation-circle',
warning: 'fa-exclamation-triangle',
info: 'fa-info-circle'
};
return icons[type] || icons.info;
}
</script>
<style>
/* Profile page styles */
.profile-page {
max-width: 1000px;
margin: 0 auto;
}
.profile-header {
display: flex;
align-items: center;
gap: 2rem;
padding: 2rem;
background: linear-gradient(
135deg,
var(--primary-color),
var(--primary-hover)
);
color: var(--white);
border-radius: var(--radius-xl);
margin-bottom: 2rem;
box-shadow: var(--shadow-lg);
}
.profile-avatar {
position: relative;
flex-shrink: 0;
}
.avatar-circle {
width: 100px;
height: 100px;
background: rgba(255, 255, 255, 0.2);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 2.5rem;
border: 3px solid rgba(255, 255, 255, 0.3);
}
.avatar-status {
position: absolute;
bottom: 0;
right: 0;
width: 30px;
height: 30px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.875rem;
border: 2px solid var(--white);
}
.avatar-status.admin {
background: #fbbf24;
color: #92400e;
}
.avatar-status.staff {
background: var(--gray-400);
color: var(--white);
}
.profile-info h1 {
font-size: 2rem;
font-weight: 700;
margin-bottom: 1rem;
}
.profile-meta {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 0.75rem;
}
.meta-item {
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 0.875rem;
opacity: 0.9;
}
.role-badge {
padding: 0.25rem 0.75rem;
border-radius: var(--radius);
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
}
.role-badge.admin {
background: #fbbf24;
color: #92400e;
}
.role-badge.staff {
background: rgba(255, 255, 255, 0.2);
color: var(--white);
}
.profile-stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1.5rem;
margin-bottom: 2rem;
}
.stat-card {
background: var(--white);
padding: 1.5rem;
border-radius: var(--radius-xl);
box-shadow: var(--shadow);
display: flex;
align-items: center;
gap: 1rem;
transition: var(--transition);
}
.stat-card:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-lg);
}
.stat-icon {
width: 50px;
height: 50px;
background: linear-gradient(
135deg,
var(--primary-color),
var(--primary-hover)
);
color: var(--white);
border-radius: var(--radius-lg);
display: flex;
align-items: center;
justify-content: center;
font-size: 1.25rem;
}
.stat-info h3 {
font-size: 1.75rem;
font-weight: 700;
color: var(--gray-900);
margin-bottom: 0.25rem;
}
.stat-info p {
color: var(--gray-500);
font-size: 0.875rem;
}
.profile-tabs {
background: var(--white);
border-radius: var(--radius-xl);
box-shadow: var(--shadow);
overflow: hidden;
}
.tab-headers {
display: flex;
border-bottom: 1px solid var(--gray-200);
}
.tab-header {
flex: 1;
padding: 1rem 1.5rem;
background: none;
border: none;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
font-weight: 500;
color: var(--gray-600);
transition: var(--transition);
border-bottom: 3px solid transparent;
}
.tab-header:hover {
background: var(--gray-50);
color: var(--gray-900);
}
.tab-header.active {
color: var(--primary-color);
border-bottom-color: var(--primary-color);
background: var(--gray-50);
}
.tab-content {
display: none;
padding: 2rem;
}
.tab-content.active {
display: block;
}
.tab-card {
max-width: 600px;
}
.tab-header-section {
margin-bottom: 2rem;
padding-bottom: 1rem;
border-bottom: 1px solid var(--gray-200);
}
.tab-header-section h3 {
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 1.25rem;
font-weight: 600;
color: var(--gray-900);
margin-bottom: 0.5rem;
}
.tab-header-section p {
color: var(--gray-500);
font-size: 0.875rem;
}
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1.5rem;
margin-bottom: 1.5rem;
}
.security-tips {
margin: 1.5rem 0;
padding: 1rem;
background: var(--gray-50);
border-radius: var(--radius-lg);
border-left: 4px solid var(--primary-color);
}
.security-tips h4 {
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 0.875rem;
font-weight: 600;
color: var(--gray-700);
margin-bottom: 0.75rem;
}
.security-tips ul {
list-style: none;
padding: 0;
margin: 0;
}
.security-tips li {
padding: 0.25rem 0;
font-size: 0.875rem;
color: var(--gray-600);
position: relative;
padding-left: 1rem;
}
.security-tips li::before {
content: "•";
position: absolute;
left: 0;
color: var(--primary-color);
font-weight: bold;
}
.settings-grid {
display: grid;
gap: 1rem;
margin-bottom: 2rem;
}
.setting-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem;
background: var(--gray-50);
border-radius: var(--radius);
border: 1px solid var(--gray-200);
}
.setting-label {
display: flex;
align-items: center;
gap: 0.5rem;
font-weight: 500;
color: var(--gray-700);
}
.setting-value {
font-weight: 500;
color: var(--gray-900);
}
.status-badge {
display: inline-flex;
align-items: center;
gap: 0.25rem;
padding: 0.25rem 0.75rem;
border-radius: var(--radius);
font-size: 0.75rem;
font-weight: 600;
}
.status-badge.active {
background: rgba(5, 150, 105, 0.1);
color: var(--success-color);
}
.status-badge.inactive {
background: rgba(220, 38, 38, 0.1);
color: var(--danger-color);
}
.account-actions {
margin-top: 2rem;
padding-top: 2rem;
border-top: 1px solid var(--gray-200);
}
.account-actions h4 {
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 1rem;
font-weight: 600;
color: var(--gray-700);
margin-bottom: 1rem;
}
.action-buttons {
display: flex;
gap: 1rem;
flex-wrap: wrap;
}
/* Toast styles */
.toast {
position: fixed;
top: 20px;
right: 20px;
background: var(--white);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-xl);
padding: 1rem;
z-index: 1000;
opacity: 0;
transform: translateX(100%);
transition: all 0.3s ease;
border-left: 4px solid;
}
.toast.show {
opacity: 1;
transform: translateX(0);
}
.toast.toast-success {
border-left-color: var(--success-color);
}
.toast.toast-info {
border-left-color: var(--info-color);
}
.toast-content {
display: flex;
align-items: center;
gap: 0.5rem;
font-weight: 500;
}
.toast.toast-success .toast-content {
color: var(--success-color);
}
.toast.toast-info .toast-content {
color: var(--info-color);
}
/* Form styles */
.form-group {
margin-bottom: 1.5rem;
}
.form-group label {
display: flex;
align-items: center;
gap: 0.5rem;
font-weight: 600;
color: var(--gray-700);
margin-bottom: 0.5rem;
font-size: 0.875rem;
}
.form-group input,
.form-group select,
.form-group textarea {
width: 100%;
padding: 0.75rem;
border: 2px solid var(--gray-200);
border-radius: var(--radius-lg);
font-size: var(--font-size-base);
transition: var(--transition);
background-color: var(--white);
}
.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
outline: none;
border-color: var(--primary-color);
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
}
.form-group input:disabled {
background-color: var(--gray-100);
color: var(--gray-500);
cursor: not-allowed;
}
.form-help {
display: block;
margin-top: 0.5rem;
font-size: 0.75rem;
color: var(--gray-500);
}
.form-actions {
display: flex;
justify-content: flex-start;
gap: 1rem;
margin-top: 2rem;
padding-top: 1.5rem;
border-top: 1px solid var(--gray-200);
}
.password-strength {
margin-top: 0.5rem;
font-size: 0.75rem;
padding: 0.5rem;
border-radius: var(--radius);
transition: var(--transition);
}
.password-strength.weak {
background-color: rgba(220, 38, 38, 0.1);
color: var(--danger-color);
}
.password-strength.medium {
background-color: rgba(217, 119, 6, 0.1);
color: var(--warning-color);
}
.password-strength.strong {
background-color: rgba(5, 150, 105, 0.1);
color: var(--success-color);
}
.password-match {
margin-top: 0.5rem;
font-size: 0.75rem;
padding: 0.5rem;
border-radius: var(--radius);
}
.password-match.match {
background-color: rgba(5, 150, 105, 0.1);
color: var(--success-color);
}
.password-match.no-match {
background-color: rgba(220, 38, 38, 0.1);
color: var(--danger-color);
}
@media (max-width: 768px) {
.profile-header {
flex-direction: column;
text-align: center;
gap: 1rem;
}
.profile-meta {
grid-template-columns: 1fr;
text-align: left;
}
.form-row {
grid-template-columns: 1fr;
}
.tab-headers {
flex-direction: column;
}
.tab-header {
border-bottom: none;
border-right: 3px solid transparent;
}
.tab-header.active {
border-bottom: none;
border-right-color: var(--primary-color);
}
.action-buttons {
flex-direction: column;
}
.form-actions {
flex-direction: column;
}
}
</style>
{% endblock %}