Skip to content

Feature/branch collaboration revamp - #50

Merged
Manan-Sharma-5 merged 4 commits into
devfrom
feature/branch-collaboration-revamp
Dec 26, 2024
Merged

Manan-Sharma-5 merged 4 commits into
devfrom
feature/branch-collaboration-revamp

Conversation

@Manan-Sharma-5

@Manan-Sharma-5 Manan-Sharma-5 commented Dec 26, 2024 •

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced new components: ProfileItemCard, ChipCard, CollaborationDetails, CollaborationHeader, CollaborationStats, ImageUploadModal, BrandModal, and ManagerModal.
    • Added functionality for image uploads and collaboration publishing.
    • Enhanced the Profile component to allow users to update their profile information.
    • Added a new SettingsTabContent component for managing collaboration settings.
  • Improvements

    • Simplified UI by removing unnecessary components and restructuring layouts for better readability.
    • Enhanced theme selection process in the Settings component.
    • Improved styling across various components for a more cohesive look.
    • Updated menu items with new icons and titles for better user navigation.
  • Bug Fixes

    • Resolved issues related to rendering and state management in several components.
  • Chores

    • Updated constants and styles for better organization and clarity.

@coderabbitai

coderabbitai Bot commented Dec 26, 2024 •

Copy link
Copy Markdown

Walkthrough

This pull request introduces several new components and style files across the project, focusing on enhancing collaboration, profile, and settings functionality. Key changes include the removal of the ScreenHeader component from both the ProfileScreen and SettingsScreen, the addition of new components like ProfileItemCard, ChipCard, CollaborationDetails, and CollaborationHeader, as well as updates to existing components for improved user interaction and theme management.

Changes

