mirror of
https://github.com/DRYTRIX/TimeTracker.git
synced 2026-01-06 11:40:52 -06:00
- Add client_max_body_size 10M to nginx config to fix 413 error - Add JavaScript preview for profile picture selection - Include client-side validation for file size and type
95 lines
5.0 KiB
HTML
95 lines
5.0 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block content %}
|
|
<div class="flex flex-col md:flex-row justify-between items-start md:items-center mb-6">
|
|
<div>
|
|
<h1 class="text-2xl font-bold">Edit Profile</h1>
|
|
<p class="text-text-muted-light dark:text-text-muted-dark">Update your personal information.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bg-card-light dark:bg-card-dark p-6 rounded-lg shadow">
|
|
<form method="POST" enctype="multipart/form-data">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
|
<div class="space-y-6">
|
|
<div class="flex items-center gap-4">
|
|
<img id="avatar-preview" src="{{ current_user.get_avatar_url() or url_for('static', filename='images/avatar-default.svg') }}" alt="{{ current_user.display_name }}" class="w-16 h-16 rounded-full object-cover bg-gray-200 dark:bg-gray-700">
|
|
<div class="flex-1">
|
|
<label for="avatar" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Profile picture</label>
|
|
<input type="file" name="avatar" id="avatar" accept="image/png,image/jpeg,image/jpg,image/gif,image/webp" class="mt-1 block w-full text-sm text-gray-900 dark:text-gray-100 file:mr-4 file:py-2 file:px-4 file:rounded file:border-0 file:text-sm file:font-semibold file:bg-primary file:text-white hover:file:bg-primary/90" onchange="previewAvatar(this)">
|
|
<p class="mt-1 text-xs text-text-muted-light dark:text-text-muted-dark">PNG, JPG, GIF, or WEBP up to 5MB.</p>
|
|
{% if current_user.has_avatar() %}
|
|
<form method="POST" action="{{ url_for('auth.remove_avatar') }}" class="mt-2">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
|
<button type="submit" class="text-red-600 hover:text-red-700 text-sm">Remove current picture</button>
|
|
</form>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<label for="username" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Username</label>
|
|
<input type="text" name="username" id="username" value="{{ current_user.username }}" disabled class="form-input bg-gray-100 dark:bg-gray-700">
|
|
</div>
|
|
<div>
|
|
<label for="full_name" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Full Name</label>
|
|
<input type="text" name="full_name" id="full_name" value="{{ current_user.full_name or '' }}" class="form-input">
|
|
</div>
|
|
<div>
|
|
<label for="preferred_language" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Language</label>
|
|
<select name="preferred_language" id="preferred_language" class="form-input">
|
|
{% for code, label in config['LANGUAGES'].items() %}
|
|
<option value="{{ code }}" {% if (current_user.preferred_language or current_language_code) == code %}selected{% endif %}>{{ label }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label for="password" class="block text-sm font-medium text-gray-700 dark:text-gray-300">New Password</label>
|
|
<input type="password" name="password" id="password" class="form-input" placeholder="Leave blank to keep current password">
|
|
</div>
|
|
<div>
|
|
<label for="password_confirm" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Confirm New Password</label>
|
|
<input type="password" name="password_confirm" id="password_confirm" class="form-input">
|
|
</div>
|
|
</div>
|
|
<div class="mt-8 border-t border-border-light dark:border-border-dark pt-6 flex justify-end">
|
|
<a href="{{ url_for('auth.profile') }}" class="bg-gray-200 dark:bg-gray-700 px-4 py-2 rounded-lg mr-4">Cancel</a>
|
|
<button type="submit" class="bg-primary text-white px-4 py-2 rounded-lg">Save Changes</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<script>
|
|
function previewAvatar(input) {
|
|
const preview = document.getElementById('avatar-preview');
|
|
|
|
if (input.files && input.files[0]) {
|
|
const file = input.files[0];
|
|
|
|
// Validate file size (5MB limit)
|
|
if (file.size > 5 * 1024 * 1024) {
|
|
alert('File size must be less than 5MB');
|
|
input.value = '';
|
|
return;
|
|
}
|
|
|
|
// Validate file type
|
|
const allowedTypes = ['image/png', 'image/jpeg', 'image/jpg', 'image/gif', 'image/webp'];
|
|
if (!allowedTypes.includes(file.type)) {
|
|
alert('Invalid file type. Please select a valid image file (PNG, JPG, GIF, or WEBP).');
|
|
input.value = '';
|
|
return;
|
|
}
|
|
|
|
// Read and display the image
|
|
const reader = new FileReader();
|
|
reader.onload = function(e) {
|
|
preview.src = e.target.result;
|
|
};
|
|
reader.readAsDataURL(file);
|
|
}
|
|
}
|
|
</script>
|
|
{% endblock %}
|
|
|
|
|