feat: implement optimistic profile updates and add navigation to profile view

This commit is contained in:
2026-04-21 16:14:00 +01:00
parent e97711ab2f
commit 6c418d9c1f

View File

@@ -272,6 +272,7 @@ export default function App() {
e.preventDefault();
setSavingProfile(true);
const fd = new FormData(e.target);
try {
const profileDoc = doc(db, 'artifacts', appId, 'users', user.uid, 'profile', 'data');
const dobDay = fd.get('dobDay');
@@ -282,16 +283,22 @@ export default function App() {
dob = `${dobYear}-${dobMonth}-${dobDay}`;
}
await setDoc(profileDoc, {
// Perform optimistc setDoc without blocking the UI
setDoc(profileDoc, {
username: fd.get('username') || '',
fullName: fd.get('fullName') || '',
dob: dob,
bio: fd.get('bio') || ''
}, { merge: true });
}, { merge: true }).catch(err => {
console.error(err);
});
} catch (err) {
console.error(err);
} finally {
// Re-enable the button shortly after for smooth optimistic UI
setTimeout(() => {
setSavingProfile(false);
}, 600);
}
};
@@ -339,12 +346,12 @@ export default function App() {
${sidebarOpen ? 'w-80 translate-x-0' : 'w-0 -translate-x-full md:w-0 md:opacity-0'}
`}>
<div className="p-10 h-full flex flex-col backdrop-blur-xl">
<div className="flex items-center gap-4 mb-16">
<button onClick={() => setView('closet')} className="flex items-center gap-4 mb-16 hover:scale-[1.02] transition-transform text-left cursor-pointer w-full">
<div className="p-3 bg-primary-600 rounded-2xl shadow-xl shadow-primary-600/30">
<Shirt className="text-white" size={24} />
</div>
<span className="text-3xl font-black tracking-tighter italic">MyCloset</span>
</div>
</button>
<nav className="flex-1 space-y-3">
{[
@@ -366,15 +373,15 @@ export default function App() {
</nav>
<div className="mt-auto pt-10 border-t border-inherit">
<div className="flex items-center gap-4 mb-8 px-2">
<div className={`w-12 h-12 rounded-2xl flex items-center justify-center font-black text-white shadow-xl ${darkMode ? 'bg-primary-500' : 'bg-primary-600'}`}>
<button onClick={() => setView('profile')} className="w-full flex items-center gap-4 mb-8 px-2 text-left hover:bg-gray-100 dark:hover:bg-gray-800 py-3 rounded-2xl transition-all cursor-pointer">
<div className={`w-12 h-12 rounded-2xl shrink-0 flex items-center justify-center font-black text-white shadow-xl ${darkMode ? 'bg-primary-500' : 'bg-primary-600'}`}>
{(userProfile?.fullName?.[0] || userProfile?.username?.[0] || user?.email?.[0] || 'U').toUpperCase()}
</div>
<div className="flex-1 min-w-0">
<p className="text-sm font-black truncate">{userProfile?.username || userProfile?.fullName || user?.email?.split('@')[0] || t('userTitle')}</p>
<Badge variant="success">{t('online')}</Badge>
</div>
</div>
</button>
<button onClick={() => signOut(auth)} className="w-full py-4 text-red-500 font-black uppercase tracking-widest text-[10px] hover:bg-red-500/10 rounded-2xl transition-all flex items-center justify-center gap-3">
<LogOut size={16} /> {t('logout')}
</button>
@@ -397,6 +404,7 @@ export default function App() {
{view === 'laundry' && t('laundry')}
{view === 'outfits' && t('outfitsAndStyle')}
{view === 'settings' && t('settings')}
{view === 'profile' && t('profileInfo')}
</h2>
</div>
@@ -690,8 +698,8 @@ export default function App() {
</div>
)}
{/* DEFINIÇÕES */}
{view === 'settings' && (
{/* PERFIL */}
{view === 'profile' && (
<div className="max-w-4xl mx-auto space-y-12 animate-in fade-in duration-700 pb-20">
<Card className="p-10 border-primary-100 relative overflow-hidden" darkMode={darkMode}>
<div className="flex items-center gap-8 relative z-10 text-inherit">
@@ -735,6 +743,12 @@ export default function App() {
</button>
</form>
</Card>
</div>
)}
{/* DEFINIÇÕES */}
{view === 'settings' && (
<div className="max-w-4xl mx-auto space-y-12 animate-in fade-in duration-700 pb-20">
{/* Preferências */}
<div className="flex flex-col gap-8">