File Change Summary
app/(main)/(screens)/(menu)/profile.tsx Removed ScreenHeader component
app/(main)/(screens)/(menu)/settings.tsx Removed ScreenHeader component and added View import
components/ProfileItemCard.tsx New component for rendering profile item cards
components/collaboration-card/* Added multiple new components for collaboration details and interactions
components/modal/ImageUploadModal.tsx New modal for image upload functionality
components/profile/index.tsx Enhanced profile management with new state and update functions
components/settings/index.tsx Updated theme selection and management
constants/Colors.ts Added new yellow color constant
constants/Menu.ts Updated menu items and icons
styles/* Added multiple new style files for various components

Possibly related PRs

Suggested Reviewers

  • dev-rathore

Poem

🐰 A Rabbit's Ode to Code Transformation
Collaborations bloom like spring's sweet flower,
Components dance with newfound power.
Styles refined, themes so bright,
Code evolves with pure delight! 🌈
Hop, hop, hooray for progress today! 🚀


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: 8

🧹 Nitpick comments (51)
styles/CollaborationCardStats.styles.tsx (1)

24-30: Space-around in action row can cause uneven layout in smaller screens.

justifyContent: "space-around" can lead to uneven margins on smaller devices. Evaluate replacing it with space-between or adding padding for a smoother layout on narrower screens.

components/profile/index.tsx (2)

39-39: Avoid leaving console logs in production
Please remove or replace this console.log with a more robust logging approach or remove it entirely to keep the production build clean.


90-114: Consolidate duplicate "Save" actions
There are two separate triggers for saving the profile: the header’s "Save" pressable and the "Save Profile" button. Consider unifying them into a single call site to enhance consistency.

Also applies to: 176-188

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

48-75: Optimize parallel data fetching
fetchManagerDetails() fetches two documents sequentially. Consider using Promise.all to parallelize the retrieval of manager and brand data for improved performance.


77-100: Encapsulate reusable star renderer
renderStars() is a neat approach. If used in multiple components, consider refactoring it into a shared utility or component.


417-475: Graceful handling for managerDetails
The managerDetails object is populated after an async call. If the user quickly opens the modal, some fields may be undefined. Add a loading or fallback indicator to handle this scenario smoothly.

app/(main)/(screens)/(menu)/settings.tsx (1)

5-5: Remove unused imports if unneeded
View is imported from @/components/theme/Themed but not currently used. If no future usage is planned, consider removing it to keep the file clean.

styles/profile/ProfileItemCard.styles.tsx (1)

5-14: Consider dynamic color for the border.
You're using a fixed borderBottomWidth: 0.2 with no dynamic color. If you want to align better with the theme, consider adding a borderBottomColor: Colors(theme).separator or something similar to ensure consistency with other components that leverage dynamic theme colors.

styles/profile/ProfileCard.styles.tsx (1)

10-11: Reassess border styling for better theming.
You're using Colors(theme).aliceBlue as a bottom border. Ensure other theming variants (like dark mode) also have the right contrast. Consider verifying the design for dark themes or customizing it further.

components/collaboration-card/card-components/ChipComponent.tsx (2)

7-7: Remove unused imports if not needed.
If you do not intend to use the Chip from react-native-paper, consider removing the import to keep the code clean.


17-40: Enhance accessibility.
It might be useful to add accessibility labels for screen readers on the chip container or text. For users relying on accessibility tools, descriptive labels help them understand the chip’s content and purpose.

components/collaboration/CollaborationHeader.tsx (2)

1-14: Consider removing unused imports.
It seems Tag might no longer be needed after removing tag-related logic. Unless you're planning to reintroduce tags, removing the unused import can keep the code lean.


31-31: Handle optional image fallback gracefully.
You're already using optional chaining (collaboration?.logo). Consider providing a fallback image to avoid potential blank or error states when logo is null or undefined.

-<Avatar.Image size={48} source={imageUrl(collaboration?.logo)} />
+<Avatar.Image 
+  size={48} 
+  source={
+    collaboration?.logo 
+      ? imageUrl(collaboration.logo) 
+      : require("@/assets/placeholder-avatar.png")
+  }
/>
styles/profile/SocialPage.styles.tsx (2)

7-12: Consider theming for borders.
Using Colors(theme).border is good. If you want more subtle variation per theme, consider adding distinct border color tokens (e.g., borderLight, borderDark) in your theme or Colors file.


44-47: Typography best practices.
Heading styles (e.g., title and fontWeight: "bold") could use named style constants or use standard text sizes to maintain a consistent hierarchy across the app.

styles/menu/MenuItem.styles.tsx (1)

14-22: Consider renaming for consistency and clarity.
The style key brandAvatar may be confused with something that specifically refers to an image object; however, it appears to represent a container. A more descriptive name, such as brandAvatarContainer, may enhance clarity.

components/collaboration-card/card-components/CollaborationStats.tsx (2)

43-43: Avoid console logs in production.
console.log(docsDataInvited.length); can clutter logs and potentially leak data. Remove debug logging or use a controlled logger with appropriate log levels.


63-67: Consider extracting budget formatting logic for reusability.
This logic is duplicated if multiple components handle budgets. Factor out into a helper function or utility module to maintain consistency.

components/collaboration/collaboration-details/modal/ManagerModal.tsx (2)

41-49: Add fallback content for missing image.
If managerImage is not a valid URI or fails to load, the user would see a broken image. Consider using a fallback image or placeholder.


81-89: Make email tappable.
Since you have the manager’s email, consider making it clickable to open a mail client. This adds user convenience if they want to contact the manager directly.

<Text
  style={{
    fontSize: 16,
    color: Colors(theme).text,
  }}
->
  Email: {managerEmail}
</Text>
+ onPress={() => Linking.openURL(`mailto:${managerEmail}`)}
>
+  Email: {managerEmail}
+</Text>
components/settings/index.tsx (3)

13-13: Consider setting the default theme from user or system preference.
Rather than hardcoding “light” by default, read from device or user settings to choose an initial value that better matches user’s environment.


67-84: Improve label for theme selection.
“Decide the theme of the platform” might be more descriptive if phrased as “Select your preferred theme” or “Preferred App Theme”.


85-93: Consolidate the two “Save” triggers or remove one for clarity.
There is both a “Save” text item in the header (line 51) and a dedicated “Save” button (line 92). This might cause confusion or duplication of calls. Consider removing one or clarifying their roles.

components/collaboration-card/card-components/CollaborationHeader.tsx (3)

11-19: Consider making some props optional for maximum flexibility.

Your interface already includes an optional timePosted prop. If there are scenarios where brandImage or paymentVerified may be absent, consider marking them optional as well. This could improve the component's reusability across different collaboration modules.


32-41: Group styling objects for better maintainability.

The inline style is fine for quick iteration, but defining a StyleSheet object or theming approach could enhance consistency, reduce re-renders, and improve readability. You might consider extracting repeated style definitions (like "display: 'flex'", "flexDirection: 'row'", etc.) into a shared style or theme file.


105-109: Ensure visual contrast for the ellipsis icon.

You’re using Colors(theme).text for the icon. Verify that this maintains readability for both light and dark modes. If your accessibility guidelines require a higher contrast, you might consider adjusting the color or background.

components/ui/select/index.tsx (2)

58-68: Use layout props consistently.

You’re conditionally setting flexDirection and flexWrap based on direction. This is a good approach. Consider factoring these repeated style decisions into a small utility function or a style object for clarity and consistency across the codebase.


70-110: Evaluate performance for large item sets.

Rendering many items with a simple .map() can be expensive. If the items array can become large, think about using a memoization technique (e.g., React.memo) or a FlatList for performance optimization, especially on low-end devices.

components/modal/ImageUploadModal.tsx (1)

65-68: Clarify the cancel action.

You’re calling removeImage() to effectively reset the image with an empty string. Rename or document this function more clearly to communicate that this is removing or clearing the image. For example, use a name like clearImageSelection or cancelUpload for clearer semantics.

components/collaboration-card/card-components/CollaborationDetails.tsx (1)

116-128: Validate content array usage.

You’re mapping over contentType when contentType.length > 0. Confirm that nested data structures or unusual content strings don’t cause unexpected rendering or layout. For instance, if content is very long, you may need to truncate or style it accordingly.

Would you like assistance implementing automatic text truncation or providing a tooltip-style expansion for lengthy text?

components/collaboration/collaboration-details/modal/BrandModal.tsx (3)

29-31: Align naming of brandVerified with local variable.
The prop is named brandVerified, but it's destructured as brandVerified: paymentVerified. Consider keeping consistent naming to avoid confusion.

- brandVerified: paymentVerified,
+ brandVerified,

49-55: Consider fallback for brand image.
If brandImage is empty or invalid, the image might fail to load. Provide a fallback image or placeholder.


68-75: Use a more descriptive icon label.
You currently use a solid check-circle to indicate verification, which is great. Consider a short text or tooltip for screen readers to improve accessibility.

components/collaboration/collaboration-details/index.tsx (3)

13-15: Centralize theme and color utilities.
Imports for Colors and useTheme look consistent. For maintainability and reusability, consider centralizing color and theme usage if they’re utilized broadly elsewhere.


37-48: Adequate error handling for publishCollaboration.
Nice usage of try/catch; consider surfacing an error message (snackbar or toast) to the user for visibility rather than just console logging.


138-179: Provide user feedback on publish state.
The "Publish Now" button calls publishCollaboration. Consider showing a spinner or disabled state while publishing, so the user knows the process is in progress.

styles/profile/TextBox.styles.tsx (5)

1-3: Adopt consistent naming conventions for style generator.
CreateCampaignstylesFn is fine, but ensure it follows consistent naming patterns (possibly createCampaignStylesFn) throughout the project.


29-34: Limit repetition in step container styles.
stepContainer and stepContainer2 share several properties. Consider merging common rules into a shared style object to reduce duplication.

- stepContainer: {
-   padding: 16,
-   ...
- },
- stepContainer2: {
-   padding: 16,
-   ...
- },
+ const baseStepContainer = {
+   padding: 16,
+   ...
+ }
+ stepContainer: {
+   ...baseStepContainer,
+   ...
+ },
+ stepContainer2: {
+   ...baseStepContainer,
+   ...
+ },

77-83: Consider using theme-based border color.
You're hardcoding #ccc in multiple places. For a consistent look, consider referencing the theme or a constants file.


167-185: Refactor repeated background colors for active/inactive stages.
#2196F3 and #ccc can be replaced by named constants or theme-based colors for better consistency.


192-201: Improve step progress styling.
activeProgressDot uses a hardcoded "blue". Consider a theme-based approach or a color from Colors(theme).

components/collaboration/collaboration-details/SettingsTabContent.tsx (4)

19-31: Initialize default states with caution.
You default each selection to { label: "None", value: "None" }. Confirm if "None" is a valid fallback in your data model or if you prefer a blank string.


89-116: Consider rearranging UI sections for clarity.
All sections are stacked linearly in a ScrollView. This is fine, but consider grouping related selects (time, niche, etc.) with headings for a more logical user flow.


144-169: Consider dynamic items for the Select component.
Currently, items are hardcoded. If these categories come from a dynamic source (API, Firestore), you might want to fetch them instead.


221-251: Explain “Dedicated” vs. “Integrated” video in detail.
You have a short text that clarifies each option. If user confusion is common, a tooltip or help link might reduce support overhead.

components/proposals/Invitations.tsx (3)

44-44: Consider verifying responsive behavior.
You are correctly using a breakpoint (xl). Slight differences in layout on smaller devices might require further testing to ensure a consistent user experience.


141-141: Fix minor spelling error in subtitle text.
“woudl” should be “would.”

-          subtitle="Start building your profile today to have better reach. If any brand invites you to collaborate we woudl show it here"
+          subtitle="Start building your profile today to have better reach. If any brand invites you to collaborate we would show it here"

145-230: Use a more stable key for list items.
Currently, index.toString() is used in keyExtractor. Prefer using item.id to preserve stable ordering and help React track item identity.

- keyExtractor={(item, index) => index.toString()}
+ keyExtractor={(item) => item.id.toString()}
components/proposals/Applications.tsx (3)

47-47: Breakpoint usage is consistent.
Same as in Invitations.tsx, test layout responsiveness across various screen sizes.


159-159: Remove or gate the debug statement.
Console logs in production code can clutter output and potentially reveal internal data.

- console.log(item),

145-262: Refine key extraction to ensure stable identity of list items.
Currently uses (item, index) => index.toString(). Use item.id if available.

- keyExtractor={(item, index) => index.toString()}
+ keyExtractor={(item) => item.id.toString()}
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 96b8749 and 8609f30.

📒 Files selected for processing (31)
  • app/(main)/(screens)/(menu)/profile.tsx (0 hunks)
  • app/(main)/(screens)/(menu)/settings.tsx (1 hunks)
  • components/ProfileItemCard.tsx (1 hunks)
  • components/collaboration-card/card-components/ChipComponent.tsx (1 hunks)
  • components/collaboration-card/card-components/CollaborationDetails.tsx (1 hunks)
  • components/collaboration-card/card-components/CollaborationHeader.tsx (1 hunks)
  • components/collaboration-card/card-components/CollaborationStats.tsx (1 hunks)
  • components/collaboration/CollaborationHeader.tsx (2 hunks)
  • components/collaboration/collaboration-details/OverviewTabContent.tsx (1 hunks)
  • components/collaboration/collaboration-details/SettingsTabContent.tsx (1 hunks)
  • components/collaboration/collaboration-details/index.tsx (5 hunks)
  • components/collaboration/collaboration-details/modal/BrandModal.tsx (1 hunks)
  • components/collaboration/collaboration-details/modal/ManagerModal.tsx (1 hunks)
  • components/explore-influencers/index.tsx (6 hunks)
  • components/menu/index.tsx (2 hunks)
  • components/modal/ImageUploadModal.tsx (1 hunks)
  • components/profile/index.tsx (1 hunks)
  • components/proposals/Applications.tsx (4 hunks)
  • components/proposals/Invitations.tsx (4 hunks)
  • components/settings/index.tsx (1 hunks)
  • components/ui/select/index.tsx (2 hunks)
  • constants/Colors.ts (1 hunks)
  • constants/Menu.ts (1 hunks)
  • styles/CollaborationCardStats.styles.tsx (1 hunks)
  • styles/CollaborationDetails.styles.tsx (1 hunks)
  • styles/menu/MenuItem.styles.tsx (1 hunks)
  • styles/profile/ProfileCard.styles.tsx (1 hunks)
  • styles/profile/ProfileItemCard.styles.tsx (1 hunks)
  • styles/profile/SocialPage.styles.tsx (1 hunks)
  • styles/profile/TextBox.styles.tsx (1 hunks)
  • styles/settings/Settings.styles.tsx (1 hunks)
💤 Files with no reviewable changes (1)
  • app/(main)/(screens)/(menu)/profile.tsx
✅ Files skipped from review due to trivial changes (1)
  • components/explore-influencers/index.tsx
🧰 Additional context used
🪛 Biome (1.9.4)
components/menu/index.tsx

[error] 69-74: Missing key property for this element in iterable.

The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.

(lint/correctness/useJsxKeyInIterable)

🔇 Additional comments (49)
styles/CollaborationCardStats.styles.tsx (3)

1-3: Imports appear consistent and straightforward.

The imports from @react-navigation/native and react-native are appropriate, and Colors is pulled from @/constants/Colors in a clear and well-organized manner. This aligns with best practices.


5-16: Good usage of theme-based color styling.

Applying Colors(theme).text ensures the text color aligns with the theme. This establishes a flexible, dynamic style to accommodate different themes (light, dark, etc.). Make sure to verify that the theme object consistently includes and transitions color properties to avoid unexpected color shifts.


17-23: Verify 'gap' property support in targeted RN versions.

While gap is a convenient CSS property, it is not supported in earlier React Native versions (< 0.70). Ensure that the target environment supports gap, or consider using spacing margins/paddings as a fallback.

components/profile/index.tsx (3)

41-78: Double-check concurrency and validation in updateProfile
If multiple profile updates happen concurrently, data could be overwritten. Also, consider verifying that capturedImage is a valid file before uploading, to avoid potential runtime errors.


80-88: Fallback handling for missing manager
The fallback to empty strings is good practice. If the manager object might change at runtime, ensuring synchronization or additional checks could help prevent outdated state usage.


123-147: Validate fetched image URIs
If capturedImage points to an invalid or remote URL, the fetch call might fail or be slow. Add stronger validation or user feedback in case the image is not accessible.

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

43-47: Good use of local state for modals
Using individual states for brandModalVisible and managerModalVisible is clear. Just be mindful of too many local states becoming unwieldy if more modals are introduced.


102-106: Handle async errors
In useEffect, if fetchManagerDetails() fails, the component might silently fail. Consider adding error handling or a fallback UI to improve user feedback.


160-207: Ensure brand modal checks
The brand modal is opened on press, but the data could be partially loaded or missing. A quick check for brand data before opening the modal can improve UX and avoid null references.

constants/Menu.ts (1)

1-14: Menu item updates look good
Icons and titles have been updated appropriately. Ensure these menu items correctly link to the respective routes /preferences and /settings.

styles/settings/Settings.styles.tsx (1)

3-26: Effective layout updates
Switching from row to column in settingsRow can improve vertical stacking. Just ensure that any horizontally aligned elements remain consistent with the intended design.

styles/profile/ProfileItemCard.styles.tsx (1)

7-14: Check React Native version support for “gap” property.
The gap property is relatively new and may not be supported in older React Native versions. If you need compatibility with older versions, consider implementing spacing with margin or a wrapper View.

styles/profile/ProfileCard.styles.tsx (1)

16-22: Verify the visual consistency of the “video” style.
Although this is labeled as “video,” it appears to be styled like a circular image or avatar. Double-check if you’re using the correct naming and layout if it's intended for actual video content.

components/collaboration/CollaborationHeader.tsx (1)

41-41: Minor consistency note.
If you intentionally removed the tag-rendering block, confirm that no other component depends on the tags feature. Otherwise, reintroducing or centralizing that feature might be necessary in the future.

constants/Colors.ts (1)

39-39: Addition of yellow color looks good.
The new color key aligns well with existing design tokens.

styles/CollaborationDetails.styles.tsx (1)

1-68: Looks clean and consistent.
These styles properly utilize theme-based colors and maintain a cohesive layout. Remember to verify shadow-related properties if you need consistent styling across iOS and Android (e.g., elevation for Android).

styles/profile/SocialPage.styles.tsx (1)

55-58: Confirm accessibility compliance for link color usage.
Using Colors(theme).primary for links works fine, but ensure it's sufficiently contrasted against the background, especially in dark mode.

styles/menu/MenuItem.styles.tsx (3)

23-26: Alignment change to center looks good.
Increasing the font size to 24 and centering the text creates a more prominent and balanced brand display. Verify if this aligns with your overall design guidelines across the app.


62-69: Enforces a large avatar size.
Allocating 200×200 for avatarBrandImage is visually striking. Confirm this doesn’t degrade performance or bleed layout on smaller screens. If it’s conceptually a brand icon, consider making it responsive or config-driven.


32-36: Check React Native compatibility for 'gap'.
The gap property is relatively new in React Native and may not work on older RN versions or certain platforms. Ensure project deployments target a version that supports it.

✅ Verification successful

gap property is supported in your React Native version

The project is using React Native v0.74.3, which fully supports the gap property in styles. This version is well above v0.71.0 where gap became stable, so there are no compatibility concerns with using this style property.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Checking if React Native version in package.json is 0.71 or higher (where gap is stable).
rg -A 3 '"react-native": "' package.json

Length of output: 220

components/settings/index.tsx (1)

37-55: ScreenHeader usage appears consistent.
Using ScreenHeader with an inline Pressable is a fair approach. Ensure design uniformity is maintained across all screens that replaced or removed the header.

components/collaboration-card/card-components/CollaborationHeader.tsx (1)

93-99: Ensure time formatting is tested for different time ranges.

You are using formatDistanceToNow(timePosted, { addSuffix: true }). It would be prudent to test or confirm that it correctly handles edge cases (e.g., extremely old timestamps, future timestamps). Make sure your testing covers these scenarios and gracefully handles invalid inputs.

components/ui/select/index.tsx (1)

39-44: Check edge cases for multiselect list management.

When the item is already selected, the else clause creates a new array without that item. This is correct for toggling. However, ensure that your parent components handle state updates properly so that adding/removing items in quick succession doesn’t cause stale updates.

components/modal/ImageUploadModal.tsx (2)

59-63: Handle partial or null results gracefully.

If result.assets is unexpectedly empty or result.assets[0].uri is missing for some reason, this could cause errors. Consider adding an additional safety check before calling onImageUpload(result.assets[0].uri).


71-76: Match modal behavior with platform conventions.

Using "fade" on web vs. "slide" on mobile is a nice approach. Consider verifying that it behaves consistently on different screen sizes and orientation changes, especially for tablets or unusual form factors.

components/collaboration-card/card-components/CollaborationDetails.tsx (1)

92-114: Prevent possible index out-of-bounds.

When platform is an array, you’re using platform[0] in multiple places without checking whether platform.length > 0. Although lines 101~103 handle multiple items, ensure that platform is non-empty so you don’t attempt to access undefined elements.

components/collaboration/collaboration-details/modal/BrandModal.tsx (1)

110-128: Validate brandWebsite before opening URL.
If brandWebsite is empty or invalid, Linking.openURL may fail silently or throw. Consider adding a quick check and possibly showing an alert if the URL is invalid.

components/collaboration/collaboration-details/index.tsx (6)

2-2: Ensure all Firebase imports are used.
You correctly import Firestore methods. Double-check if any are unused to keep the code clean.


29-29: Double-check the pageID prop usage.
Ensure you handle situations when pageID might be undefined or invalid, potentially blocking data fetching or updates.


31-33: Initialize collaboration state properly.
The typed state <CollaborationDetail | undefined> is good. Ensure that every place using collaboration checks for undefined or handles it gracefully.


102-102: Pass additional props to InvitationsTabContent if needed.
Ensure InvitationsTabContent also needs only pageID. If more data is required, consider passing it down in a similar manner.


105-108: Preference naming convention.
Changing from "Settings" to "Preferences" helps clarity. Verify any deep-linked routes or references in the codebase since the naming changed.


180-185: Conditional tabs are well-handled.
The draft condition vs. active condition is clear. This logic is straightforward and maintainable.

styles/profile/TextBox.styles.tsx (2)

58-65: Ensure correct text contrast.
backgroundColor: Colors(theme).background within input fields is good. Verify that the text color remains legible across light/dark modes.


129-133: Check screen resizing behavior.
flexDirection: "row" with a width of "100%" in step3Container may need responsive adjustments on smaller devices.

components/collaboration/collaboration-details/SettingsTabContent.tsx (3)

17-17: Validate pageID before rendering.
Inside the component, ensure pageID is valid. If pageID is missing, it can break Firestore queries.


33-41: Confirm multi-select usage.
The function handleNicheSelect sets an array of items. Ensure the parent or next steps handle multiple niches properly.


54-82: Ensure data.preferences is safe to access.
preferences is assumed to exist in the retrieved doc. If it's missing, this code can throw. Provide a safe fallback or handle undefined properties.

components/proposals/Invitations.tsx (3)

8-8: Firestore imports look good.
No issues found with consolidating Firestore imports.


17-22: All new imports appear valid and in use.
Great job adding the new components and utilities for collaboration stats, details, and attachments.


232-240: BottomSheet integration looks well-structured.
No issues found with how the bottom sheet actions are being handled.

components/proposals/Applications.tsx (3)

3-3: New imports for text display appear appropriate.
No issues found with adding Text to address dynamic content.


9-9: Consolidated Firestore imports are correct and concise.


18-25: All newly introduced imports are in active use.
The new UI components and utilities fit well into this file’s functionality.

components/menu/index.tsx (5)

8-8: New import for direct Image usage is valid.
This aligns with removing the ImageBackground.


16-16: New ProfileItemCard import is ready for usage.
This improves modularity of menu items.


31-31: Logout flow is clear and concise.
No issues found regarding the handleSignOut logic.


36-74: Overall brand info rendering is well-structured.
Displaying brand info and “Edit Brand” button is straightforward.

🧰 Tools
🪛 Biome (1.9.4)

[error] 69-74: Missing key property for this element in iterable.

The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.

(lint/correctness/useJsxKeyInIterable)


86-90: Profile image fallback is handled properly.
No further changes needed.

Comment on lines +14 to +39
const ProfileItemCard: React.FC<ProfileItemCardProps> = ({ item, onPress }) => {
const theme = useTheme();
const styles = stylesFn(theme);

return (
<Pressable onPress={onPress}>
<View style={styles.container}>
<FontAwesomeIcon
color={theme.dark ? Colors(theme).text : Colors(theme).primary}
icon={item.icon}
size={22}
style={styles.avatar}
/>
<View style={styles.textContainer}>
<Text style={styles.titleText}>{item.title}</Text>
</View>
<FontAwesomeIcon
color={theme.dark ? Colors(theme).text : Colors(theme).primary}
icon={faChevronRight}
size={14}
style={styles.icon}
/>
</View>
</Pressable>
);
};

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

Refine prop typing for “item.”
Instead of using any, consider defining a more specific interface for the item prop. This will improve maintainability, code readability, and help detect type mismatches during development.

Comment on lines +33 to +35
<Text style={styles.title}>{collaboration?.name}</Text>
<View style={styles.companyRow}>
<Text style={styles.companyText}>
{collaboration?.brandName}
</Text>
<Text style={styles.companyText}>{collaboration?.brandName}</Text>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codebase verification

Optional fields for collaboration name and brandName should have fallbacks

Based on the search results, I found several instances where collaboration.name and collaboration.brandName are used across different components. While the optional chaining is good, there's inconsistency in handling null/undefined values:

  • In components/create-collaboration/index.tsx, there's proper handling with the OR operator for other fields like collaboration.description || "", but not for the name
  • In other components like OverviewTabContent.tsx and BottomSheetActions.tsx, these fields are used directly without fallbacks

To maintain consistency and improve user experience, add fallback values:

<Text style={styles.title}>
  {collaboration?.name || "Untitled Collaboration"}
</Text>
<View style={styles.companyRow}>
  <Text style={styles.companyText}>
    {collaboration?.brandName || "Unknown Brand"}
  </Text>
🔗 Analysis chain

Validate optional fields.
Calls to collaboration?.name and collaboration?.brandName are safe, but consider default text (e.g., "Unknown Collaboration") if these properties are null or undefined.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Searching for references to collaboration.name or brandName across the repository:
rg -A 3 $'collaboration\\.name' 
rg -A 3 $'collaboration\\.brandName'

Length of output: 2873

Comment on lines +25 to +46
const fetchAppliedCount = async () => {
const appliedRef = collection(
FirestoreDB,
"collaborations",
props.collabID,
"applications"
);
const fetchApplied = await getDocs(appliedRef);
const docsData = fetchApplied.docs.map((doc) => doc.data());
setAppliedCount(docsData.length);
const invitedRef = collection(
FirestoreDB,
"collaborations",
props.collabID,
"invitations"
);
const fetchInvite = await getDocs(invitedRef);
const docsDataInvited = fetchInvite.docs.map((doc) => doc.data());
console.log(docsDataInvited.length);

setInvitedCount(docsDataInvited.length);
};

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

Add basic error handling for Firestore operations.
Currently, if getDocs fails, we don’t catch or handle the error, which might cause unhandled promise rejections. Consider wrapping the request in a try-catch block to provide user feedback or fallback behavior.

 const fetchAppliedCount = async () => {
-  const appliedRef = ...
-  const fetchApplied = await getDocs(appliedRef);
-  ...
+  try {
+    const appliedRef = collection(
+      FirestoreDB,
+      "collaborations",
+      props.collabID,
+      "applications"
+    );
+    const fetchApplied = await getDocs(appliedRef);
+    const docsData = fetchApplied.docs.map((doc) => doc.data());
+    setAppliedCount(docsData.length);
+
+    const invitedRef = ...
+    const fetchInvite = await getDocs(invitedRef);
+    const docsDataInvited = fetchInvite.docs.map((doc) => doc.data());
+    setInvitedCount(docsDataInvited.length);
+  } catch (error) {
+    console.error("Failed to fetch applied/invited counts", error);
+  }
 };

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

Comment on lines +18 to +27
const themeChange = async () => {
if (!manager) {
return;
}

updateManager(
manager?.id,
{
settings: {
theme: isSwitchOn ? "light" : "dark"
},
updateManager(manager?.id, {
settings: {
theme: selectedTheme,
},
);
});

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

Add error handling for manager updates.
themeChange updates the manager’s settings but doesn’t catch or handle potential Firestore errors. Wrap in a try-catch to prevent unhandled rejections.

const themeChange = async () => {
  if (!manager) {
    return;
  }
- updateManager(manager?.id, {
-   settings: {
-     theme: selectedTheme,
-   },
- });
+ try {
+   await updateManager(manager?.id, {
+     settings: {
+       theme: selectedTheme,
+     },
+   });
+ } catch (err) {
+   console.error("Failed to update manager data", err);
+ }
};
📝 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 themeChange = async () => {
if (!manager) {
return;
}
updateManager(
manager?.id,
{
settings: {
theme: isSwitchOn ? "light" : "dark"
},
updateManager(manager?.id, {
settings: {
theme: selectedTheme,
},
);
});
const themeChange = async () => {
if (!manager) {
return;
}
try {
await updateManager(manager?.id, {
settings: {
theme: selectedTheme,
},
});
} catch (err) {
console.error("Failed to update manager data", err);
}

Comment on lines +25 to +43
const openGallery = async () => {
const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
if (status !== 'granted') {
alert('We need camera permissions');
return;
}

const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [4, 3],
quality: 1,
});

if (!result.canceled) {
onImageUpload(result.assets[0].uri);
setVisible(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.

⚠️ Potential issue

Alert text "We need camera permissions" might be misleading.

When opening the gallery, you request media library permissions, not camera permissions. Adjust the alert message to reference the correct permission for clarity. For example: "We need media library permissions."

- alert('We need camera permissions');
+ alert('We need gallery permissions');
📝 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 openGallery = async () => {
const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
if (status !== 'granted') {
alert('We need camera permissions');
return;
}
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [4, 3],
quality: 1,
});
if (!result.canceled) {
onImageUpload(result.assets[0].uri);
setVisible(false);
}
}
const openGallery = async () => {
const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
if (status !== 'granted') {
alert('We need gallery permissions');
return;
}
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [4, 3],
quality: 1,
});
if (!result.canceled) {
onImageUpload(result.assets[0].uri);
setVisible(false);
}
}

Comment on lines +21 to +30
interface CollaborationDetailsProps {
collabDescription: string;
name: string;
collabId: string;
location: any;
platform: string[];
contentType: string[];
promotionType: PromotionType;
onOpenBottomSheet: (id: string) => void;
}

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

Use stronger typing for location.

location is typed as any. If you know its structure (e.g., an object with type, region, etc.), define a proper interface or type alias. This ensures better code completion and prevents accidental property access errors.

-interface CollaborationDetailsProps {
-  location: any;
+interface CollaborationDetailsProps {
+  location: {
+    type: string;
+    // add other relevant fields
+  };

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

Comment on lines +42 to +52
const updateCollaboration = async (field: string, value: any) => {
try {
const collabRef = doc(FirestoreDB, "collaborations", pageID);
await updateDoc(collabRef, {
[`preferences.${field}`]: value,
});
console.log(`${field} updated to`, value);
} catch (error) {
console.error("Error updating Firestore:", error);
}
};

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

Check for missing Firestore fields.
When updating a subfield like preferences.<field>, confirm that preferences is already defined in Firestore. If not, consider fallback initialization or error handling.

Comment thread components/menu/index.tsx
Comment on lines +69 to +74
<ProfileItemCard
item={item}
onPress={() => {
router.push(item.href);
}}
/>

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

Add a unique key to each iterated item.
React requires a key prop to efficiently track components in lists.

-            <ProfileItemCard
-              item={item}
-              onPress={() => {
-                router.push(item.href);
-              }}
-            />
+            <ProfileItemCard
+              key={item.id}
+              item={item}
+              onPress={() => {
+                router.push(item.href);
+              }}
+            />

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

🧰 Tools
🪛 Biome (1.9.4)

[error] 69-74: Missing key property for this element in iterable.

The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.

(lint/correctness/useJsxKeyInIterable)

brandImage: string;
brandWebsite: string;
visible: boolean;
category: string[];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Similar properties to object

managerName: string;
managerEmail: string;
brandDescription: string;
managerImage: string;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Similar properties to object.

Comment thread components/proposals/Applications.tsx Resolved
fontSize: 14,
color: Colors(theme).text,
textAlign: "left",
marginVertical: 8,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can we use gap instead of a lot of marginVertical, Top, and Bottom. We may need to update a lot of we use marginVertical, Top and Bottom

@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: 0

🔭 Outside diff range comments (2)
components/collaboration/CollaborationHeader.tsx (2)

Line range hint 43-58: Remove duplicate promotion type tag

The same promotion type is being rendered twice with different icons. This appears to be unintentional.

Consider combining these into a single tag or clarifying the purpose if they're meant to represent different aspects:

- {
-   collaboration.promotionType && (
-     <Tag
-       icon={() => (
-         <FontAwesomeIcon
-           color={Colors(theme).primary}
-           icon={faBolt}
-           size={14}
-         />
-       )}
-     >
-       {collaboration.promotionType}
-     </Tag>
-   )
- }

Line range hint 75-89: Add null check for location property

The location tag is rendered without checking if the location object exists, which could cause runtime errors.

-        <Tag
-          icon={() => (
-            <FontAwesomeIcon
-              color={Colors(theme).primary}
-              icon={faLocationDot}
-              size={14}
-            />
-          )}
-        >
-          {
-            collaboration.location.type === "Remote"
-              ? "Remote"
-              : collaboration.location.name
-          }
-        </Tag>
+        {collaboration.location && (
+          <Tag
+            icon={() => (
+              <FontAwesomeIcon
+                color={Colors(theme).primary}
+                icon={faLocationDot}
+                size={14}
+              />
+            )}
+          >
+            {
+              collaboration.location.type === "Remote"
+                ? "Remote"
+                : collaboration.location.name
+            }
+          </Tag>
+        )}
🧹 Nitpick comments (1)
components/collaboration/CollaborationHeader.tsx (1)

16-18: Fix typo in interface name

The interface name contains a spelling error: ColloborationHeaderProps should be CollaborationHeaderProps.

-interface ColloborationHeaderProps {
+interface CollaborationHeaderProps {
   collaboration: CollaborationDetail;
 }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 8609f30 and 98d4b07.

📒 Files selected for processing (1)
  • components/collaboration/CollaborationHeader.tsx (3 hunks)
🔇 Additional comments (2)
components/collaboration/CollaborationHeader.tsx (2)

31-35: Add fallback values for optional fields

The optional chaining is good, but fallback values should be provided for better user experience.

-          <Avatar.Image size={48} source={imageUrl(collaboration?.logo)} />
+          <Avatar.Image size={48} source={imageUrl(collaboration?.logo) || require('@/assets/images/default-avatar.png')} />
           <View style={styles.titleSection}>
-            <Text style={styles.title}>{collaboration?.name}</Text>
+            <Text style={styles.title}>{collaboration?.name || "Untitled Collaboration"}</Text>
             <View style={styles.companyRow}>
-              <Text style={styles.companyText}>{collaboration?.brandName}</Text>
+              <Text style={styles.companyText}>{collaboration?.brandName || "Unknown Brand"}</Text>

Line range hint 16-90: Verify component usage after proposed changes

Since this component is used in Applications.tsx, we should verify that our proposed changes won't break existing functionality.

✅ Verification successful

No issues found with the proposed changes to CollaborationHeader

Based on the search results, the CollaborationHeader component's usage of collaboration properties is consistent with how they are accessed throughout the codebase. The component safely handles optional properties using the optional chaining operator (?.), which aligns with the pattern seen in other components. The properties being accessed (promotionType, location, name, brandName, logo) are used consistently across the codebase.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for CollaborationHeader usage
rg -A 5 "CollaborationHeader" --type typescript --type tsx

# Search for patterns of accessing collaboration properties to ensure consistent handling
ast-grep --pattern 'collaboration.$_'

Length of output: 6345

@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: 7

🔭 Outside diff range comments (1)
components/proposals/Applications.tsx (1)

Line range hint 31-32: Improve type safety and error handling in data fetching

  1. Replace any[] with proper types
  2. Add error boundaries
  3. Enhance error handling in fetch operations
+interface Proposal {
+  id: string;
+  brandId: string;
+  applications: number;
+  invitations: number;
+  acceptedApplications: number;
+  status: string;
+  timeStamp: Date;
+}

-const [proposals, setProposals] = useState<any[]>([]);
+const [proposals, setProposals] = useState<Proposal[]>([]);

 const fetchProposals = async () => {
   try {
     if (!selectedBrand) {
+      throw new Error("No brand selected");
       return;
     }
     // ... existing code ...
   } catch (error) {
-    console.error("Error fetching proposals: ", error);
+    const e = error as Error;
+    console.error("Error fetching proposals: ", e.message);
+    // TODO: Add user feedback mechanism
   }
 };

Also applies to: 58-108

🧹 Nitpick comments (15)
components/BottomSheetActions.tsx (1)

13-18: Refine the union type definitions.
The expanded union for cardType is clear and descriptive. Consider adding JSDoc comments or TypeScript docs here for each possible cardType value to aid future contributors.

components/collaboration/collaboration-details/modal/BrandModal.tsx (4)

5-5: Consider creating dedicated styles for BrandModal

The component is currently using styles from UploadModal.styles. While this might work, it's better to have dedicated styles for the BrandModal component to maintain better separation of concerns and prevent unintended style changes.

Create a new styles file at @/styles/modal/BrandModal.styles.ts for better maintainability.


11-22: Extract Brand interface for reusability

The brand object structure could be reused across components. Consider extracting it into a separate interface.

+interface Brand {
+  name: string;
+  description: string;
+  image: string;
+  website: string;
+  verified: boolean;
+  category: string[];
+}

 interface BrandModalProps {
-  brand: {
-    name: string;
-    description: string;
-    image: string;
-    website: string;
-    verified: boolean;
-    category: string[];
-  };
+  brand: Brand;
   visible: boolean;
   setVisibility: (visible: boolean) => void;
 }

54-72: Improve brand name and verification icon layout

The current implementation uses string concatenation with space which could cause layout issues. Also, the Text component has a redundant alignItems style.

-<Text
+<View
   style={{
-    fontSize: 24,
-    fontWeight: "bold",
-    color: Colors(theme).text,
-    textAlign: "center",
-    alignItems: "center",
+    flexDirection: 'row',
+    alignItems: 'center',
+    justifyContent: 'center',
   }}
 >
-  {brand.name}{" "}
+  <Text
+    style={{
+      fontSize: 24,
+      fontWeight: "bold",
+      color: Colors(theme).text,
+    }}
+  >
+    {brand.name}
+  </Text>
   {brand.verified && (
     <FontAwesomeIcon
+      style={{ marginLeft: 5 }}
       icon={faCheckCircle}
       color={Colors(theme).primary}
       size={20}
     />
   )}
-</Text>
+</View>

93-103: Enhance category chips and fix key prop

Using array index as key prop could cause issues with React's reconciliation if the array order changes. Also, consider enhancing the chips with color coding or icons for better visual hierarchy.

-{brand.category.map((cat, index) => (
+{brand.category.map((cat) => (
   <Chip
-    key={index}
+    key={cat}
     style={{
       margin: 5,
+      backgroundColor: getCategoryColor(cat),
     }}
     mode="outlined"
+    icon={() => getCategoryIcon(cat)}
   >
     {cat}
   </Chip>
))}
components/collaboration/collaboration-details/index.tsx (3)

40-51: Enhance error handling in publishCollaboration

While basic error handling exists, consider these improvements:

  1. Add error type checking
  2. Add success/failure feedback to the user
  3. Validate the update success
 const publishCollaboration = async () => {
   if (!pageID) return;
   try {
     const collabRef = doc(FirestoreDB, "collaborations", pageID);
     await updateDoc(collabRef, {
       status: "active",
     });
+    const updatedDoc = await getDoc(collabRef);
+    const success = updatedDoc.data()?.status === "active";
+    if (!success) {
+      throw new Error("Failed to update collaboration status");
+    }
     fetchCollaboration();
   } catch (e) {
-    console.error(e);
+    const error = e as Error;
+    console.error("Failed to publish collaboration:", error.message);
+    // TODO: Add user feedback mechanism
   }
 };

Line range hint 54-83: Improve error handling and type safety in fetchCollaboration

The function handles data fetching well, but consider these improvements:

  1. Add type guard for brand data
  2. Add loading state management
  3. Add specific error types
 const fetchCollaboration = async () => {
   if (!pageID) return;
+  setLoading(true);
   try {
     const collabRef = doc(FirestoreDB, "collaborations", pageID as string);
     const snapshot = await getDoc(collabRef);
     const data = snapshot.data() as ICollaboration;
-    if (!data) return;
+    if (!data) throw new Error("Collaboration not found");

     const brandRef = doc(FirestoreDB, "brands", data.brandId);
     const brandSnapshot = await getDoc(brandRef);
+    if (!brandSnapshot.exists()) {
+      throw new Error("Brand not found");
+    }
     const brandData = brandSnapshot.data() as IBrands;
+    if (!brandData) throw new Error("Invalid brand data");

     setCollaboration({
       ...data,
       logo: brandData?.image || "",
       brandName: brandData?.name || "Unknown Brand",
       paymentVerified: brandData?.paymentMethodVerified || false,
       brandDescription: brandData?.profile?.about || "",
       brandWebsite: brandData?.profile?.website || "",
       brandCategory: brandData?.preferences?.influencerCategory || [],
     });
   } catch (e) {
-    console.error(e);
+    const error = e as Error;
+    console.error("Failed to fetch collaboration:", error.message);
+    // TODO: Add user feedback mechanism
   } finally {
     setLoading(false);
   }
 };

149-190: Improve button styling consistency

The Edit Draft and Publish Now buttons have inconsistent styling. Consider extracting common button styles and maintaining consistent visual hierarchy.

 <Button
   mode="contained"
   onPress={() => {
     router.push({
       pathname: "/edit-collaboration",
       params: {
         id: pageID,
       },
     });
   }}
   style={{
     flex: 1,
-    backgroundColor: Colors(theme).background,
-    borderWidth: 0.3,
-    borderColor: Colors(theme).outline,
+    backgroundColor: Colors(theme).surface,
+    borderWidth: 1,
+    borderColor: Colors(theme).primary,
   }}
-  textColor={Colors(theme).text}
+  textColor={Colors(theme).primary}
 >
   Edit Draft
 </Button>
components/collaboration/collaboration-details/SettingsTabContent.tsx (2)

19-40: Simplify state management and improve type safety

Consider using a single state object with proper typing for better maintainability.

+interface SettingsState {
+  timeCommitment: SelectItem;
+  niches: SelectItem[];
+  influencerLookingFor: SelectItem;
+  preferredVideoType: SelectItem;
+}
+
+const initialState: SettingsState = {
+  timeCommitment: { label: "None", value: "None" },
+  niches: [],
+  influencerLookingFor: { label: "None", value: "None" },
+  preferredVideoType: { label: "None", value: "None" }
+};
+
-const [timeCommitment, setTimeCommitment] = useState<{label: string; value: string}>({...});
-const [niches, setNiches] = useState<SelectItem[]>([]);
-const [influencerLookingFor, setInfluencerLookingFor] = useState<{...}>({...});
-const [preferredVideoType, setPreferredVideoType] = useState<{...}>({...});
+const [settings, setSettings] = useState<SettingsState>(initialState);

100-262: Optimize styles and reduce duplication

Consider extracting common styles into reusable constants or components.

+const styles = {
+  sectionContainer: {
+    gap: 12,
+  },
+  sectionTitle: {
+    fontSize: 20,
+    fontWeight: "bold",
+  },
+  sectionDescription: {
+    fontSize: 14,
+  },
+};

-<View style={{ gap: 12 }}>
+<View style={styles.sectionContainer}>
   <Text
-    style={{
-      fontSize: 20,
-      fontWeight: "bold",
-    }}
+    style={styles.sectionTitle}
   >
components/proposals/Applications.tsx (1)

254-259: Implement proper responsive design

The TODO comment indicates incomplete responsive implementation. Consider using a proper responsive grid system.

Would you like me to help implement a proper responsive grid system that doesn't rely on hardcoded column counts?

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

1-38: LGTM! Consider organizing imports for better maintainability.

The imports and interface declaration look good. Consider grouping imports into categories:

  1. React/React Native
  2. UI Libraries (react-native-paper)
  3. Internal components/utils
  4. Icons/constants
// React and React Native
import React, { useEffect, useState } from "react";
import { View, Image, ScrollView, Pressable, Linking } from "react-native";

// UI Libraries
import { Text, Card, Paragraph, Button, Portal } from "react-native-paper";
import { useTheme } from "@react-navigation/native";

// Internal Components and Utils
import { stylesFn } from "@/styles/CollaborationDetails.styles";
import { FirestoreDB } from "@/utils/firestore";
import { doc, getDoc, updateDoc } from "firebase/firestore";
import { CollaborationDetail } from ".";

// Icons and Constants
import {
  faCheckCircle,
  faCoins,
  // ... rest of the icons
} from "@fortawesome/free-solid-svg-icons";

77-100: Extract star rating logic into a reusable component and use dynamic rating.

The star rating implementation is good but could be improved:

  1. Should be a separate reusable component
  2. Rating value is hardcoded (4.5)

Consider creating a separate StarRating component:

// components/StarRating.tsx
interface StarRatingProps {
  rating: number;
  size?: number;
  color?: string;
}

const StarRating: React.FC<StarRatingProps> = ({ rating, size = 16, color }) => {
  const fullStars = Math.floor(rating);
  const hasHalfStar = rating % 1 !== 0;

  return (
    <View style={{ flexDirection: "row" }}>
      {Array.from({ length: fullStars }, (_, i) => (
        <FontAwesomeIcon
          key={i}
          icon={faStar}
          size={size}
          color={color}
        />
      ))}
      {hasHalfStar && (
        <FontAwesomeIcon
          icon={faStarHalfStroke}
          size={size}
          color={color}
        />
      )}
    </View>
  );
};

Then update the usage:

- <View style={{ flexDirection: "row" }}>{renderStars(4.5)}</View>
+ <StarRating 
+   rating={props.collaboration.rating} 
+   color={Colors(theme).yellow} 
+ />

Also applies to: 164-164


109-207: Improve code maintainability by extracting inline styles and simplifying logic.

The collaboration details card needs the following improvements:

  1. Move inline styles to the styles object
  2. Simplify nested ternary operators
// In stylesFn
+ const styles = StyleSheet.create({
+   headerContainer: {
+     display: "flex",
+     flexDirection: "row",
+     justifyContent: "space-between",
+     width: "100%",
+     alignItems: "center",
+   },
+   timestamp: {
+     fontSize: 12,
+     paddingRight: 8,
+   },
+   // ... other styles
+ });

// In component
- <View
-   style={{
-     display: "flex",
-     flexDirection: "row",
-     justifyContent: "space-between",
-     width: "100%",
-     alignItems: "center",
-   }}
- >
+ <View style={styles.headerContainer}>

// Simplify budget display
+ const formatBudget = (budget) => {
+   if (!budget) return '';
+   return budget.min === budget.max 
+     ? `$${budget.min}` 
+     : `$${budget.min} - $${budget.max}`;
+ };

478-499: Add TypeScript interfaces and improve props organization for modals.

The modals implementation could be improved with better TypeScript support:

// Add these interfaces
interface BrandModalProps {
  brand: {
    category: string;
    description: string;
    image: string;
    name: string;
    verified: boolean;
    website: string;
  };
  visible: boolean;
  setVisibility: (visible: boolean) => void;
}

interface ManagerModalProps {
  manager: {
    email: string;
    image: string;
    name: string;
  };
  brandDescription: string;
  visible: boolean;
  setVisibility: (visible: boolean) => void;
}

// Update modal usage
<ManagerModal
  manager={{
    email: managerDetails?.email,
    image: managerDetails?.profileImage,
    name: managerDetails?.name,
  }}
  brandDescription={props.collaboration.brandDescription}
  visible={managerModalVisible}
  setVisibility={setManagerModalVisible}
/>
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 98d4b07 and 3835600.

📒 Files selected for processing (7)
  • components/BottomSheetActions.tsx (5 hunks)
  • components/collaboration/CollaborationHeader.tsx (2 hunks)
  • components/collaboration/collaboration-details/OverviewTabContent.tsx (1 hunks)
  • components/collaboration/collaboration-details/SettingsTabContent.tsx (1 hunks)
  • components/collaboration/collaboration-details/index.tsx (5 hunks)
  • components/collaboration/collaboration-details/modal/BrandModal.tsx (1 hunks)
  • components/proposals/Applications.tsx (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • components/collaboration/CollaborationHeader.tsx
🔇 Additional comments (5)
components/BottomSheetActions.tsx (3)

2-2: Ensure consistent usage of imports.
You introduced Pressable in the import statement, which is a great way to manage pressing interactions. Just verify that you have standardized on Pressable vs TouchableOpacity throughout the codebase for consistency.


39-40: Destructure context carefully.
Destructuring createGroupWithMembers, connectUser, and createNotification here is concise. However, ensure that these functions are only accessed where needed to minimize potential confusion.


242-242: Validate route parameters.
Navigating to /collaboration-details/${cardId} is straightforward. Just verify that cardId is defined and is the correct value type (string vs. object) to avoid broken routes.

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

18-24: LGTM: Interface extensions are well-defined

The new fields brandWebsite and brandCategory are properly typed and extend the base interface.

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

51-63: Check for missing Firestore fields

When updating a subfield like preferences.<field>, confirm that preferences is already defined in Firestore. If not, consider fallback initialization or error handling.

Comment on lines +67 to +69
createGroupWithMembers(data.collaboration.name, [
cardId.influencerID,
]).then((channel) => {

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

Check error boundary coverage.
The createGroupWithMembers call is wrapped inside a .then(...) but a potential rejection from this promise chain might bypass the subsequent .catch(...) block. Adding inline error handling or a try/catch within the .then(...) block can provide clearer error feedback.

- createGroupWithMembers(data.collaboration.name, [
-   cardId.influencerID,
- ])
-   .then((channel) => {
+ createGroupWithMembers(data.collaboration.name, [cardId.influencerID])
+   .then((channel) => {
      try {
        connectUser();
        ...
      } catch (err) {
        console.error(err);
        handleClose();
        Toaster.error("Group creation encountered a problem");
      }
    })

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

Comment on lines +107 to +125
<Pressable
onPress={() => Linking.openURL(brand.website)}
style={{
backgroundColor: Colors(theme).primary,
borderRadius: 5,
paddingVertical: 10,
paddingHorizontal: 20,
}}
>
<Text
style={{
fontSize: 16,
color: "white",
fontWeight: "bold",
}}
>
Visit Website
</Text>
</Pressable>

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

Add error handling for URL opening and use theme colors consistently

The website button should handle URL opening errors and use theme colors consistently.

 <Pressable
-  onPress={() => Linking.openURL(brand.website)}
+  onPress={async () => {
+    try {
+      const supported = await Linking.canOpenURL(brand.website);
+      if (supported) {
+        await Linking.openURL(brand.website);
+      } else {
+        Alert.alert("Error", "Cannot open this URL");
+      }
+    } catch (error) {
+      Alert.alert("Error", "Failed to open website");
+    }
+  }}
   style={{
     backgroundColor: Colors(theme).primary,
     borderRadius: 5,
     paddingVertical: 10,
     paddingHorizontal: 20,
   }}
 >
   <Text
     style={{
       fontSize: 16,
-      color: "white",
+      color: Colors(theme).background,
       fontWeight: "bold",
     }}
   >
     Visit Website
   </Text>
 </Pressable>
📝 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
<Pressable
onPress={() => Linking.openURL(brand.website)}
style={{
backgroundColor: Colors(theme).primary,
borderRadius: 5,
paddingVertical: 10,
paddingHorizontal: 20,
}}
>
<Text
style={{
fontSize: 16,
color: "white",
fontWeight: "bold",
}}
>
Visit Website
</Text>
</Pressable>
<Pressable
onPress={async () => {
try {
const supported = await Linking.canOpenURL(brand.website);
if (supported) {
await Linking.openURL(brand.website);
} else {
Alert.alert("Error", "Cannot open this URL");
}
} catch (error) {
Alert.alert("Error", "Failed to open website");
}
}}
style={{
backgroundColor: Colors(theme).primary,
borderRadius: 5,
paddingVertical: 10,
paddingHorizontal: 20,
}}
>
<Text
style={{
fontSize: 16,
color: Colors(theme).background,
fontWeight: "bold",
}}
>
Visit Website
</Text>
</Pressable>

Comment on lines +45 to +52
<Image
source={{ uri: brand.image }}
style={{
width: 120,
height: 120,
borderRadius: 10,
}}
/>

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

Add error handling for image loading

The Image component should handle loading and error states to provide better user feedback.

 <Image
   source={{ uri: brand.image }}
+  onError={(e) => console.error('Error loading brand image:', e.nativeEvent.error)}
+  defaultSource={require('@/assets/images/placeholder.png')}
   style={{
     width: 120,
     height: 120,
     borderRadius: 10,
   }}
+  loadingIndicatorSource={require('@/assets/images/loading.gif')}
 />

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

Comment on lines +165 to +169
item.attachments?.map(
//@ts-ignore
(attachment: MediaItem) =>
processRawAttachment(attachment)
) || []

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

Remove @ts-ignore and fix type safety

Instead of using @ts-ignore, properly type the attachment processing.

-//@ts-ignore
-item.attachments?.map((attachment: MediaItem) =>
+item.attachments?.map((attachment: MediaItem & { uri?: string }) =>

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

Comment on lines +416 to +475
<View style={{ width: "100%", marginTop: 16, gap: 16 }}>
<Text
style={{
fontSize: 16,
color: Colors(theme).text,
fontWeight: "bold",
}}
>
Posted by
</Text>
<Pressable onPress={() => setManagerModalVisible(true)}>
<View
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
gap: 10,
}}
>
<Image
source={
!managerDetails?.profileImage
? { uri: PLACEHOLDER_IMAGE }
: { uri: managerDetails?.profileImage }
}
style={{
width: 40,
height: 40,
borderRadius: 20,
}}
/>
<View
style={{
display: "flex",
flexDirection: "column",
justifyContent: "center",
gap: 2,
}}
>
<Text
style={{
fontSize: 16,
fontWeight: "bold",
color: Colors(theme).text,
}}
>
{managerDetails?.name}
</Text>
<Text
style={{
fontSize: 16,
color: Colors(theme).gray100,
}}
>
{managerDetails?.role} - {props.collaboration.brandName}
</Text>
</View>
</View>
</Pressable>
</View>

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 loading state and handle potential race conditions in manager details.

The manager details section needs improvement in handling asynchronous data:

  1. Missing loading state while fetching manager details
  2. Potential race condition between render and data fetching
+ const [isLoading, setIsLoading] = useState(true);

  const fetchManagerDetails = async () => {
+   setIsLoading(true);
    try {
      // ... existing fetch logic
      setManagerDetails({
        name: managerData.name,
        email: managerData.email,
        profileImage: managerData.profileImage,
        role: managerBrandData?.role,
      });
    } catch (error) {
      console.error('Error fetching manager details:', error);
    } finally {
+     setIsLoading(false);
    }
  };

  // In the render section
  <View style={{ width: "100%", marginTop: 16, gap: 16 }}>
    <Text style={styles.sectionTitle}>Posted by</Text>
+   {isLoading ? (
+     <ActivityIndicator size="small" color={Colors(theme).primary} />
+   ) : (
      <Pressable onPress={() => setManagerModalVisible(true)}>
        {/* ... existing manager details UI */}
      </Pressable>
