Skip to content

Conversation

@Saahi30
Copy link
Collaborator

@Saahi30 Saahi30 commented Sep 23, 2025

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

  • [✅ ] I have read the contributing guidelines.

Summary by CodeRabbit

  • New Features

    • Added a Profile page to view and edit username, bio, and avatar, with image preview, validation, and save flow.
    • Enhanced onboarding to support profile picture and brand logo uploads with improved preview and review steps.
  • Bug Fixes

    • Fixed preview reliability and removed stale file previews; previews now clean up to prevent memory leaks.
    • Improved upload robustness when filenames are missing and ensured user is established before submission.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 23, 2025

Walkthrough

Adds 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

Cohort / File(s) Summary
Onboarding flow & previews
Frontend/src/components/Onboarding.tsx
- Memoize profile picture preview URL and revoke ObjectURLs on change/unmount
- Revoke previous brand logo preview and avoid storing stale File objects when restoring state
- Pre-submit upsert of user (id,email,username,role) before onboarding/brand uploads
- Uploads profile and brand logos with contentType/cacheControl and proper filename handling (guarded extension extraction)
New profile management page
Frontend/src/pages/Profile.tsx
- Adds ProfilePage default export component
- Loads profile on mount via auth/userApi, displays editable username, bio, avatar, and role
- Validates image type/size, previews via ObjectURL, uploads image then updates profile; handles loading, disabled and error states

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
Loading
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
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

A hop, a bop, I tweak the view,
Revoked old URLs to keep things new.
I upsert names and logos bright,
Previewing selfies in soft daylight.
Thump-thump—saved; I nibble a carrot bite 🥕🐇

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "fix: reapir brand signup flow(frontend)" accurately reflects the primary intent of the changeset—fixing the brand signup/onboarding flow on the frontend—so it is related to the PR objectives and specific enough for a reviewer scanning history; however it contains a spelling error ("reapir") and a minor readability issue with the missing space before the parenthetical.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 upload

If 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 revoke

Repeated 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 contentType

LocalStorage 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 proceeding

Fail 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 serializable

After 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 idempotent

Use 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 typo

Image 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 reloads

Improves 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 intended

You 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 pick

Tiny 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

📥 Commits

Reviewing files that changed from the base of the PR and between a3be437 and 4da2223.

📒 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).

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (5)
Frontend/src/components/Onboarding.tsx (5)

666-676: Validate logo file type/size and clear errors when valid

Add 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 brandLogoPreview

The 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 state

When 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 typo

Image 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

📥 Commits

Reviewing files that changed from the base of the PR and between 4da2223 and 0e87d02.

📒 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 + cleanup

Avoids 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant