Redesign profile photo upload with circular crop UI, server-side processing, and form integration

- Replace square preview with 280x280 circular container with crop ring overlay
- Zoom slider and drag-to-pan for precise crop positioning
- Client-side crop via canvas at 400x400 output resolution
- Server-side resize to 400x400 (LANCZOS), EXIF stripping, RGB conversion, JPEG optimization
- Generate CSRF token in before_request so photo upload fetch works
- Save button now also submits the profile form (photo + fields updated together)
- Remove dead photo-upload-form handler and unused CSS
- Clean up debug markers and attendee code field from profile page

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-27 07:10:52 +00:00
parent a3546b4c01
commit 3c26f1310e
4 changed files with 380 additions and 305 deletions
+3 -45
View File
@@ -20,6 +20,9 @@ document.addEventListener('DOMContentLoaded', function() {
const toastContainer = document.getElementById('toast-container');
function showToast(message, type = 'info', duration = 5000) {
// Expose globally for inline scripts
window.showToast = showToast;
const toast = document.createElement('div');
toast.className = `toast toast-${type}`;
@@ -167,51 +170,6 @@ document.addEventListener('DOMContentLoaded', function() {
});
});
// ----- Photo Upload Form -----
const photoForm = document.querySelector('.photo-upload-form');
if (photoForm) {
const submitBtn = photoForm.querySelector('button[type="submit"]');
photoForm.addEventListener('submit', async function(e) {
e.preventDefault();
// Add loading state
if (submitBtn) {
submitBtn.classList.add('btn-loading');
submitBtn.disabled = true;
}
const formData = new FormData(this);
try {
const response = await fetch(this.action, {
method: 'POST',
body: formData
});
const data = await response.json();
if (data.success) {
showToast('Photo updated successfully!', 'success');
// Reload page to show new photo
setTimeout(() => location.reload(), 1000);
} else {
showToast(data.error || 'Error uploading photo', 'error');
if (submitBtn) {
submitBtn.classList.remove('btn-loading');
submitBtn.disabled = false;
}
}
} catch (error) {
showToast('Error uploading photo', 'error');
if (submitBtn) {
submitBtn.classList.remove('btn-loading');
submitBtn.disabled = false;
}
}
});
}
// ----- Connection Request Buttons -----
const connectForms = document.querySelectorAll('.connect-form');
connectForms.forEach(form => {