Skip to content

Dev - #66

Merged
dev-rathore merged 32 commits into
masterfrom
dev
Jan 11, 2025
Merged

Dev#66
dev-rathore merged 32 commits into
masterfrom
dev

Conversation

@dev-rathore

@dev-rathore dev-rathore commented Jan 11, 2025 •

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features

    • Introduced a new BrandProtectedScreen component to manage brand data loading for authenticated users
    • Added a LoadingScreen component to display loading indicators during data fetching
  • Chores

    • Updated shared-uis subproject commit

Manan-Sharma-5 and others added 28 commits January 2, 2025 16:47
* redesign application and invitation cards
* repurpose messaging screen

* accept application changes

* added phases

* invitationId as influencerId

* fixed signup signin

* fix: bug on brand creation

* minor changes

* minor changes

* minor changes

* minor changes
* fix: accept application

* fix: managerId
* fix: initial redirections

* scheme change

* router navigate fix

* router navigate fix
* redirection changes

* redirection changes
* added no brands protected screen and shimmer ui

* shimmer ui update

* added loading indicator
@dev-rathore dev-rathore self-assigned this Jan 11, 2025
@coderabbitai

coderabbitai Bot commented Jan 11, 2025 •

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

Walkthrough

The pull request introduces a new React functional component BrandProtectedScreen in the layouts/protected/brand-protected-screen.tsx file. This component manages the loading state and fetches brand data for an authenticated user's manager. It uses React hooks to handle asynchronous data fetching, checks for a manager's presence, and queries Firestore for brands. If no brands are found, the user is redirected to an onboarding screen. The component includes a loading indicator for different platforms.

Changes

File Change Summary
layouts/protected/brand-protected-screen.tsx Added new React component BrandProtectedScreen with loading state management, brand data fetching, and conditional rendering
shared-uis Updated subproject commit from e071922330d1f2682e4088d152f4416395160e1f to c30d9b16f866327299696e4f97cd55263eda6e5a

Sequence Diagram

sequenceDiagram
    participant User
    participant BrandProtectedScreen
    participant Firestore
    
    User->>BrandProtectedScreen: Authenticate
    BrandProtectedScreen->>Firestore: Fetch Brands
    alt Brands Found
        Firestore-->>BrandProtectedScreen: Return Brand Data
        BrandProtectedScreen->>User: Render Content
    else No Brands
        BrandProtectedScreen->>User: Redirect to Onboarding
    end
Loading

Possibly related PRs

Suggested reviewers

  • Manan-Sharma-5

Poem

🐰 A Rabbit's Ode to Brand Protection 🔒

Loading brands with graceful might,
Firestore queries shining bright,
Managers' data, smooth and clean,
Redirecting where we've never been!
Code hops forward, no delay! 🚀


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between abf9dec and bc8b2db.

📒 Files selected for processing (1)
  • shared-uis (1 hunks)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🔭 Outside diff range comments (2)
contexts/auth-context.provider.tsx (1)

Line range hint 183-189: Consider extracting API URL to environment variables.

The hardcoded API URL should be moved to environment variables for better configuration management.

+// In .env file
+CHAT_API_URL=https://be.trendly.pro/api/v1/chat/auth

