From 8961fa701ac806ea5e87116fbaec3c71e9e7976b Mon Sep 17 00:00:00 2001 From: delta-lynx-89e8 Date: Sun, 22 Feb 2026 13:09:41 -0800 Subject: [PATCH] Wire avatar upload on Create/Update profile pages Camera button now triggers file input -> uploads to POST /users/avatar -> updates auth context and preview. Co-Authored-By: Claude Opus 4.6 --- client/src/pages/CreateProfilePage.tsx | 23 ++++++++++++++++++++--- client/src/pages/UpdateProfilePage.tsx | 21 ++++++++++++++++++--- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/client/src/pages/CreateProfilePage.tsx b/client/src/pages/CreateProfilePage.tsx index 6395d90..fb4a22a 100644 --- a/client/src/pages/CreateProfilePage.tsx +++ b/client/src/pages/CreateProfilePage.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useState, useRef } from 'react'; import { useNavigate } from 'react-router-dom'; import { User, Mail, Phone, Camera } from 'lucide-react'; import { Input } from '../components/ui/Input'; @@ -19,6 +19,22 @@ export function CreateProfilePage() { const [bio, setBio] = useState(''); const [saving, setSaving] = useState(false); const [error, setError] = useState(''); + const [avatarPreview, setAvatarPreview] = useState(user?.avatar || undefined); + const fileInputRef = useRef(null); + + const handleAvatarChange = async (e: React.ChangeEvent) => { + const file = e.target.files?.[0]; + if (!file) return; + setAvatarPreview(URL.createObjectURL(file)); + try { + const formData = new FormData(); + formData.append('avatar', file); + const result = await api.upload>('/users/avatar', formData); + updateUser(result); + } catch { + setError('Failed to upload avatar'); + } + }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); @@ -51,8 +67,9 @@ export function CreateProfilePage() {
- -
diff --git a/client/src/pages/UpdateProfilePage.tsx b/client/src/pages/UpdateProfilePage.tsx index 15e4068..8a17040 100644 --- a/client/src/pages/UpdateProfilePage.tsx +++ b/client/src/pages/UpdateProfilePage.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useState, useRef } from 'react'; import { useNavigate } from 'react-router-dom'; import { Camera } from 'lucide-react'; import { Input } from '../components/ui/Input'; @@ -23,6 +23,20 @@ export function UpdateProfilePage() { const [showPhone, setShowPhone] = useState(user?.showPhone ?? true); const [showLocation, setShowLocation] = useState(user?.showLocation ?? true); const [saving, setSaving] = useState(false); + const [avatarPreview, setAvatarPreview] = useState(user?.avatar || undefined); + const fileInputRef = useRef(null); + + const handleAvatarChange = async (e: React.ChangeEvent) => { + const file = e.target.files?.[0]; + if (!file) return; + setAvatarPreview(URL.createObjectURL(file)); + try { + const formData = new FormData(); + formData.append('avatar', file); + const result = await api.upload>('/users/avatar', formData); + updateUser(result); + } catch {} + }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); @@ -47,8 +61,9 @@ export function UpdateProfilePage() {
- -