fix: reapir brand signup flow(frontend) #120
fix: reapir brand signup flow(frontend) #120Saahi30 wants to merge 4 commits intoAOSSIE-Org:mainfrom
Conversation
WalkthroughAdds memoized object URLs and cleanup for profile and brand image previews; inserts pre-submit user upsert and refined upload flows in onboarding/brand submission; introduces a new Profile page component that loads, validates, previews, and saves username, bio, and avatar with optional image upload. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor U as User
participant OB as Onboarding Component
participant Auth as Auth Context
participant API as App API
participant Storage as Object Storage / DB
U->>OB: Submit onboarding / brand
OB->>Auth: get current user (id,email)
OB->>API: upsert user {id,email,username,role}
API->>Storage: upsert users table
Storage-->>API: upsert ok
API-->>OB: user exists
alt logo/profile is File
OB->>Storage: upload file (contentType, cacheControl, filename)
Storage-->>OB: file URL
end
OB->>API: submit onboarding data (including uploaded URLs)
API-->>OB: success
Note right of OB: ObjectURLs are memoized and revoked on change/unmount
sequenceDiagram
autonumber
actor U as User
participant P as ProfilePage
participant Auth as Auth Context
participant UA as userApi
participant Store as Image Storage
U->>P: Open profile page
P->>Auth: get current user
P->>UA: getProfile(userId)
UA-->>P: profile {username,bio,avatar_url,role}
U->>P: Edit fields / choose image
alt Image selected
P->>Store: uploadProfileImage(file, contentType)
Store-->>P: {avatar_url}
end
P->>UA: updateProfile({username,bio,avatar_url?})
UA-->>P: success
P-->>U: Save complete
Note right of P: previews use memoized ObjectURL and are revoked on change/unmount
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (6)
Frontend/src/components/Onboarding.tsx (6)
446-451: Avoid empty/unsafe extensions and set contentType on uploadIf the file has no extension, you create a trailing dot. Also, set contentType for correct headers.
Apply:
- 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); + const ext = profilePic.name.includes('.') ? profilePic.name.split('.').pop()!.toLowerCase() : undefined; + const fileName = `${user?.id}_${Date.now()}${ext ? `.${ext}` : ''}`; + const { data, error } = await supabase + .storage.from('profile-pictures') + .upload(fileName, profilePic, { contentType: profilePic.type, cacheControl: '3600', upsert: false });
456-466: Guard auth and upsert the user (avoid eq('id', undefined) and missing row updates)If user is null/undefined, the update can be a no-op and miss creating the row. Upsert and coerce age to number.
- // 2. Update users table - const categoryToSave = personal.category === 'Other' ? personal.otherCategory : personal.category; - const { error: userError } = await supabase.from('users').update({ - username: personal.name, - age: personal.age, - gender: personal.gender, - country: personal.country, - category: categoryToSave, - profile_image: profile_image_url, - role, - }).eq('id', user?.id); + // 2. Ensure auth and upsert users row + if (!user?.id || !user?.email) throw new Error('You must be signed in to submit onboarding.'); + const categoryToSave = personal.category === 'Other' ? personal.otherCategory : personal.category; + const { error: userError } = await supabase.from('users').upsert({ + id: user.id, + email: user.email, + username: personal.name, + age: Number(personal.age), + gender: personal.gender, + country: personal.country, + category: categoryToSave, + profile_image: profile_image_url, + role, + }, { onConflict: 'id' });
418-421: Stop creating new ObjectURLs on every render; memoize and revokeRepeated URL.createObjectURL calls leak. Memoize once per file and revoke on change/unmount.
- src={profilePic ? URL.createObjectURL(profilePic) : user?.user_metadata?.avatar_url} + src={profilePicUrl ?? user?.user_metadata?.avatar_url}Add near the component state (after line 88):
+ const profilePicUrl = useMemo(() => (profilePic ? URL.createObjectURL(profilePic) : null), [profilePic]); + useEffect(() => () => { if (profilePicUrl) URL.revokeObjectURL(profilePicUrl); }, [profilePicUrl]);Also applies to: 544-545
957-964: Brand logo: ensure File type, sanitize extension, set contentTypeLocalStorage rehydration can make logo an object; gate with instanceof File. Also fix extension and headers.
- if (brandData.logo) { - 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 (brandData.logo instanceof File) { + const ext = brandData.logo.name.includes('.') ? brandData.logo.name.split('.').pop()!.toLowerCase() : undefined; + const fileName = `${user!.id}_${Date.now()}${ext ? `.${ext}` : ''}`; + const { data, error } = await supabase + .storage.from('brand-logos') + .upload(fileName, brandData.logo, { contentType: brandData.logo.type, cacheControl: '3600', upsert: false });
940-956: Brand submit: enforce auth before proceedingFail fast if not signed in to avoid undefined user.id in filenames and DB rows.
const handleBrandSubmit = async () => { setBrandSubmitting(true); setBrandSubmitError(""); setBrandSubmitSuccess(""); let logo_url = null; try { + if (!user?.id || !user?.email) throw new Error("Please sign in to finish brand onboarding."); // 0. Ensure user exists in users table (upsert) if (user) {
1066-1075: Fix brandData rehydration: File isn’t serializableAfter JSON.parse, logo becomes a plain object. Normalize to null to avoid runtime upload errors.
- if (savedStep) setBrandStep(Number(savedStep)); - if (savedData) setBrandData(JSON.parse(savedData)); + if (savedStep) setBrandStep(Number(savedStep)); + if (savedData) { + const parsed = JSON.parse(savedData); + if (parsed.logo) parsed.logo = null; + setBrandData(parsed); + }
🧹 Nitpick comments (5)
Frontend/src/components/Onboarding.tsx (3)
965-989: Make brand insert idempotentUse upsert on user_id to prevent duplicate brand rows on retries.
- const { error: brandError } = await supabase.from('brands').insert({ + const { error: brandError } = await supabase.from('brands').upsert({ user_id: user?.id, brand_name: brandData.brand_name, logo_url, website_url: brandData.website_url, industry: brandData.industry, company_size: brandData.company_size, location: brandData.location, description: brandData.description, contact_person: brandData.contact_person, contact_email: brandData.contact_email, contact_phone: brandData.contact_phone, role: brandData.role, instagram_url: brandData.social_links.instagram_url || null, facebook_url: brandData.social_links.facebook_url || null, twitter_url: brandData.social_links.twitter_url || null, linkedin_url: brandData.social_links.linkedin_url || null, youtube_url: brandData.social_links.youtube_url || null, collaboration_types: brandData.collaboration_types, preferred_creator_categories: brandData.preferred_creator_categories, brand_values: brandData.brand_values, preferred_tone: brandData.preferred_tone, platforms: brandData.platforms, - }); + }, { onConflict: 'user_id' });
165-166: Fix asset path typoImage likely broken: “contnetcreator.png”.
- <img src="/contnetcreator.png" alt="Content Creator" className="h-20 w-20 mb-2" /> + <img src="/contentcreator.png" alt="Content Creator" className="h-20 w-20 mb-2" />
1066-1075: Optional: persist selected role across reloadsImproves UX when resuming brand onboarding.
useEffect(() => { const savedStep = localStorage.getItem("brandStep"); const savedData = localStorage.getItem("brandData"); + const savedRole = localStorage.getItem("onboardingRole"); if (savedStep) setBrandStep(Number(savedStep)); if (savedData) { const parsed = JSON.parse(savedData); if (parsed.logo) parsed.logo = null; setBrandData(parsed); } + if (savedRole) setRole(savedRole); }, []); useEffect(() => { localStorage.setItem("brandStep", String(brandStep)); localStorage.setItem("brandData", JSON.stringify(brandData)); + if (role) localStorage.setItem("onboardingRole", role); }, [brandStep, brandData, role]);Frontend/src/pages/Profile.tsx (2)
78-99: Save flow looks solid; minor: avoid sending role unless intendedYou currently send role back even though it’s not editable. Consider omitting to prevent accidental changes.
- const payload: Partial<UserProfile> = { + const payload: Partial<UserProfile> = { 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, + // role: keep server source of truth; omit here unless explicitly editable
64-71: Image validation LGTM; add early clear of prior error on new pickTiny UX nit: clear error when a new valid file is selected.
- const onSelectImage: React.ChangeEventHandler<HTMLInputElement> = (e) => { + const onSelectImage: React.ChangeEventHandler<HTMLInputElement> = (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); + setError(null); setImageFile(f); };
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
Frontend/src/components/Onboarding.tsx(3 hunks)Frontend/src/pages/Profile.tsx(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
Frontend/src/components/Onboarding.tsx (1)
Frontend/src/utils/supabase.tsx (1)
supabase(11-11)
Frontend/src/pages/Profile.tsx (1)
Frontend/src/context/AuthContext.tsx (1)
useAuth(216-222)
🔇 Additional comments (1)
Frontend/src/pages/Profile.tsx (1)
41-62: Verify userApi exports and return types used by Profile.tsx
- Confirm getProfile, uploadProfileImage, updateProfile and the UserProfile type are exported from Frontend/src/services/userApi.
- Ensure those functions return an object with the fields consumed in Frontend/src/pages/Profile.tsx (username, email, bio, role, profile_image_url).
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (5)
Frontend/src/components/Onboarding.tsx (5)
666-676: Validate logo file type/size and clear errors when validAdd basic guards to prevent huge/non-image uploads for brand logos.
Apply:
const handleBrandLogoChange = (e: React.ChangeEvent<HTMLInputElement>) => { if (e.target.files && e.target.files[0]) { const file = e.target.files[0]; + if (!file.type.startsWith('image/')) { + setBrandError('Please select a valid image file.'); + return; + } + if (file.size > 3 * 1024 * 1024) { + setBrandError('Logo must be less than 3MB.'); + return; + } // Revoke previous preview if present if (brandLogoPreview) { try { URL.revokeObjectURL(brandLogoPreview); } catch {} } const url = URL.createObjectURL(file); setBrandData({ ...brandData, logo: file }); setBrandLogoPreview(url); + setBrandError(''); } };
463-469: Storage path hygiene (optional)Consider namespacing uploads by user to avoid flat bucket clutter and improve traceability.
Apply:
- const { data, error } = await supabase + const filePath = `${user!.id}/${fileName}`; + const { data, error } = await supabase .storage.from('profile-pictures') - .upload(fileName, profilePic, { contentType: profilePic.type, cacheControl: '3600', upsert: false }); + .upload(filePath, profilePic, { contentType: profilePic.type, cacheControl: '3600', upsert: false }); - profile_image_url = `${supabase.storage.from('profile-pictures').getPublicUrl(fileName).data.publicUrl}`; + profile_image_url = supabase.storage.from('profile-pictures').getPublicUrl(filePath).data.publicUrl;
1037-1039: Simplify: rely solely on brandLogoPreviewThe ternaries are redundant; just gate on brandLogoPreview.
Apply:
- {(brandLogoPreview || (brandData.logo instanceof File ? brandLogoPreview : undefined)) ? ( - <img src={(brandLogoPreview || (brandData.logo instanceof File ? brandLogoPreview : undefined)) ?? ''} alt="Logo Preview" className="h-16 w-16 rounded-full object-cover border mb-2" /> + {brandLogoPreview ? ( + <img src={brandLogoPreview} alt="Logo Preview" className="h-16 w-16 rounded-full object-cover border mb-2" />
1101-1105: Clear preview on restored stateWhen restoring brandData (logo reset to null), also clear brandLogoPreview to avoid stale previews in long-lived mounts.
Apply:
if (savedData) { const parsed = JSON.parse(savedData); if (parsed.logo) parsed.logo = null; setBrandData(parsed); + setBrandLogoPreview(null); }
182-183: Fix asset filename typoImage likely won’t load.
Apply:
- <img src="/contnetcreator.png" alt="Content Creator" className="h-20 w-20 mb-2" /> + <img src="/contentcreator.png" alt="Content Creator" className="h-20 w-20 mb-2" />
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
Frontend/src/components/Onboarding.tsx(9 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
Frontend/src/components/Onboarding.tsx (1)
Frontend/src/utils/supabase.tsx (1)
supabase(11-11)
🔇 Additional comments (3)
Frontend/src/components/Onboarding.tsx (3)
89-96: Good: memoized ObjectURL + cleanupAvoids leaks and redundant URLs for the profile picture.
562-569: Stop calling URL.createObjectURL during render (profile preview in Review step)This recreates a blob URL every render and isn’t revoked. Reuse profilePicUrl like you did in the upload step.
Apply:
- {(profilePic || user?.user_metadata?.avatar_url) ? ( + {(profilePicUrl || user?.user_metadata?.avatar_url) ? ( <img - src={profilePic ? URL.createObjectURL(profilePic) : user?.user_metadata?.avatar_url} + src={profilePicUrl ?? user?.user_metadata?.avatar_url} alt="Profile Preview" className="h-20 w-20 rounded-full object-cover border-2 border-purple-500" />Run to ensure no other render-path usages remain:
#!/bin/bash rg -n -C2 -g 'Frontend/**' 'createObjectURL\(' --type tsx
973-985: Upserting users with role='brand' may overwrite an existing role — confirm business rule
If users can hold multiple roles (e.g., creator + brand) a single role string is lossy; use an array of roles or a join table.
Location: Frontend/src/components/Onboarding.tsx (lines 973–985).
Verification attempt failed (repo scan returned "No such file or directory"); re-run a repo-wide search for upsert/insert/update to the 'users' table or manually confirm whether this is the only place writing users.role before merging.
Closes #
📝 Description
Fixes several frontend issues in the brand signup/onboarding flow to make registration, login, password reset, and onboarding navigation reliable for new Brand users. This PR focuses on the client-side flow and UX (no backend schema or DB changes).
Context:
Users experienced broken or confusing flows during brand signup and onboarding.
Google OAuth and password-based signup had edge-case handling and redirect issues.
Onboarding redirects were inconsistent after successful sign-up or OAuth.
🔧 Changes Made
Refactored and fixed signup flow logic in [Signup.tsx]
Improved duplicate-account detection and error messages.
Fixed password validation and strength display behavior.
Ensured Supabase signUp/signInWithOAuth calls are handled consistently.
Fixed login flow in [Login.tsx]
Improved sign-in error handling and OAuth fallback behavior.
Improved password recovery flow:
[ForgotPassword.tsx] validates email existence and shows clear guidance when no account exists.
[ResetPassword.tsx] applies the password update reliably after reset.
Onboarding & role selection:
[RoleSelection.tsx] clarified navigation and ensured chosen role persists to onboarding.
[Onboarding.tsx] fixed onboarding submission, persistence and redirects to the correct dashboard after completion.
Profile page adjustments:
[Profile.tsx] made certain profile/role reads safe during onboarding and removed accidental edits to role.
Minor UX / error messaging improvements across auth flows:
Standardized messages for authentication failures and session expiry.
-->
✅ Checklist
Summary by CodeRabbit
New Features
Bug Fixes