+   )}
  </View>

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

Comment on lines +40 to +75
const OverviewTabContent = (props: CollaborationDetailsContentProps) => {
const theme = useTheme();
const styles = stylesFn(theme);
const [status, setStatus] = React.useState("pending");
const [managerDetails, setManagerDetails] = React.useState<any>();
const [brandModalVisible, setBrandModalVisible] = useState(false);
const [managerModalVisible, setManagerModalVisible] = useState(false);

const fetchManagerDetails = async () => {
const managerRef = doc(
FirestoreDB,
"managers",
props.collaboration.managerId
);
const managerDoc = await getDoc(managerRef);

const managerBrandref = doc(
FirestoreDB,
"brands",
props.collaboration.brandId,
"members",
props.collaboration.managerId
);

const {
xl,
} = useBreakpoints();
const managerBrandDoc = await getDoc(managerBrandref);

const managerData = managerDoc.data() as IManagers;
const managerBrandData = managerBrandDoc.data();

setManagerDetails({
name: managerData.name,
email: managerData.email,
profileImage: managerData.profileImage,
role: managerBrandData?.role,
});
};

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 to Firestore operations and remove unused state.

The data fetching logic needs improvement in the following areas:

  1. Missing error handling in fetchManagerDetails
  2. Unused status state variable
- const [status, setStatus] = React.useState("pending");
  const [managerDetails, setManagerDetails] = React.useState<any>();

  const fetchManagerDetails = async () => {
+   try {
      const managerRef = doc(
        FirestoreDB,
        "managers",
        props.collaboration.managerId
      );
      const managerDoc = await getDoc(managerRef);
+     if (!managerDoc.exists()) {
+       throw new Error('Manager document not found');
+     }

      const managerBrandref = doc(
        FirestoreDB,
        "brands",
        props.collaboration.brandId,
        "members",
        props.collaboration.managerId
      );

      const managerBrandDoc = await getDoc(managerBrandref);
+     if (!managerBrandDoc.exists()) {
+       throw new Error('Manager brand document not found');
+     }

      const managerData = managerDoc.data() as IManagers;
      const managerBrandData = managerBrandDoc.data();

      setManagerDetails({
        name: managerData.name,
        email: managerData.email,
        profileImage: managerData.profileImage,
        role: managerBrandData?.role,
      });
+   } catch (error) {
+     console.error('Error fetching manager details:', error);
+     // Consider showing an error message to the user
+   }
  };
📝 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 OverviewTabContent = (props: CollaborationDetailsContentProps) => {
const theme = useTheme();
const styles = stylesFn(theme);
const [status, setStatus] = React.useState("pending");
const [managerDetails, setManagerDetails] = React.useState<any>();
const [brandModalVisible, setBrandModalVisible] = useState(false);
const [managerModalVisible, setManagerModalVisible] = useState(false);
const fetchManagerDetails = async () => {
const managerRef = doc(
FirestoreDB,
"managers",
props.collaboration.managerId
);
const managerDoc = await getDoc(managerRef);
const managerBrandref = doc(
FirestoreDB,
"brands",
props.collaboration.brandId,
"members",
props.collaboration.managerId
);
const {
xl,
} = useBreakpoints();
const managerBrandDoc = await getDoc(managerBrandref);
const managerData = managerDoc.data() as IManagers;
const managerBrandData = managerBrandDoc.data();
setManagerDetails({
name: managerData.name,
email: managerData.email,
profileImage: managerData.profileImage,
role: managerBrandData?.role,
});
};
const OverviewTabContent = (props: CollaborationDetailsContentProps) => {
const theme = useTheme();
const styles = stylesFn(theme);
const [managerDetails, setManagerDetails] = React.useState<any>();
const [brandModalVisible, setBrandModalVisible] = useState(false);
const [managerModalVisible, setManagerModalVisible] = useState(false);
const fetchManagerDetails = async () => {
try {
const managerRef = doc(
FirestoreDB,
"managers",
props.collaboration.managerId
);
const managerDoc = await getDoc(managerRef);
if (!managerDoc.exists()) {
throw new Error('Manager document not found');
}
const managerBrandref = doc(
FirestoreDB,
"brands",
props.collaboration.brandId,
"members",
props.collaboration.managerId
);
const managerBrandDoc = await getDoc(managerBrandref);
if (!managerBrandDoc.exists()) {
throw new Error('Manager brand document not found');
}
const managerData = managerDoc.data() as IManagers;
const managerBrandData = managerBrandDoc.data();
setManagerDetails({
name: managerData.name,
email: managerData.email,
profileImage: managerData.profileImage,
role: managerBrandData?.role,
});
} catch (error) {
console.error('Error fetching manager details:', error);
// Consider showing an error message to the user
}
};

Comment on lines +346 to +377
{props.collaboration.location.type === "Physical" && (
<View
style={{
width: "100%",
gap: 16,
marginVertical: 16,
}}
>
<Text
style={{
fontSize: 16,
color: Colors(theme).text,
fontWeight: "bold",
}}
>
Location
</Text>
<CreateCollaborationMap
mapRegion={{
latitude: props.collaboration?.location?.latlong?.lat,
longitude: props.collaboration?.location?.latlong?.long,
latitudeDelta: 0.0922,
longitudeDelta: 0.042,
}}
onMapRegionChange={(region) => {}}
onFormattedAddressChange={(address) => {}}
/>
<Text style={{ fontSize: 16, color: Colors(theme).text }}>
{props.collaboration.location.name}
</Text>
</View>
)}

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 proper handling for map callbacks and location coordinates.

The location rendering section has potential issues:

  1. Empty callback functions in CreateCollaborationMap
  2. Missing null checks for location coordinates
+ const mapRegion = props.collaboration?.location?.latlong 
+   ? {
+     latitude: props.collaboration.location.latlong.lat,
+     longitude: props.collaboration.location.latlong.long,
+     latitudeDelta: 0.0922,
+     longitudeDelta: 0.042,
+   }
+   : null;

  {props.collaboration.location.type === "Physical" && (
+   mapRegion && (
      <View style={styles.locationContainer}>
        <Text style={styles.sectionTitle}>Location</Text>
        <CreateCollaborationMap
-         mapRegion={{
-           latitude: props.collaboration?.location?.latlong?.lat,
-           longitude: props.collaboration?.location?.latlong?.long,
-           latitudeDelta: 0.0922,
-           longitudeDelta: 0.042,
-         }}
-         onMapRegionChange={(region) => {}}
-         onFormattedAddressChange={(address) => {}}
+         mapRegion={mapRegion}
+         onMapRegionChange={(region) => {
+           // Consider if you need to handle region changes
+           console.log('Map region changed:', region);
+         }}
+         onFormattedAddressChange={(address) => {
+           // Consider if you need to handle address changes
+           console.log('Address changed:', address);
+         }}
        />
      </View>
+   )
  )}

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

@Manan-Sharma-5
Manan-Sharma-5 merged commit 48835a9 into dev Dec 26, 2024
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