From 4da2223f3ba90fbd74ff261ba752f251717a1d8a Mon Sep 17 00:00:00 2001 From: Saahi30 Date: Tue, 23 Sep 2025 06:14:39 +0530 Subject: [PATCH 1/4] fix: reapir brand signup flow(frontend) --- Frontend/src/components/Onboarding.tsx | 19 ++- Frontend/src/pages/Profile.tsx | 196 +++++++++++++++++++++++++ 2 files changed, 211 insertions(+), 4 deletions(-) create mode 100644 Frontend/src/pages/Profile.tsx diff --git a/Frontend/src/components/Onboarding.tsx b/Frontend/src/components/Onboarding.tsx index 950b09e..15bd3b6 100644 --- a/Frontend/src/components/Onboarding.tsx +++ b/Frontend/src/components/Onboarding.tsx @@ -443,7 +443,7 @@ export default function Onboarding() { // 1. Upload profile picture if provided if (profilePic) { setProgress(20); - const fileExt = profilePic.name.split('.').pop(); + const fileExt = profilePic.name ? profilePic.name.split('.').pop() : ''; const fileName = `${user?.id}_${Date.now()}.${fileExt}`; const { data, error } = await supabase.storage.from('profile-pictures').upload(fileName, profilePic); if (error) throw error; @@ -942,9 +942,20 @@ export default function Onboarding() { setBrandSubmitSuccess(""); let logo_url = null; try { + // 0. Ensure user exists in users table (upsert) + if (user) { + const upsertUser = { + id: user.id, + email: user.email, + username: user.user_metadata?.name || user.email || `user_${user.id}`, + role: 'brand', + }; + const { error: upsertError } = await supabase.from('users').upsert(upsertUser, { onConflict: 'id' }); + if (upsertError) throw upsertError; + } // 1. Upload logo if provided if (brandData.logo) { - const fileExt = brandData.logo.name.split('.').pop(); + const fileExt = brandData.logo.name ? brandData.logo.name.split('.').pop() : ''; const fileName = `${user?.id}_${Date.now()}.${fileExt}`; const { data, error } = await supabase.storage.from('brand-logos').upload(fileName, brandData.logo); if (error) throw error; @@ -992,8 +1003,8 @@ export default function Onboarding() {

Review & Submit

- {(brandLogoPreview || brandData.logo) ? ( - Logo Preview + {(brandLogoPreview || (brandData.logo instanceof File ? URL.createObjectURL(brandData.logo) : undefined)) ? ( + Logo Preview ) : (
No Logo
)} diff --git a/Frontend/src/pages/Profile.tsx b/Frontend/src/pages/Profile.tsx new file mode 100644 index 0000000..fa0b7aa --- /dev/null +++ b/Frontend/src/pages/Profile.tsx @@ -0,0 +1,196 @@ +import React, { useEffect, useMemo, useState } from "react"; +import { userApi, UserProfile } from "../services/userApi"; +import { useAuth } from "../context/AuthContext"; + +const container: React.CSSProperties = { + maxWidth: 840, + margin: "40px auto", + padding: 24, + background: "rgba(26,26,26,0.6)", + border: "1px solid rgba(255,255,255,0.08)", + borderRadius: 16, + backdropFilter: "blur(16px)", + color: "#fff", +}; + +const field: React.CSSProperties = { display: "flex", flexDirection: "column", gap: 8, marginBottom: 16 }; +const label: React.CSSProperties = { fontSize: 13, color: "#a0a0a0" }; +const inputBase: React.CSSProperties = { + background: "#121212", + border: "1px solid #2a2a2a", + color: "#fff", + borderRadius: 10, + padding: "10px 12px", +}; +const actions: React.CSSProperties = { display: "flex", gap: 12, justifyContent: "flex-end", marginTop: 20 }; + +export default function ProfilePage() { + const { user } = useAuth(); + const [loading, setLoading] = useState(true); + const [saving, setSaving] = useState(false); + const [error, setError] = useState(null); + const [profile, setProfile] = useState(null); + const [username, setUsername] = useState(""); + const [email, setEmail] = useState(""); + const [bio, setBio] = useState(""); + const [role, setRole] = useState(undefined); + const [imageFile, setImageFile] = useState(null); + + const disabled = useMemo(() => saving || loading, [saving, loading]); + + useEffect(() => { + let mounted = true; + (async () => { + try { + setLoading(true); + const p = await userApi.getProfile(); + if (!mounted) return; + setProfile(p); + setUsername(p.username ?? ""); + setEmail(p.email ?? user?.email ?? ""); + setBio(p.bio ?? ""); + setRole((p.role ?? undefined) as string | undefined); + setError(null); + } catch (e: any) { + if (!mounted) return; + setError(e?.message || "Failed to load profile"); + } finally { + if (mounted) setLoading(false); + } + })(); + return () => { mounted = false; }; + }, [user?.id]); + + const onSelectImage: React.ChangeEventHandler = (e) => { + const f = e.target.files?.[0] || null; + if (!f) return setImageFile(null); + if (!f.type.startsWith("image/")) { setError("Please select an image file"); return; } + if (f.size > 5 * 1024 * 1024) { setError("Max file size is 5MB"); return; } + setError(null); + setImageFile(f); + }; + + const onSave = async () => { + try { + setSaving(true); + setError(null); + + // 1) Upload image first if any (backend returns updated profile) + if (imageFile) { + const updatedAfterImage = await userApi.uploadProfileImage(imageFile); + setProfile(updatedAfterImage); + } + + // 2) Update profile fields + const payload: Partial = { + username: username.trim() || undefined, + bio: bio.trim() || undefined, + // role is generally set during onboarding; do not allow arbitrary changes here unless present + role: role as any, + // profile_image_url already updated if image was uploaded above + }; + const updated = await userApi.updateProfile(payload); + setProfile(updated); + if (updated.username) setUsername(updated.username); + if (updated.email) setEmail(updated.email); + if (updated.bio) setBio(updated.bio); + if (updated.role) setRole(updated.role); + if (updated.profile_image_url) setImageFile(null); + } catch (e: any) { + setError(e?.message || "Failed to save profile"); + } finally { + setSaving(false); + } + }; + + return ( +
+
+

Your Profile

+ {loading && Loading…} +
+ + {error && ( +
{error}
+ )} + + {/* Avatar */} +
+
+ {profile?.profile_image_url ? ( + avatar + ) : ( +
+ )} +
+ +
+ + {/* Fields */} +
+ + setUsername(e.target.value)} + placeholder="Your display name" + disabled={disabled} + /> +
+ +
+ + +
+ +
+ +