+// In the code
-            await fetch("https://be.trendly.pro/api/v1/chat/auth", {
+            await fetch(process.env.CHAT_API_URL, {
components/profile/index.tsx (1)

Line range hint 59-94: Consider handling potential race conditions in profile update.

The updateProfile function updates both the profile image and user data. If the image upload fails, the profile data might still be updated, leading to inconsistency.

Consider wrapping the updates in a transaction:

 try {
+  const batch = writeBatch(FirestoreDB);
   let updatedImageURL = manager.profileImage || "";

   if (imageToUpload) {
     // ... image upload logic ...
     updatedImageURL = await getDownloadURL(storageRef);
   }

-  await updateDoc(managerRef, {
+  batch.update(managerRef, {
     profileImage: updatedImageURL,
     name: name || manager.name,
   });
   if (selectedBrand) {
     const brandMemberRef = doc(
       FirestoreDB,
       "brands",
       selectedBrand.id,
       "members",
       manager.id
     );
-    await updateDoc(brandMemberRef, {
+    batch.update(brandMemberRef, {
       role,
     });
   }
+  await batch.commit();
   setImageToUpload("");
   Toaster.success("Profile updated successfully");
   fetchRole();
🧹 Nitpick comments (19)
layouts/protected/brand-protected-screen.tsx (1)

69-92: Consider component reusability

The LoadingScreen component could be moved to a shared components directory since loading states are commonly needed across the app.

Consider moving this to @/components/ui/loading-screen for better reusability.

components/shimmers/explore-influencer-shimmer.tsx (1)

56-62: Consider extracting bottom navigation items count to a constant.

The magic number 5 in the Array constructor could be extracted to a constant for better maintainability.

+const BOTTOM_NAV_ITEMS_COUNT = 5;
+
 const ExploreInfluencerShimmer = () => {
   // ...
   <View style={styles.bottomNav}>
-    {[...Array(5)].map((_, index) => (
+    {[...Array(BOTTOM_NAV_ITEMS_COUNT)].map((_, index) => (
       <Shimmer
         key={index}
         width={56}
         height={48}
       />
     ))}
   </View>
app/index.tsx (1)

13-19: Consider adding loading and error handling for the splash image.

While the implementation is correct, consider adding:

  • resizeMode prop to control how the image scales
  • Error handling for failed image loads
 <Image
   source={require("@/assets/images/splash.png")}
+  resizeMode="contain"
+  onError={(e) => console.warn('Error loading splash image:', e.nativeEvent.error)}
   style={{
     width: '100%',
     height: '100%',
   }}
 />
components/ui/screen-header/index.tsx (1)

34-34: Fix the handleAction function syntax.

The function has inconsistent syntax (semicolon instead of closing brace).

- };
+ }
components/collaboration/collaboration-details/modal/ManagerModal.tsx (1)

43-54: Remove duplicate styling in ImageComponent.

The component has overlapping size definitions:

  • size="medium" prop
  • Explicit width/height in style prop
 <ImageComponent
   url={managerImage}
   altText={managerName}
   shape="circle"
   size="medium"
-  style={{
-    width: 120,
-    height: 120,
-    borderRadius: 240,
-  }}
   initials={managerName}
   initialsSize={40}
 />
components/brand-profile/members-card/index.tsx (1)

50-56: Improve the altText for better accessibility.

The altText "Image" is too generic. Consider using a more descriptive text that includes the manager's name.

 <ImageComponent
   size="small"
   shape="circle"
   initials={manager.name}
   url={manager.profileImage || ""}
-  altText="Image"
+  altText={`Profile picture of ${manager.name}`}
 />
components/contract-card/ContractHeader.tsx (2)

Line range hint 5-5: Remove unused import

The Image import from 'react-native' is no longer used after switching to ImageComponent.

-import { Image, Pressable } from "react-native";
+import { Pressable } from "react-native";

50-55: Improve accessibility and remove redundant styling

  1. The altText "Image" is not descriptive enough for screen readers. Consider using something more meaningful like {username}'s profile picture.
  2. The borderRadius style may conflict with shape="circle" prop.
 <ImageComponent
   url={userImage}
-  altText="Image"
+  altText={`${username}'s profile picture`}
   shape="circle"
   initials={username}
   size="small"
   style={{
     width: 40,
     height: 40,
-    borderRadius: 20,
   }}
 />
components/collaboration-card/card-components/CollaborationHeader.tsx (2)

Line range hint 5-5: Remove unused import

The Image import from 'react-native' is no longer used after switching to ImageComponent.

-import { Image, Pressable } from "react-native";
+import { Pressable } from "react-native";

51-55: Fix inconsistent styling and improve accessibility

  1. There's a mismatch between shape="circle" and borderRadius: 5
  2. The altText should be more descriptive
  3. Consider adding initials prop for fallback display
 <ImageComponent
   url={brandImage}
-  altText="Image"
+  altText={`${brandName}'s brand logo`}
   shape="circle"
   size="small"
+  initials={brandName}
   style={{
     width: 40,
     height: 40,
-    borderRadius: 5,
   }}
 />
components/collaboration/collaboration-details/modal/BrandModal.tsx (2)

Line range hint 1-1: Remove unused import

The Image import is no longer used after switching to ImageComponent.

-import { Image, Pressable, Linking } from "react-native";
+import { Pressable, Linking } from "react-native";

46-50: Add fallback display and improve accessibility

Consider adding initials prop for fallback display when image fails to load, and make the altText more descriptive.

 <ImageComponent
   url={brand.image}
-  altText="Brand Image"
+  altText={`${brand.name}'s brand logo`}
   shape="square"
   size="medium"
+  initials={brand.name}
   style={{
components/menu/index.tsx (3)

Line range hint 44-47: Standardize image component usage

For consistency, replace the brand logo Image component with ImageComponent as well.

-<Image
-  source={imageUrl(selectedBrand?.image)}
-  style={styles.avatarBrandImage}
-/>
+<ImageComponent
+  url={selectedBrand?.image || ""}
+  altText={`${selectedBrand?.name}'s brand logo`}
+  shape="square"
+  size="medium"
+  initials={selectedBrand?.name}
+  style={styles.avatarBrandImage}
+/>

94-99: Improve accessibility description

The altText "Image" is not descriptive enough for screen readers.

 <ImageComponent
   url={manager?.profileImage || ""}
   initials={manager?.name}
   shape="circle"
   size="small"
-  altText="Image"
+  altText={`${manager?.name}'s profile picture`}
   style={styles.avatar}
 />

Line range hint 1-1: Consider creating image component usage guidelines

To maintain consistency across the codebase, consider:

  1. Creating documentation for ImageComponent usage patterns
  2. Establishing guidelines for:
    • Meaningful altText construction
    • When to use different shapes and sizes
    • Style handling with shape props
    • Required props for different use cases (profile pictures vs logos)
  3. Adding prop-types or TypeScript interfaces to enforce these guidelines

This will help maintain consistency as the component is adopted across the codebase.

components/InfluencerCard.tsx (1)

150-154: Consider memoizing the carousel data transformation.

The attachment processing is performed on every render.

Consider using useMemo:

+const carouselData = useMemo(() => 
+  influencer.profile?.attachments?.map((attachment) =>
+    processRawAttachment(attachment)
+  ) || [],
+  [influencer.profile?.attachments]
+);

-data={
-  influencer.profile?.attachments?.map((attachment) =>
-    processRawAttachment(attachment)
-  ) || []
-}
+data={carouselData}
components/contracts/ActionContainer.tsx (1)

217-222: Move inline styles to StyleSheet.

Both ImageComponent implementations use identical inline styles. This should be moved to the StyleSheet for better maintainability.

Apply this refactor:

 const styles = StyleSheet.create({
   // ... existing styles ...
+  profileImage: {
+    width: 40,
+    height: 40,
+    borderRadius: 20,
+  },
 });

-style={{ width: 40, height: 40, borderRadius: 20 }}
+style={styles.profileImage}

Also applies to: 271-276

components/collaboration/collaboration-details/OverviewTabContent.tsx (1)

190-194: Consolidate image styles in StyleSheet.

Both ImageComponent implementations use inline styles that could be moved to the StyleSheet. Additionally, consider creating reusable size constants.

Apply this refactor:

+const IMAGE_SIZES = {
+  BRAND_LOGO: {
+    width: 40,
+    height: 40,
+    borderRadius: 5,
+  },
+  PROFILE: {
+    width: 40,
+    height: 40,
+    borderRadius: 20,
+  },
+};

 const styles = stylesFn(theme);
+const localStyles = StyleSheet.create({
+  brandLogo: IMAGE_SIZES.BRAND_LOGO,
+  profileImage: IMAGE_SIZES.PROFILE,
+});

-style={{ width: 40, height: 40, borderRadius: 5 }}
+style={localStyles.brandLogo}

-style={{ width: 40, height: 40, borderRadius: 20 }}
+style={localStyles.profileImage}

Also applies to: 449-454

components/contracts/AddMemberModal.tsx (1)

115-120: Consider improving type safety and accessibility.

While the ImageComponent implementation is good, there are a few suggestions for improvement:

  1. Type Safety:

    • Consider adding proper typing for the item parameter instead of using 'any'
    • Add fallback handling for undefined profileImage
  2. Accessibility:

    • Make the altText more descriptive by including the member's name

Here's a suggested improvement:

-        <ImageComponent
-          size="small"
-          url={item.item.profileImage}
-          initials={item.item.name}
-          altText="Profile Image"
-        />
+        <ImageComponent
+          size="small"
+          url={item.item.profileImage}
+          initials={item.item.name}
+          altText={`Profile image of ${item.item.name}`}
+        />

Also, consider adding proper TypeScript interfaces:

interface Member {
  profileImage?: string;
  name: string;
  email: string;
  managerId: string;
}

interface RenderItemProps {
  item: Member;
  index: number;
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c101c7d and b0723fd.

📒 Files selected for processing (21)
  • app/(main)/(drawer)/_layout.tsx (1 hunks)
  • app/(main)/(screens)/(onboarding)/onboarding-your-brand.tsx (4 hunks)
  • app/index.tsx (1 hunks)
  • components/InfluencerCard.tsx (7 hunks)
  • components/brand-profile/members-card/index.tsx (2 hunks)
  • components/collaboration-card/card-components/CollaborationHeader.tsx (2 hunks)
  • components/collaboration/collaboration-details/OverviewTabContent.tsx (3 hunks)
  • components/collaboration/collaboration-details/modal/BrandModal.tsx (2 hunks)
  • components/collaboration/collaboration-details/modal/ManagerModal.tsx (2 hunks)
  • components/contract-card/ContractHeader.tsx (2 hunks)
  • components/contracts/ActionContainer.tsx (3 hunks)
  • components/contracts/AddMemberModal.tsx (2 hunks)
  • components/menu/index.tsx (2 hunks)
  • components/profile/index.tsx (4 hunks)
  • components/shimmers/explore-influencer-shimmer.tsx (1 hunks)
  • components/ui/screen-header/index.tsx (3 hunks)
  • constants/Colors.ts (2 hunks)
  • contexts/auth-context.provider.tsx (2 hunks)
  • layouts/protected/brand-protected-screen.tsx (1 hunks)
  • layouts/protected/index.ts (1 hunks)
  • shared-uis (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • shared-uis
🧰 Additional context used
🪛 Biome (1.9.4)
layouts/protected/brand-protected-screen.tsx

[error] 56-56: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🔇 Additional comments (13)
app/(main)/(drawer)/_layout.tsx (1)

11-28: LGTM! Clean integration of BrandProtectedScreen

The wrapper implementation looks good and maintains the existing drawer functionality while adding brand protection.

layouts/protected/index.ts (1)

1-3: LGTM! Clean export implementation

The barrel file pattern is properly implemented.

constants/Colors.ts (1)

22-23: LGTM! Consistent color definitions

The shimmer colors are properly defined for both themes and follow the existing pattern.

Also applies to: 38-39

components/shimmers/explore-influencer-shimmer.tsx (1)

12-67: LGTM! Well-structured shimmer implementation.

The component is well-organized and follows React best practices. The shimmer effects are appropriately sized for each UI element, providing a good loading state experience.

app/(main)/(screens)/(onboarding)/onboarding-your-brand.tsx (2)

45-56: Good improvement in error handling!

The error handling has been enhanced by properly setting isSubmitting to false when validation fails.


63-66: Consider adding error handling for image upload.

The image upload process should include error handling to manage potential failures.

-      imageUrl = await uploadImageBytes(
-        blob,
-        `brands/${brandData.name}-${Date.now()}`
-      );
+      try {
+        imageUrl = await uploadImageBytes(
+          blob,
+          `brands/${brandData.name}-${Date.now()}`
+        );
+      } catch (error) {
+        Toaster.error('Failed to upload brand image');
+        setIsSubmitting(false);
+        return;
+      }
✅ Verification successful

Error handling is necessary for Firebase Storage operations

The uploadImageBytes function uses Firebase Storage operations that can fail due to network issues, permissions, or storage quota. The suggested error handling:

  • Aligns with existing error handling patterns in the component
  • Uses the available Toaster utility
  • Properly manages the submission state
🏁 Scripts executed

The following scripts were executed for the analysis:

Error: No shell scripts found


Script:

#!/bin/bash
# Find uploadImageBytes implementation
ast-grep --pattern 'export $_ function uploadImageBytes'

# Search for Toaster utility
ast-grep --pattern 'export $_ Toaster'

# Look at the full component file to understand the context
cat "app/(main)/(screens)/(onboarding)/onboarding-your-brand.tsx"

Length of output: 3803


Script:

#!/bin/bash
# Search for uploadImageBytes implementation in contexts
fd "firebase-storage.context" --exec cat {}

Length of output: 1523

contexts/auth-context.provider.tsx (1)

250-252: LGTM! Clean function signature formatting.

The function signature formatting improves readability.

app/index.tsx (1)

1-11: LGTM! Clean implementation of centered container.

The styling implementation correctly centers the content using flex properties.

components/ui/screen-header/index.tsx (1)

44-64: LGTM! Accessible back button implementation.

The back button implementation:

  • Uses Pressable for better touch handling
  • Properly implements the FontAwesomeIcon
  • Has appropriate spacing and transparency
components/profile/index.tsx (1)

163-169: LGTM! Clean implementation of ImageComponent.

The ImageComponent implementation with fallback initials and proper alt text improves accessibility and user experience.

components/InfluencerCard.tsx (2)

81-86: LGTM! Well-structured ImageComponent implementation.

The ImageComponent implementation with proper size, shape, and accessibility attributes enhances the user interface.


98-100: Verify the social media handle fallback.

The fallback value "influencer-handle" might not be the best user experience.

Consider handling empty social media data more gracefully:

-{influencer.socials?.[0] || "influencer-handle"}
+{influencer.socials?.[0] || `@${influencer.name.toLowerCase().replace(/\s+/g, '')}`}
✅ Verification successful

Verified: The suggested social media handle fallback is appropriate

The suggestion to use @${influencer.name.toLowerCase().replace(/\s+/g, '')} as fallback is valid because:

  • The name field is required in the data structure
  • It follows the established handle format seen in the codebase
  • It provides more meaningful information than the static "influencer-handle"
🏁 Scripts executed

The following scripts were executed for the analysis:

Error: No shell scripts found


Script:

#!/bin/bash
# Search for type definitions of influencer
ast-grep --pattern 'interface $_ {
  $$$
  socials?: $_
  $$$
}'

# Search for type definitions with name field
ast-grep --pattern 'interface $_ {
  $$$
  name: $_
  $$$
}'

# Look for other usages of social handles
rg "socials\?\.\[" -A 2 -B 2

# Search for any existing handle generation patterns
rg -i "\.replace\(/\s+/g,\s*['\"]" -A 2 -B 2

Length of output: 2516


Script:

#!/bin/bash
# Search for influencer type/interface definitions
ast-grep --pattern 'type Influencer = {
  $$$
}'

ast-grep --pattern 'interface Influencer {
  $$$
}'

# Look for usages of influencer.name
rg "influencer\.name" -A 2 -B 2

# Search for files that might contain influencer types
fd "influencer.*\.(ts|tsx)" --type f

Length of output: 2394


Script:

#!/bin/bash
# Check contents of influencer type files
cat constants/Influencer.ts
cat constants/Influencers.ts

# Double check for any type definitions in the codebase
ast-grep --pattern 'export type $_ = {
  name: $_
  socials?: $_
  $$$
}'

Length of output: 3445

components/contracts/AddMemberModal.tsx (1)

23-23: LGTM! Clean import of the shared ImageComponent.

The import follows the project's component organization pattern, using the shared UI components effectively.

Comment on lines +55 to +60
useEffect(() => {
if (manager && manager.id) {
setLoading(true);
fetchBrands(manager.id);
}
}, [manager]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add error handling for async operations

The fetchBrands call lacks error handling, which could leave users stuck in a loading state if the Firebase query fails.

Add try-catch block:

  useEffect(() => {
    if (manager?.id) {
      setLoading(true);
-     fetchBrands(manager.id);
+     fetchBrands(manager.id).catch((error) => {
+       console.error('Failed to fetch brands:', error);
+       setLoading(false);
+     });
    }
  }, [manager]);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
useEffect(() => {
if (manager && manager.id) {
setLoading(true);
fetchBrands(manager.id);
}
}, [manager]);
useEffect(() => {
if (manager?.id) {
setLoading(true);
fetchBrands(manager.id).catch((error) => {
console.error('Failed to fetch brands:', error);
setLoading(false);
});
}
}, [manager]);
🧰 Tools
🪛 Biome (1.9.4)

[error] 56-56: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

Comment on lines +25 to +53
const fetchBrands = async (
managerId: string
): Promise<void> => {
const brandsCollection = collection(FirestoreDB, "brands");

const brandsSnapshot = await getDocs(brandsCollection);

for (const brandDoc of brandsSnapshot.docs) {
const brandId = brandDoc.id;

const membersCollection = collection(FirestoreDB, `brands/${brandId}/members`);
const membersQuery = query(membersCollection, where("managerId", "==", managerId));
const membersSnapshot = await getDocs(membersQuery);

if (!membersSnapshot.empty) {
setLoading(false);
return;
}
}

// If the manager has no brands, redirect to onboarding
router.replace({
pathname: "/onboarding-your-brand",
params: {
firstBrand: "true",
},
});
setLoading(false);
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Optimize Firestore query performance

The current implementation fetches all brands and then queries their members collections individually, which could be inefficient as the number of brands grows.

Consider restructuring the data model or query to reduce the number of reads:

-    const brandsCollection = collection(FirestoreDB, "brands");
-    const brandsSnapshot = await getDocs(brandsCollection);
-
-    for (const brandDoc of brandsSnapshot.docs) {
-      const brandId = brandDoc.id;
-
-      const membersCollection = collection(FirestoreDB, `brands/${brandId}/members`);
-      const membersQuery = query(membersCollection, where("managerId", "==", managerId));
-      const membersSnapshot = await getDocs(membersQuery);
-
-      if (!membersSnapshot.empty) {
-        setLoading(false);
-        return;
-      }
-    }
+    // Create a collection group query across all members subcollections
+    const membersQuery = query(
+      collectionGroup(FirestoreDB, 'members'),
+      where('managerId', '==', managerId),
+      limit(1)
+    );
+    const membersSnapshot = await getDocs(membersQuery);
+
+    if (!membersSnapshot.empty) {
+      setLoading(false);
+      return;
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const fetchBrands = async (
managerId: string
): Promise<void> => {
const brandsCollection = collection(FirestoreDB, "brands");
const brandsSnapshot = await getDocs(brandsCollection);
for (const brandDoc of brandsSnapshot.docs) {
const brandId = brandDoc.id;
const membersCollection = collection(FirestoreDB, `brands/${brandId}/members`);
const membersQuery = query(membersCollection, where("managerId", "==", managerId));
const membersSnapshot = await getDocs(membersQuery);
if (!membersSnapshot.empty) {
setLoading(false);
return;
}
}
// If the manager has no brands, redirect to onboarding
router.replace({
pathname: "/onboarding-your-brand",
params: {
firstBrand: "true",
},
});
setLoading(false);
};
const fetchBrands = async (
managerId: string
): Promise<void> => {
// Create a collection group query across all members subcollections
const membersQuery = query(
collectionGroup(FirestoreDB, 'members'),
where('managerId', '==', managerId),
limit(1)
);
const membersSnapshot = await getDocs(membersQuery);
if (!membersSnapshot.empty) {
setLoading(false);
return;
}
// If the manager has no brands, redirect to onboarding
router.replace({
pathname: "/onboarding-your-brand",
params: {
firstBrand: "true",
},
});
setLoading(false);
};

Comment on lines +89 to +105
role: "Manager",
})
.then(() => {
router.replace({
pathname: "/onboarding-get-started",
params: {
brandId: docRef.id,
firstBrand: firstBrand === "true" ? "true" : "false",
},
});
setSelectedBrand(brandData as Brand);
})
.catch((error) => {
Toaster.error("Error creating brand");
})
.finally(() => {
setIsSubmitting(false);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider using a transaction for atomic operations.

The brand creation and manager role assignment should be atomic operations to maintain data consistency.

-      await setDoc(managerRef, {
-        managerId: user.id,
-        role: "Manager",
-      })
+      try {
+        await FirestoreDB.runTransaction(async (transaction) => {
+          transaction.set(docRef, brand);
+          transaction.set(managerRef, {
+            managerId: user.id,
+            role: "Manager",
+          });
+        });
+        
+        router.replace({
+          pathname: "/onboarding-get-started",
+          params: {
+            brandId: docRef.id,
+            firstBrand: firstBrand === "true" ? "true" : "false",
+          },
+        });
+        setSelectedBrand(brandData as Brand);
+      } catch (error) {
+        Toaster.error("Error creating brand");
+      } finally {
+        setIsSubmitting(false);
+      }

Committable suggestion skipped: line range outside the PR's diff.

@dev-rathore
dev-rathore merged commit 65d40b5 into master Jan 11, 2025
@coderabbitai coderabbitai Bot mentioned this pull request May 2, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants