Skip to content

Conversation

@priscilladeroode
Copy link
Contributor

@priscilladeroode priscilladeroode commented Dec 1, 2024

As a user, on the BaseApp Profile,I would like to Create an organization profile, In order to To create social activites in the name of that organization profile.

Acceptance Criteria
Context
This story will handle creating a new organization profiles and also setting the logic for the relationship between members and organizations.

Business Rules - Create Organization

Given I select the Switch Profile option in my User Settings dropdown, then display an [Add new organization] option at the bottom of the list.

After clicking that new option, a modal should appear with the following inputs:

Name

URL path - this will be the details to navigate to the profile in the URL.

(Ex: airtab.com/BigAlex)

Given the user has submitted the form, display a toast indicating the organizations creation was succesful.

Given the creation was succesful, then the user that created the organization should be set as a member with the Owner Role

The user should now see this organization in his Switch Profile list.

Validation Rules

The username should be unique, meaning that we should display an error message if the username has already been taken.

Both input fields should have content before enabling the "Create organization button"

Copy Text

Username already in use, please choose another one"

Design Link: https://www.figma.com/design/XRD6wSl1m8Kz6XUcAy5CLp/BaseApp---WEB?node-id=218-293195&node-type=canvas&t=o7CIQdSvtnKDiy3z-0

Notes
Implementation Rules

Create Organization modal

Needs to inherent from the baseapp ProfieableModel

Create GraphQL mutation

Approvd
https://app.approvd.io/projects/BA/stories/34196

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a modal for creating new organization profiles.
    • Added functionality to manage profile visibility and permissions dynamically.
    • Enhanced the ProfilesList component with pagination and loading states.
    • Implemented a new query for fetching user permissions related to organization creation.
    • Added a new styled component for form layouts.
    • Introduced a new constant for URL path validation.
  • Bug Fixes

    • Improved error handling for profile data fetching.
    • Enhanced safety in accessing profile data to prevent runtime errors.
  • Documentation

    • Updated GraphQL schema with new types and improved comments for clarity.
  • Chores

    • Updated dependencies for better version control and stability.
    • Added new scripts for managing GraphQL schema.

@coderabbitai
Copy link

coderabbitai bot commented Dec 1, 2024

Warning

There were issues while running some tools. Please review the errors and either fix the tool’s configuration or disable the tool if it’s a critical failure.

🔧 eslint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

packages/components/modules/navigations/Header/AccountMenu/AccountPopover/__tests__/AccountPopover.cy.tsx

Oops! Something went wrong! :(

ESLint: 8.57.1

Error: Cannot read config file: /packages/components/.eslintrc.js
Error: Cannot find module '@baseapp-frontend/config/.eslintrc.js'
Require stack:

  • /packages/components/.eslintrc.js
  • /node_modules/.pnpm/@eslint+eslintrc@2.1.4/node_modules/@eslint/eslintrc/dist/eslintrc.cjs
  • /node_modules/.pnpm/eslint@8.57.1/node_modules/eslint/lib/cli-engine/cli-engine.js
  • /node_modules/.pnpm/eslint@8.57.1/node_modules/eslint/lib/eslint/eslint.js
  • /node_modules/.pnpm/eslint@8.57.1/node_modules/eslint/lib/eslint/index.js
  • /node_modules/.pnpm/eslint@8.57.1/node_modules/eslint/lib/cli.js
  • /node_modules/.pnpm/eslint@8.57.1/node_modules/eslint/bin/eslint.js
    at Module._resolveFilename (node:internal/modules/cjs/loader:1248:15)
    at Module._load (node:internal/modules/cjs/loader:1074:27)
    at TracingChannel.traceSync (node:diagnostics_channel:315:14)
    at wrapModuleLoad (node:internal/modules/cjs/loader:217:24)
    at Module.require (node:internal/modules/cjs/loader:1339:12)
    at require (node:internal/modules/helpers:135:16)
    at Object. (/packages/components/.eslintrc.js:1:18)
    at Module._compile (node:internal/modules/cjs/loader:1546:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1691:10)
    at Module.load (node:internal/modules/cjs/loader:1317:32)

Walkthrough

The pull request introduces significant enhancements to the AddProfileMenuItem component, transitioning it to a stateful design with modal functionality for adding profiles. It includes new props and state management while updating the GraphQL queries and mutations related to profile creation. Additionally, several styled components and TypeScript interfaces are added, improving the structure and type safety. The changes also extend to the GraphQL schema and various related components, ensuring a cohesive update across the profile management system.

Changes

File Change Summary
packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/index.tsx Refactored to a stateful component, added 'use client' directive, introduced CreateProfileModal prop, updated addNewProfileLabel, and managed modal visibility with state. The component is now wrapped in AddProfileMenuItemSuspended.
packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/styled.tsx Added a new styled component Form for layout.
packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/types.ts Introduced CreateProfileModal prop to AddProfileMenuItemProps interface.
packages/components/modules/profiles/ProfilePopover/AddProfileModal/index.tsx Added AddProfileModal component for creating organization profiles, with form management and validation.
packages/components/modules/profiles/ProfilePopover/ProfilesList/ProfileMenuItem/index.tsx Updated data fetching from readInlineData to useFragment, added null checks for profile validity, and adjusted logic for active profile determination.
packages/components/modules/profiles/ProfilePopover/ProfilesList/ProfileMenuItem/types.ts Modified ProfileMenuItemProps interface to include currentProfile property and updated import statements.
packages/components/modules/profiles/ProfilePopover/ProfilesList/index.tsx Enhanced ProfilesList component with new props, pagination handling, and loading state management.
packages/components/modules/profiles/ProfilePopover/ProfilesList/types.ts Added LoadingStateProps to ProfilesListProps interface.
packages/components/modules/profiles/graphql/mutations/OrganizationCreate.ts Introduced OrganizationCreateMutationQuery for creating organizations, along with a custom hook useOrganizationCreateMutation.
packages/components/modules/profiles/graphql/queries/ProfilesList.ts Updated ProfilesListQuery for pagination with new arguments and introduced ProfilesListFragment.
packages/components/modules/profiles/graphql/queries/UserProfile.ts Added id field to UserProfileQuery.
packages/components/package.json Updated dependencies and added new scripts for relay operations.
packages/components/schema.graphql Enhanced GraphQL schema with new types for organization handling and improved documentation comments.
packages/components/modules/messages/MessagesList/MessagesGroup/MessageItem/index.tsx Updated MessageItem to use useFragment for safer message data access.
packages/components/modules/navigations/Header/AccountMenu/AccountPopover/__tests__/AccountPopover.cy.tsx Enhanced test suite for AccountPopover with updated mock functions and assertions to reflect new data structure.
packages/components/modules/navigations/Header/AccountMenu/AccountPopover/__tests__/__mocks__/profiles.ts Modified mock functions to align with the updated profile data structure and introduced mockAddProfileData.
packages/components/modules/profiles/ProfilePopover/AddProfileModal/constants.ts Introduced constants for schema validation using zod.
packages/components/modules/profiles/ProfilePopover/AddProfileModal/styled.tsx Added a new styled component Form for layout.
packages/components/modules/profiles/ProfilePopover/AddProfileModal/types.ts Introduced interfaces for modal props and organization creation form.
packages/components/modules/profiles/graphql/queries/AddProfilePopover.ts Added AddProfilePopoverUserQuery to retrieve user permissions for adding organizations.
package.json Added resolutions field for specific dependency versions.
packages/components/modules/profiles/ProfilePopover/CurrentProfile/index.tsx Updated to use optional chaining for safer profile data access.
pnpm-workspace.yaml Updated cypress dependency version.

Possibly related PRs

Suggested reviewers

  • Hercilio1
  • deboracosilveira
  • anicioalexandre

Poem

🐰 In the meadow where profiles grow,
A menu item now steals the show.
With modals and states, oh what a sight,
Adding organizations feels just right!
Hopping through code, with joy we cheer,
For every new feature, we hold so dear! 🌼


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. (Experiment)
  • @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.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Outside diff range and nitpick comments (15)
packages/components/modules/profiles/ProfilePopover/ProfilesList/types.ts (1)

11-11: Consider adding JSDoc documentation

The addition of LoadingStateProps is well-placed and properly typed. However, consider adding JSDoc documentation to explain its purpose in the context of profile management and organization creation.

+ /** Properties for customizing the loading state display during profile operations */
  LoadingStateProps?: LoadingStateProps
packages/components/modules/profiles/graphql/queries/ProfileItem.ts (1)

Line range hint 4-13: Consider adding a type field to distinguish between user and organization profiles.

Since this fragment will be used for both user and organization profiles (as per PR objectives), adding a type field would help components differentiate between profile types and render appropriate UI elements.

 fragment ProfileItemFragment on Profile {
   id
   name
+  type
   image(width: 100, height: 100) {
     url
   }
   urlPath {
     path
   }
 }
packages/components/modules/navigations/Header/AccountMenu/AccountPopover/index.tsx (2)

44-44: Consider adding JSDoc documentation.

Adding documentation about the fragment's purpose and shape would improve maintainability.

+/** 
+ * Fragment containing profile data including image information.
+ * @see ProfileItemFragment for the complete fragment definition
+ */
 const profileData = useFragment(ProfileItemFragment, profile)

76-76: Consider adding type assertion for null profile.

While the optional chaining works correctly, being explicit about the null case would improve type safety.

-src={profileData?.image?.url ?? user?.avatar?.small}
+src={profileData?.image?.url ?? user?.avatar?.small ?? undefined}
packages/components/modules/profiles/context/CurrentProfileProvider/types.ts (1)

1-1: Excellent architectural improvement using React-Relay's data masking pattern!

The shift from ProfileItemFragment$data to ProfileItemFragment$key enforces better data handling practices by requiring explicit fragment spreads. This change improves type safety and ensures consistent data access patterns across components.

Also applies to: 4-4

packages/components/modules/profiles/graphql/queries/UserProfile.ts (1)

7-7: LGTM! Essential field addition for profile management.

The addition of the id field is crucial for the new organization profiles feature, enabling unique identification of profiles and supporting the profile switching functionality.

This change aligns with GraphQL best practices by:

  • Ensuring each profile has a unique identifier
  • Supporting efficient profile switching in the UI
  • Enabling proper relationship management between users and organizations
packages/components/modules/profiles/context/CurrentProfileProvider/__tests__/__utils__/TestComponent/index.tsx (1)

1-3: Consider simplifying the import path structure.

The deep relative import path (../../../../../graphql/queries/ProfileItem) could make the test utilities harder to maintain. Consider either:

  1. Moving the fragment closer to the test utilities
  2. Creating a dedicated test fragments file
  3. Using path aliases to simplify imports
packages/components/modules/messages/SendMessage/index.tsx (1)

79-81: Consider adding error handling for undefined profile.

While the implementation correctly uses fragments for data management, consider handling the case where profile is undefined before passing it to useFragment.

    const { profile } = useCurrentProfile()
+   if (!profile) {
+     // Handle the case where profile is undefined
+     return null // or an appropriate error state
+   }
    const currentProfile = useFragment(ProfileItemFragment, profile)
packages/components/modules/messages/MessagesGroup/index.tsx (1)

92-93: Consider optimizing fragment usage and adding error handling

While the fragment implementation is correct, consider these improvements:

  1. Optimize performance by memoizing the fragment result
  2. Add error handling for cases where fragment data might be null

Consider applying this change:

-  const currentProfileData = useFragment(ProfileItemFragment, currentProfile)
-  const isOwnMessage = currentProfileData?.id === message?.profile?.id
+  const currentProfileData = useMemo(
+    () => useFragment(ProfileItemFragment, currentProfile),
+    [currentProfile]
+  )
+  const isOwnMessage = currentProfileData?.id === message?.profile?.id || false
packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/index.tsx (1)

18-32: Consider simplifying the Stack wrapper.

The Stack component might be unnecessary since it only wraps a single MenuItem. Consider removing it unless there are specific styling requirements.

-      <Stack>
        <MenuItem
          tabIndex={0}
          component={ButtonBase}
          sx={{ justifyContent: 'space-between' }}
          onClick={() => setOpen(true)}
        >
          {addNewProfileLabel}
          <AddIcon color="action" />
        </MenuItem>
-      </Stack>
packages/components/modules/profiles/ProfilePopover/CurrentProfile/index.tsx (1)

21-23: Consider using a proper fallback image URL

Instead of falling back to an empty string, consider using a default avatar image URL. This would provide a better user experience when the profile image is not available.

-        src={profileData.image?.url ?? ''}
+        src={profileData.image?.url ?? '/assets/default-avatar.png'}
packages/components/modules/profiles/ProfilePopover/ProfilesList/index.tsx (1)

38-38: Remove debugging console.log statement

The console.log('me', me) statement appears to be used for debugging purposes. It's recommended to remove console logs in production code to keep the console output clean and avoid potential performance issues.

Apply this diff to remove the console.log statement:

-  console.log('me', me)
packages/components/modules/profiles/ProfilePopover/AddProfileModal/index.tsx (1)

31-31: Correct the grammatical error in the validation message

The error message should read "Must have at least three characters" instead of "Must have at least three character".

Apply this diff to fix the message:

-.min(3, { message: 'Must have at least three character' })
+.min(3, { message: 'Must have at least three characters' })
packages/components/modules/profiles/ProfilePopover/ProfilesList/ProfileMenuItem/index.tsx (2)

20-21: Consider adding type annotations for better clarity

The migration to useFragment is good, but consider adding explicit type annotations for better code readability:

-  const profile = useFragment(ProfileItemFragment, profileRef)
-  const currentProfileData = useFragment(ProfileItemFragment, currentProfile)
+  const profile = useFragment<ProfileItemFragment>(ProfileItemFragment, profileRef)
+  const currentProfileData = useFragment<ProfileItemFragment>(ProfileItemFragment, currentProfile)

23-25: Consider enhancing error handling with logging

While the null check is good, consider adding error logging to track invalid fragment references in production:

  if (!profile) {
+   console.warn('ProfileMenuItem: Received invalid fragment reference')
    return null // Safeguard for invalid fragment reference
  }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 913f14e and 1583d0d14f66dc6c4b6455c3ffcd6cbae8a1dfe3.

⛔ Files ignored due to path filters (7)
  • packages/components/__generated__/OrganizationCreateMutation.graphql.ts is excluded by !**/__generated__/**
  • packages/components/__generated__/ProfileItemFragment.graphql.ts is excluded by !**/__generated__/**
  • packages/components/__generated__/ProfilesListFragment.graphql.ts is excluded by !**/__generated__/**
  • packages/components/__generated__/ProfilesListQuery.graphql.ts is excluded by !**/__generated__/**
  • packages/components/__generated__/UserProfileQuery.graphql.ts is excluded by !**/__generated__/**
  • packages/components/__generated__/profilesListRefetchable.graphql.ts is excluded by !**/__generated__/**
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (23)
  • package.json (1 hunks)
  • packages/components/modules/messages/MessageItem/index.tsx (2 hunks)
  • packages/components/modules/messages/MessagesGroup/index.tsx (2 hunks)
  • packages/components/modules/messages/SendMessage/index.tsx (3 hunks)
  • packages/components/modules/navigations/Header/AccountMenu/AccountPopover/index.tsx (4 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/index.tsx (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/styled.tsx (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/types.ts (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/index.tsx (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/CurrentProfile/index.tsx (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/ProfileMenuItem/index.tsx (2 hunks)
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/ProfileMenuItem/types.ts (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/index.tsx (2 hunks)
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/types.ts (2 hunks)
  • packages/components/modules/profiles/context/CurrentProfileProvider/__tests__/__utils__/TestComponent/index.tsx (2 hunks)
  • packages/components/modules/profiles/context/CurrentProfileProvider/index.tsx (2 hunks)
  • packages/components/modules/profiles/context/CurrentProfileProvider/types.ts (1 hunks)
  • packages/components/modules/profiles/graphql/mutations/OrganizationCreate.ts (1 hunks)
  • packages/components/modules/profiles/graphql/queries/ProfileItem.ts (1 hunks)
  • packages/components/modules/profiles/graphql/queries/ProfilesList.ts (1 hunks)
  • packages/components/modules/profiles/graphql/queries/UserProfile.ts (1 hunks)
  • packages/components/package.json (2 hunks)
  • packages/components/schema.graphql (10 hunks)
✅ Files skipped from review due to trivial changes (2)
  • package.json
  • packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/styled.tsx
🔇 Additional comments (37)
packages/components/modules/profiles/ProfilePopover/ProfilesList/types.ts (1)

1-1: LGTM! Clean import from design system

The import follows the baseapp conventions and correctly imports the type from the design system package.

packages/components/modules/profiles/graphql/queries/ProfileItem.ts (1)

Line range hint 4-13: LGTM! Removal of @inline directive is a good practice.

The removal of the @inline directive allows Relay to better optimize query composition and data fetching.

packages/components/modules/navigations/Header/AccountMenu/AccountPopover/index.tsx (1)

44-44: LGTM! Good use of the useFragment hook.

The implementation correctly uses the useFragment hook for efficient data fetching, following React-Relay best practices.

packages/components/modules/profiles/context/CurrentProfileProvider/types.ts (1)

1-1: Verify complete migration to fragment keys

Let's ensure all components consuming the CurrentProfile context have been updated to handle the new $key type correctly.

Also applies to: 4-4

✅ Verification successful

Based on the results, I can now generate the final response:

Migration to fragment keys is complete and correctly implemented

All components using the CurrentProfile context have been properly updated to use useFragment with the ProfileItemFragment and $key type. No instances of ProfileItemFragment$data were found, and the fragment usage is consistent across all profile-related components.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any remaining usages of ProfileItemFragment$data 
# and verify all profile-related components are using useFragment

# Check for any remaining $data type usage
echo "Checking for remaining ProfileItemFragment\$data usage:"
rg "ProfileItemFragment\$data"

# Find components that might need updating
echo "\nChecking components that use CurrentProfile context:"
rg -l "useCurrentProfile|CurrentProfileContext"

# Look for proper useFragment implementation
echo "\nVerifying useFragment implementation in profile components:"
ast-grep --pattern 'useFragment($_, $_)'

Length of output: 3311

packages/components/modules/profiles/ProfilePopover/ProfilesList/ProfileMenuItem/types.ts (2)

13-13: LGTM! Good practice using Relay fragment references.

The change from ProfileItemFragment$data to ProfileItemFragment$key enforces proper data fetching through Relay's useFragment hook, preventing direct access to fragment data.


13-13: Verify consistent fragment reference usage.

Let's ensure this pattern is consistently applied across related components.

✅ Verification successful

Fragment reference usage is consistent across the codebase

The verification shows that ProfileItemFragment$key is consistently used across the codebase for fragment references, and ProfileItemFragment$data is used appropriately for the actual data type. All components using the fragment follow the same pattern:

  • Type imports use ProfileItemFragment$key for refs and ProfileItemFragment$data for data
  • Fragment usage with useFragment(ProfileItemFragment, profile) is consistent
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for consistent usage of ProfileItemFragment types
# Expected: All profile-related components should use $key for fragment references

# Search for ProfileItemFragment type usage
rg -t typescript "ProfileItemFragment\$(data|key)" --glob "!**/__generated__/**"

# Search for potentially inconsistent fragment handling
ast-grep --pattern 'const $_ = useFragment<$_>($_)'

Length of output: 170


Script:

#!/bin/bash
# Let's try a different approach to find fragment references

# Search for ProfileItemFragment usage without file type restriction
rg "ProfileItemFragment" --glob "!**/__generated__/**"

# Look for fragment references with $key pattern
rg "\$key" --glob "!**/__generated__/**" -A 2 -B 2

Length of output: 5512

packages/components/package.json (2)

12-13: LGTM! Script naming follows conventional standards.

The script renaming from relay-download-schema to relay:download-schema and the switch to pnpm align with standard naming conventions and the project's package manager.


38-38: Verify slugify version and consider version pinning.

The addition of slugify aligns with the PR objectives for handling organization profile URLs. However, let's verify the version and consider security implications.

Run the following script to check the latest stable version and known vulnerabilities:

Consider pinning the version to avoid potential breaking changes:

-    "slugify": "^1.6.6",
+    "slugify": "1.6.6",
✅ Verification successful

Version is up-to-date and no security concerns found. Consider version pinning for better dependency management.

The verification shows that:

  • The current version (^1.6.6) matches the latest version from NPM registry
  • No security vulnerabilities are reported for this package
  • This is the only instance of slugify usage in the codebase

While the version and security aspects are verified as safe, the suggestion to pin the version remains valid for better dependency management:

-    "slugify": "^1.6.6",
+    "slugify": "1.6.6",
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check slugify's latest version and security advisories

# Check NPM for latest versions
echo "Latest version from NPM:"
curl -s https://registry.npmjs.org/slugify | jq '.["dist-tags"].latest'

# Check for security advisories
echo "Security advisories from GitHub:"
gh api graphql -f query='
{
  securityVulnerabilities(first: 5, ecosystem: NPM, package: "slugify") {
    nodes {
      advisory {
        summary
        severity
        publishedAt
      }
      vulnerableVersionRange
      firstPatchedVersion {
        identifier
      }
    }
  }
}'

# Check usage in other packages for version consistency
echo "Usage in other package.json files:"
fd --type f "package.json" | xargs rg "slugify"

Length of output: 738

packages/components/modules/profiles/graphql/queries/UserProfile.ts (1)

8-8: Verify ProfileItemFragment fields for organization support

Ensure that the ProfileItemFragment includes all necessary fields for both user and organization profiles.

✅ Verification successful

Let me gather more information about the fragment definition by checking the generated GraphQL files.


Let me check the original fragment definition and its usage in organization-related code to ensure it supports both user and organization profiles.


ProfileItemFragment is compatible with both user and organization profiles

The fragment is correctly defined with basic profile fields (id, name, image, urlPath) that are common to both user and organization profiles. This is evidenced by:

  1. Its definition on the base Profile type, making it polymorphic
  2. Its successful usage in both user profile queries and organization mutations
  3. The fields included are fundamental profile attributes needed for both types
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify ProfileItemFragment definition and usage
# Expected: Fragment should include fields needed for organizations

# Find the fragment definition
echo "=== ProfileItemFragment Definition ==="
ast-grep --pattern 'fragment ProfileItemFragment on Profile {
  $$$
}'

# Find all fragment usages to ensure consistency
echo -e "\n=== ProfileItemFragment Usage ==="
rg -l "...ProfileItemFragment"

Length of output: 1847


Script:

#!/bin/bash
# Check the generated fragment definition
echo "=== Generated ProfileItemFragment Definition ==="
cat packages/components/__generated__/ProfileItemFragment.graphql.ts

# Check the original fragment definition
echo -e "\n=== Original Fragment Definition ==="
rg -p "fragment ProfileItemFragment" packages/components/modules/profiles/

Length of output: 2658


Script:

#!/bin/bash
# Get the complete fragment definition
echo "=== Complete Fragment Definition ==="
cat packages/components/modules/profiles/graphql/queries/ProfileItem.ts

# Check organization-related usage
echo -e "\n=== Organization Profile Usage ==="
rg -p "ProfileItemFragment" packages/components/modules/profiles/graphql/mutations/OrganizationCreate.ts

Length of output: 615

packages/components/modules/messages/MessageItem/index.tsx (2)

7-7: LGTM! Good use of GraphQL fragments for data consistency

The addition of ProfileItemFragment and its usage with useFragment hook aligns well with the organization profiles feature, ensuring consistent profile data handling across components.

Also applies to: 16-16


17-17: Verify the message ownership logic with organization profiles

The ownership check has been updated to use the fragmented profile data, which is good. However, since this PR introduces organization profiles, we should verify that this check works correctly when messages are sent from organization profiles.

✅ Verification successful

Based on the search results, I can see that the schema defines both regular profiles and organization profiles, where an Organization has a profile field of type Profile. The Message type in the schema shows that messages are associated with a profile field of type Profile, which is the same type regardless of whether it's a user profile or an organization profile.

This means the current ownership check:

const isOwnMessage = currentProfileData?.id === message?.profile?.id

is correct and will work properly for both user profiles and organization profiles since it compares the profile IDs directly, regardless of whether the profile belongs to a user or an organization.

The message ownership check is profile-type agnostic and works correctly

The implementation correctly handles ownership verification for both user and organization profiles since it compares profile IDs directly, and the schema shows that messages are always associated with a Profile type, regardless of whether that profile belongs to a user or an organization.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if there are any special cases for organization profile message ownership

# Look for organization profile related types or interfaces
ast-grep --pattern 'type Organization {
  $$$
  id: $_
  $$$
}'

# Check for any message-related mutations that might handle organization profiles
ast-grep --pattern 'mutation $_($profile: $_) {
  $$$
  message
  $$$
}'

Length of output: 140


Script:

#!/bin/bash
# Let's try a different approach to verify organization profile handling

# Check for organization profile types/interfaces in TypeScript files
rg -t typescript "interface.*Organization" -A 5

# Look for message-related types that might include organization profiles
rg -t typescript "type.*Message" -A 10

# Search for profile-related types or interfaces
rg -t typescript "interface.*Profile" -A 5

# Check message component implementations for organization handling
rg -t typescript "isOwnMessage|message\?.profile" -A 2

Length of output: 332


Script:

#!/bin/bash
# Let's try without typescript file type specification

# Check for organization profile types/interfaces
rg "interface.*Organization|type.*Organization" -A 5

# Look for message-related types that might include organization profiles
rg "type.*Message|interface.*Message" -A 10

# Search for profile-related types or interfaces
rg "interface.*Profile|type.*Profile" -A 5

# Check message component implementations for organization handling
rg "isOwnMessage.*=|message\?.profile" -A 2

Length of output: 69302

packages/components/modules/profiles/context/CurrentProfileProvider/__tests__/__utils__/TestComponent/index.tsx (2)

13-14: LGTM: Fragment implementation looks correct.

The implementation of useFragment follows React Relay best practices:

  • Correctly pairs the fragment with its source data
  • Maintains type safety through the ProfileItemFragment

13-17: Enhance test coverage for organization profiles.

Given that this PR implements organization profile features, consider extending this test component to cover organization-specific scenarios:

  1. Add test cases for organization profile data
  2. Include validation for organization-specific fields
  3. Test the owner role assignment

Let's verify if there are any organization-specific test cases:

packages/components/modules/messages/SendMessage/index.tsx (1)

9-9: LGTM! Import changes are well-structured.

The new imports for relay functionality are properly organized and align with the component's enhanced data handling approach.

Also applies to: 18-18

packages/components/modules/messages/MessagesGroup/index.tsx (2)

8-8: LGTM: Clean import additions for fragment support

The new imports properly support the transition to fragment-based data fetching.

Also applies to: 11-11


92-93: Verify fragment usage consistency across message components

Since this component is part of the messages module, we should ensure consistent fragment usage across related components.

✅ Verification successful

Fragment usage is consistent across message components

The verification shows that:

  • ProfileItemFragment is properly defined in profiles/graphql/queries/ProfileItem.ts with all necessary fields including id
  • The fragment is consistently imported and used across all message components (MessagesGroup, SendMessage, MessageItem)
  • All components use the fragment with useFragment hook in the same pattern for profile identification
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other message components using ProfileItemFragment
echo "Checking ProfileItemFragment usage in message components..."
rg -l "ProfileItemFragment" "packages/components/modules/messages"

# Verify fragment definition
echo "Verifying fragment definition..."
rg -A 10 "fragment ProfileItem on" 

Length of output: 484


Script:

#!/bin/bash
# Let's check the actual fragment usage in the found files
echo "Checking ProfileItemFragment usage in MessagesGroup..."
rg -B 2 -A 2 "ProfileItemFragment" "packages/components/modules/messages/MessagesGroup/index.tsx"

echo "\nChecking ProfileItemFragment usage in SendMessage..."
rg -B 2 -A 2 "ProfileItemFragment" "packages/components/modules/messages/SendMessage/index.tsx"

echo "\nChecking ProfileItemFragment usage in MessageItem..."
rg -B 2 -A 2 "ProfileItemFragment" "packages/components/modules/messages/MessageItem/index.tsx"

# Let's also search for the fragment definition in the entire codebase
echo "\nSearching for ProfileItemFragment definition..."
ast-grep --pattern 'fragment ProfileItemFragment on Profile {
  $$$
}'

Length of output: 2159


Script:

#!/bin/bash
# Let's find the fragment definition in profiles directory
echo "Searching for ProfileItemFragment definition in profiles..."
rg -A 10 "fragment ProfileItemFragment" "packages/components/modules/profiles/"

# Let's also check if the fragment is used consistently for profile ID comparison
echo "\nChecking profile ID usage pattern..."
ast-grep --pattern 'const $_ = useFragment(ProfileItemFragment, $_)
  const isOwnMessage = $_.id === $$$'

Length of output: 1301

packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/index.tsx (3)

1-9: LGTM! Well-organized imports and component setup.

The imports are properly organized, and the component is correctly set up as a client-side component.


16-16: LGTM! Clean state management implementation.

The modal visibility state is managed appropriately using the useState hook.


12-15: Verify the AddProfileMenuItemProps interface implementation.

The component allows for customization through props, but let's verify the complete interface implementation.

✅ Verification successful

Props interface is correctly implemented and aligned with component usage

The interface AddProfileMenuItemProps in types.ts correctly defines both optional props:

  • addNewProfileLabel?: string
  • CreateProfileModal?: FC<AddProfileModalProps>

The default values in the component match the interface, and the AddProfileModalProps interface shows proper type definitions for the modal component, including the shared addNewProfileLabel prop.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the AddProfileMenuItemProps interface definition
# Expected: Interface should define addNewProfileLabel and CreateProfileModal props

# Search for the interface definition
ast-grep --pattern 'interface AddProfileMenuItemProps {
  $$$
}'

# Search for any usage of these props in tests
rg -l "AddProfileMenuItemProps|addNewProfileLabel|CreateProfileModal" --type ts --type tsx

Length of output: 638


Script:

#!/bin/bash
# Search for interface and its usage with proper file type patterns
rg -l "AddProfileMenuItemProps|addNewProfileLabel|CreateProfileModal" --type typescript --type tsx

# Check the AddProfileModal import and its props
ast-grep --pattern 'import { $$ AddProfileModal $$ } from'

# Search for AddProfileModalProps definition
ast-grep --pattern 'interface AddProfileModalProps {
  $$$
}'

Length of output: 1227

packages/components/modules/profiles/ProfilePopover/CurrentProfile/index.tsx (4)

6-6: LGTM: Clean import additions for GraphQL fragment support

The new imports are properly organized and necessary for implementing the GraphQL fragment pattern.

Also applies to: 9-9


15-16: LGTM: Proper null handling

The null check ensures type safety and prevents rendering with invalid data.


12-14: Verify fragment implementation across the codebase

The implementation of useFragment is correct and follows best practices. However, let's verify that the ProfileItemFragment is consistently used across other profile-related components.

✅ Verification successful

Fragment implementation is consistent across the codebase

The ProfileItemFragment is properly implemented and consistently used across all profile-related components. The verification shows:

  • The fragment is correctly defined in ProfileItem.ts
  • All components using profile data properly consume it through useFragment hook
  • No direct access to profile properties without using fragments was found
  • The fragment is used consistently in:
    • Profile components (CurrentProfile, ProfilesList, ProfileMenuItem)
    • Message components (MessageItem, SendMessage, MessagesGroup)
    • Navigation components (AccountPopover)
    • GraphQL operations (queries and mutations)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for consistent usage of ProfileItemFragment across the codebase

# Search for ProfileItemFragment usage
echo "Checking ProfileItemFragment usage patterns:"
rg -A 2 "ProfileItemFragment" --type tsx

# Search for potential inconsistent profile data access
echo "\nChecking for potential direct profile access that should use fragments:"
rg -A 2 "useCurrentProfile.*profile\." --type tsx

Length of output: 404


Script:

#!/bin/bash
# Let's try with typescript files instead of tsx extension

# Search for ProfileItemFragment usage
echo "Checking ProfileItemFragment usage patterns:"
rg -A 2 "ProfileItemFragment"

# Search for potential inconsistent profile data access
echo -e "\nChecking for potential direct profile access that should use fragments:"
rg -A 2 "useCurrentProfile.*profile\."

Length of output: 18211


9-10: Verify organization profile support in the fragment

Since this component will display both user and organization profiles, we should verify that the ProfileItemFragment includes all necessary fields for organization profiles.

✅ Verification successful

ProfileItemFragment is sufficient for both user and organization profiles

Looking at the schema and fragment definitions, I can confirm that:

  1. The Profile type is used for both user and organization profiles (as seen in Organization type having a profile: Profile field)
  2. The ProfileItemFragment includes all essential fields (id, name, image, urlPath) that are common and necessary for displaying both user and organization profiles
  3. The fragment is already being used successfully in OrganizationCreateMutation, indicating it's compatible with organization profiles
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if ProfileItemFragment includes organization-specific fields

# Search for the fragment definition
echo "Checking ProfileItemFragment definition:"
rg -A 10 "fragment ProfileItem" --type graphql

# Search for organization-related types in schema
echo "\nChecking organization-related types:"
rg -A 5 "type Organization" --type graphql

Length of output: 335


Script:

#!/bin/bash
# Let's try searching without file type restriction since GraphQL files might have different extensions
echo "Checking ProfileItemFragment definition:"
rg -A 10 "fragment ProfileItem"

echo -e "\nChecking files containing GraphQL fragments:"
fd -e graphql -e gql

echo -e "\nChecking organization-related types:"
rg -A 5 "type Organization"

Length of output: 8890


Script:

#!/bin/bash
# Let's check the Profile type definition to understand if it's a union or interface type that can represent both user and organization profiles
echo "Checking Profile type definition:"
rg -A 10 "type Profile" packages/components/schema.graphql

# Also check if there are any discriminator fields in the Profile type
echo -e "\nChecking Profile-related interfaces or unions:"
rg "(interface|union).*Profile" packages/components/schema.graphql

Length of output: 2477

packages/components/modules/profiles/context/CurrentProfileProvider/index.tsx (2)

8-8: Verify data consistency after removing readInlineData

The removal of readInlineData in favor of direct data access might impact type safety and performance. Consider using useFragment within the React components that consume this data to maintain consistent data access patterns.

Let's check for other components that might still be using readInlineData:

✅ Verification successful

Removal of readInlineData is safe and consistent with codebase patterns

The codebase shows consistent usage of useFragment with ProfileItemFragment across multiple components, and there are no instances of readInlineData being used. The current implementation aligns with the established pattern of using fragments for type-safe data access.

Key observations:

  • Multiple components correctly use useFragment(ProfileItemFragment, profile) pattern
  • No existing usage of readInlineData found in the codebase
  • Fragment-based data access is consistently implemented across profile-related components
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any remaining readInlineData usage that might need updating
rg "readInlineData" --type ts --type tsx

# Search for components using the profile fragment to ensure consistent pattern
ast-grep --pattern 'useFragment($_, $_ProfileFragment)'

Length of output: 126


Script:

#!/bin/bash
# Search for readInlineData usage with proper file type
rg "readInlineData" -t typescript -t tsx

# Search for profile-related fragments and data access patterns
rg "useFragment.*Profile" -t typescript -t tsx

# Look for other components accessing profile data
rg "ProfileFragment" -t typescript -t tsx

Length of output: 236


Script:

#!/bin/bash
# Search for readInlineData usage without file type filters
rg "readInlineData"

# Search for profile-related fragments and data access patterns
rg "useFragment.*Profile"

# Look for other components accessing profile data
rg "ProfileFragment"

# Find all TypeScript/TSX files that might be related to profiles
fd -e ts -e tsx | grep -i profile

Length of output: 4350


Line range hint 41-52: Update state persistence for organization profiles

The current state persistence configuration might need updates to handle organization profiles effectively. Consider:

  1. Adding organization-specific fields to the persisted state
  2. Implementing validation for organization membership
  3. Adding state cleanup for organization profile switching

Let's verify the current state structure and usage:

packages/components/modules/profiles/graphql/queries/ProfilesList.ts (2)

4-10: Addition of Pagination Parameters to ProfilesListQuery

The query ProfilesListQuery now correctly includes $count and $cursor variables for pagination, enhancing data retrieval capabilities.


12-27: Implementation of ProfilesListFragment with Refetchable Pagination

The ProfilesListFragment is properly set up with @argumentDefinitions and the @refetchable directive, enabling efficient pagination and data refetching.

packages/components/modules/profiles/ProfilePopover/AddProfileModal/index.tsx (1)

95-122: Verify the connection key in Relay store updater

In the updater function, the ConnectionHandler.getConnection uses the key 'ProfilesListFragment_profiles'. Ensure that this key matches the one used in the @connection directive in the fragment to properly update the Relay store.

Run the following script to check the @connection key usage:

✅ Verification successful

Connection key in Relay store updater is correctly configured

The connection key 'ProfilesListFragment_profiles' used in the ConnectionHandler.getConnection matches exactly with the @connection directive's key in the fragment definition, ensuring proper Relay store updates.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that the connection key in the updater matches the fragment's connection key

# Find the @connection directive in the fragment
rg -A2 '@connection\(key:\s*"ProfilesListFragment_profiles"' packages/components/modules/profiles/graphql/queries/ProfilesList.ts

Length of output: 256

packages/components/schema.graphql (4)

755-761: Addition of Organization Type

The Organization type is correctly defined, implementing Node and including necessary fields such as id, pk, and profile.


763-769: Definition of OrganizationCreateInput and Payload

The OrganizationCreateInput and OrganizationCreatePayload are properly defined to support organization creation through GraphQL mutations.


1044-1052: Addition of ProfileRoles Enumeration

The ProfileRoles enum is appropriately added, defining possible roles within an organization.


1065-1069: Implementation of ProfilesInterface for Pagination

The ProfilesInterface defines paginated access to profiles, enhancing scalability and performance.

packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/types.ts (1)

3-7: Addition of CreateProfileModal Prop to AddProfileMenuItemProps

The optional CreateProfileModal property is correctly added to the AddProfileMenuItemProps interface, allowing customization of the profile creation modal component.

packages/components/modules/profiles/graphql/mutations/OrganizationCreate.ts (1)

1-54: Introduction of OrganizationCreateMutation and Hook

The OrganizationCreateMutation is well-defined, and the useOrganizationCreateMutation hook correctly handles mutation execution with proper error handling and user notifications.

packages/components/modules/profiles/ProfilePopover/ProfilesList/ProfileMenuItem/index.tsx (3)

6-6: LGTM: Good migration to useFragment

The switch from readInlineData to useFragment follows React-Relay best practices, enabling better compile-time optimizations and runtime performance.


29-29: LGTM: Clean active profile detection

The comparison logic correctly uses the fragmented data and maintains type safety.


Line range hint 13-19: Verify organization profile support

Let's verify that the component properly handles organization profiles as mentioned in the PR objectives:

✅ Verification successful

Based on the shell script results, I can now generate the final response:

Profile component properly handles all profile types

The component is already designed to handle all profile types generically through the ProfileItemFragment interface, which includes common fields like id, name, image.url, and urlPath.path. The implementation is type-safe and doesn't need special handling for organization profiles as it:

  • Uses a generic profile interface that works for both user and organization profiles
  • Renders profile information consistently regardless of the profile type
  • Handles profile switching through a type-safe onProfileChange callback
  • Includes proper null checks and type safety measures
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if there are any organization-specific fields or types in the GraphQL fragment
ast-grep --pattern 'fragment ProfileItemFragment on Profile {
  $$$
}'

# Look for any organization-related tests
rg -l "organization.*profile" --type=test

Length of output: 145


Script:

#!/bin/bash
# Look for Profile type definitions and interfaces
ast-grep --pattern 'type Profile = {
  $$$
}'

# Look for organization-related tests with correct file extensions
rg -l "organization.*profile" --type=ts --type=tsx

# Check for Profile-related type definitions
rg "type.*Profile.*=" --type=ts --type=tsx -A 5

# Look for organization-specific handling in the component
rg "organization" packages/components/modules/profiles/ProfilePopover/ProfilesList/ProfileMenuItem/ -A 3

Length of output: 311


Script:

#!/bin/bash
# Look for Profile type definitions and interfaces without file type restrictions
rg "type.*Profile.*=" -A 5

# Look for organization-related code in the ProfileMenuItem directory
fd . packages/components/modules/profiles/ProfilePopover/ProfilesList/ProfileMenuItem --type f --exec cat {}

# Look for Profile-related interfaces and types
rg "interface.*Profile" -A 5

# Search for organization-related code in the profiles module
rg "organization.*profile" packages/components/modules/profiles/

Length of output: 12687

Comment on lines +21 to +35
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Add ARIA attributes for improved accessibility

The menu item that triggers the modal should include the following accessibility attributes:

  • aria-haspopup="dialog" to indicate it opens a modal
  • aria-expanded={open} to reflect the modal's state
  • aria-label to provide a clear description of the action

Example locations requiring updates:

  • packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/index.tsx: Add ARIA attributes to the MenuItem component that opens the modal.

This recommendation is based on established patterns found in the codebase, where other interactive elements consistently implement ARIA attributes for accessibility, as seen in the ProfilesList and NotificationsPopover components.

🔗 Analysis chain

Verify accessibility implementation.

While the component includes tabIndex, let's ensure it meets all accessibility requirements for a menu item that opens a modal.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for accessibility-related attributes in similar components
# Expected: Find patterns of accessible modal triggers

# Search for aria-label or aria-labelledby usage with modals
rg -A 3 "aria-label|aria-labelledby" --type tsx

# Search for similar modal trigger patterns
ast-grep --pattern 'MenuItem {
  $$$
  onClick={$_}
  $$$
}'

Length of output: 140


Script:

#!/bin/bash
# Let's try different approaches to find accessibility patterns

# Search for aria attributes in all files
rg -A 3 "aria-label|aria-labelledby|aria-expanded|aria-haspopup"

# Search for MenuItem components with accessibility attributes
rg -A 5 "MenuItem.*aria-"

# Look for modal-related accessibility implementations
rg -A 5 "Modal.*aria-"

# Search for similar components that might show accessibility patterns
rg -A 5 "onClick.*setOpen.*true"

Length of output: 8680

Comment on lines 27 to 38
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider organization profile handling in data fetching

Given that this PR implements organization profiles, the current implementation might need enhancement to handle both user and organization profiles effectively. Consider:

  1. Adding type discrimination for different profile types
  2. Implementing proper error handling for organization-specific cases
  3. Adding comments about the expected data structure for organization profiles

Consider refactoring the profile fetching logic to handle both profile types:

 const fetchUserProfile = async (environment: Environment) => {
   const data = await fetchQuery<UserProfileQueryType>(
     environment,
     UserProfileQuery,
     {},
     { fetchPolicy: 'store-or-network' },
   ).toPromise()
 
   // useFragment is a React hook and can't be used outside of a React component
   // We need to use the fragment node directly since this is in an async function
-  const userProfile = data?.me?.profile
+  const profile = data?.me?.profile
+  if (!profile) return null
+
+  // Add type discrimination for different profile types
+  if (profile.__typename === 'OrganizationProfile') {
+    // Handle organization profile specific logic
+  }
 
-  return userProfile
+  return profile
 }

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

@priscilladeroode priscilladeroode force-pushed the BA-1832-fe-multiple-profiles-create-organization-profile branch from 1583d0d to 97f1749 Compare December 2, 2024 12:50
@changeset-bot
Copy link

changeset-bot bot commented Dec 2, 2024

⚠️ No Changeset found

Latest commit: b7f1edc

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🧹 Outside diff range and nitpick comments (13)
packages/components/modules/messages/MessagesList/index.tsx (2)

36-36: Good use of Relay fragments for type-safe data access

Using useFragment with ProfileItemFragment ensures type-safe access to profile data and enables Relay's data masking and colocation features. This approach is more maintainable than using raw props.

This pattern helps with:

  • Type safety through generated types
  • Optimized data fetching
  • Better component isolation

Line range hint 124-125: Consider creating an issue for the overscan TODO

The TODO comment indicates a potential performance issue with overscan causing stack overflow. This should be tracked and investigated.

Would you like me to create a GitHub issue to track this performance optimization task? I can help outline the problem and potential solutions.

packages/components/modules/messages/graphql/subscriptions/useRoomListSubscription.tsx (1)

37-37: Clarify and track the subscription verification TODO

The TODO comment lacks specificity about what aspects of the backend subscription need verification. This should be tracked properly to ensure reliability.

Would you like me to:

  1. Create a GitHub issue to track this verification task?
  2. Help generate test cases to verify the subscription functionality?
packages/components/modules/profiles/context/CurrentProfileProvider/index.tsx (1)

Line range hint 31-93: Enhance state management for organization profiles

The current state management implementation needs to be updated to properly handle organization profiles, especially in the context of profile switching and validation.

Consider these enhancements:

  1. Update the UseCurrentProfile type to include organization-specific fields:
interface UseCurrentProfile {
  profile: UserProfile | OrganizationProfile | null;
  userId: string | null;
  profileType: 'user' | 'organization' | null;
  setCurrentProfile: (state: Partial<UseCurrentProfile>) => void;
}
  1. Update the initial state constant:
export const INITIAL_CURRENT_PROFILE_STATE: UseCurrentProfile = {
  profile: null,
  userId: null,
  profileType: null,
  setCurrentProfile: () => {},
};
  1. Modify the state updates to include profile type:
- storeRef.current?.setState({ profile: userProfile, userId: user.id })
+ storeRef.current?.setState({ 
+   profile: userProfile,
+   userId: user.id,
+   profileType: userProfile.__typename === 'OrganizationProfile' ? 'organization' : 'user'
+ })
packages/components/modules/messages/ChatRoomsList/ChatRoomItem/index.tsx (2)

35-35: Consider memoizing participant filtering logic

The participant filtering logic could be memoized to prevent unnecessary recalculations on re-renders.

+const useOtherParticipant = (room, profileId) => {
+  return useMemo(() => 
+    room.participants?.edges.find(
+      (edge) => edge?.node?.profile?.id && edge?.node?.profile?.id !== profileId
+    ),
+    [room.participants?.edges, profileId]
+  );
+};

-const otherParticipant = room.participants.edges.find(
-  (edge) => edge?.node?.profile?.id && edge?.node?.profile?.id !== profileData?.id,
-);
+const otherParticipant = useOtherParticipant(room, profileData?.id);

Also applies to: 47-47


Line range hint 1-124: Consider splitting component for better maintainability

The component handles multiple responsibilities (rendering, actions, state management). Consider splitting it into smaller, focused components:

  • ChatRoomHeader (title and avatar)
  • ChatRoomContent (messages and time)
  • ChatRoomActions (archive, mark as unread)
packages/components/modules/messages/MessagesList/MessagesGroup/index.tsx (1)

126-126: Consider memoizing isOwnMessage calculation

Since isOwnMessage is used in multiple places and depends on props/state that don't change frequently, consider memoizing it with useMemo to prevent unnecessary recalculations.

Here's how you could optimize it:

-  const isOwnMessage = currentProfileData?.id === message?.profile?.id
+  const isOwnMessage = useMemo(
+    () => currentProfileData?.id === message?.profile?.id,
+    [currentProfileData?.id, message?.profile?.id]
+  )
packages/components/modules/profiles/graphql/mutations/OrganizationCreate.ts (1)

37-51: Enhance mutation reliability with input validation and retry mechanism

Consider these improvements:

  1. Add input validation before mutation execution
  2. Implement retry logic for network failures
  3. Add error type narrowing for better error handling
 const commit = (config: UseMutationConfig<OrganizationCreateMutation>) =>
+  // Add input validation
+  if (!config.variables?.input?.name || !config.variables?.input?.urlPath) {
+    throw new Error('Invalid input: name and urlPath are required')
+  }
   commitMutation({
     ...config,
+    // Add retry logic
+    retry: (error) => {
+      return error.name === 'NetworkError' && retryCount < 3
+    },
     onCompleted: (response, errors) => {
       errors?.forEach((error) => {
         sendToast(error.message, { type: 'error' })
       })
       config?.onCompleted?.(response, errors)
     },
     onError: (error) => {
+      // Narrow error types
+      if (error instanceof NetworkError) {
+        sendToast('Network error. Please try again.', { type: 'error' })
+      } else {
         sendToast(error.message, { type: 'error' })
+      }
       config?.onError?.(error)
     },
   })
packages/components/modules/profiles/ProfilePopover/ProfilesList/ProfileMenuItem/index.tsx (1)

20-25: Add loading state handling

The component handles null data but should also handle loading states for better UX.

 const profile = useFragment(ProfileItemFragment, profileRef)
 const currentProfileData = useFragment(ProfileItemFragment, currentProfile)

+const isLoading = !profileRef || !currentProfile
+if (isLoading) {
+  return <StyledMenuItem disabled><Skeleton variant="rectangular" width="100%" height={36} /></StyledMenuItem>
+}
 if (!profile) {
   return null // Safeguard for invalid fragment reference
 }
packages/components/modules/profiles/ProfilePopover/ProfilesList/index.tsx (2)

41-42: Consider adding error boundaries for data fetching

While the implementation of fragments and pagination is solid, consider wrapping the data fetching logic in error boundaries to gracefully handle potential failures.

+import { ErrorBoundary } from 'react-error-boundary'
+
+const ProfilesListErrorFallback = () => (
+  <Box sx={{ p: 2, textAlign: 'center' }}>
+    <Typography color="error">Failed to load profiles</Typography>
+  </Box>
+)

 const ProfilesList: FC<ProfilesListProps> = ({
   // ...props
 }) => {
+  return (
+    <ErrorBoundary FallbackComponent={ProfilesListErrorFallback}>
       {/* existing component content */}
+    </ErrorBoundary>
+  )
 }

Also applies to: 56-64


102-114: Enhance accessibility for virtualized list

While the Virtuoso implementation is efficient, consider adding ARIA attributes to improve accessibility.

 <Virtuoso
   data={profiles}
   style={{ height: 'auto', minHeight }}
+  tabIndex={0}
+  role="list"
+  aria-label="List of profiles"
   itemContent={(index, profile) => renderProfileItem(profile, index)}
   components={{
     Footer: renderVirtuosoLoadingState,
   }}
   endReached={() => {
     if (hasNext) {
       loadNext(10)
     }
   }}
 />
packages/components/schema.graphql (2)

1029-1054: Add descriptive documentation for Organization types

The Organization-related types would benefit from more detailed documentation.

+"""
+Represents an organization profile that can have multiple members and manage social activities.
+"""
 type Organization implements Node {
+  """
+  The associated profile for this organization
+  """
   profile: Profile

   """
   The ID of the object
   """
   id: ID!
   pk: Int!
 }

+"""
+Input for creating a new organization profile
+"""
 input OrganizationCreateInput {
+  """
+  The display name of the organization
+  """
   name: String!
+  """
+  The unique URL path for the organization's profile
+  """
   urlPath: String
   clientMutationId: String
 }

1464-1477: Consider reorganizing Profile and User interfaces

The Profile and User types have grown large and could benefit from better organization using interfaces.

Consider splitting the interfaces into more focused ones:

  • AuthenticatedEntity for authentication-related fields
  • NotifiableEntity for notification-related fields
  • SocialEntity for social interaction fields
  • OrganizationalEntity for organization-related fields

This would make the schema more maintainable and easier to understand.

Also applies to: 1952-2044

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 1583d0d14f66dc6c4b6455c3ffcd6cbae8a1dfe3 and 97f1749d3850bad6058396e8d3719503198b39cc.

⛔ Files ignored due to path filters (6)
  • packages/components/__generated__/OrganizationCreateMutation.graphql.ts is excluded by !**/__generated__/**
  • packages/components/__generated__/ProfilesListFragment.graphql.ts is excluded by !**/__generated__/**
  • packages/components/__generated__/ProfilesListQuery.graphql.ts is excluded by !**/__generated__/**
  • packages/components/__generated__/UserProfileQuery.graphql.ts is excluded by !**/__generated__/**
  • packages/components/__generated__/profilesListRefetchable.graphql.ts is excluded by !**/__generated__/**
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (26)
  • packages/components/modules/messages/ChatRoomsList/ChatRoomItem/index.tsx (3 hunks)
  • packages/components/modules/messages/CreateChatRoomList/ChatRoomListItem/index.tsx (2 hunks)
  • packages/components/modules/messages/MessagesList/MessagesGroup/MessageItem/index.tsx (2 hunks)
  • packages/components/modules/messages/MessagesList/MessagesGroup/index.tsx (4 hunks)
  • packages/components/modules/messages/MessagesList/index.tsx (3 hunks)
  • packages/components/modules/messages/SendMessage/index.tsx (3 hunks)
  • packages/components/modules/messages/graphql/subscriptions/useMessageCountUpdateSubscription.tsx (2 hunks)
  • packages/components/modules/messages/graphql/subscriptions/useRoomListSubscription.tsx (2 hunks)
  • packages/components/modules/navigations/Header/AccountMenu/AccountPopover/index.tsx (4 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/index.tsx (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/styled.tsx (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/types.ts (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/index.tsx (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/CurrentProfile/index.tsx (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/ProfileMenuItem/index.tsx (2 hunks)
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/ProfileMenuItem/types.ts (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/index.tsx (2 hunks)
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/types.ts (2 hunks)
  • packages/components/modules/profiles/context/CurrentProfileProvider/__tests__/__utils__/TestComponent/index.tsx (2 hunks)
  • packages/components/modules/profiles/context/CurrentProfileProvider/index.tsx (2 hunks)
  • packages/components/modules/profiles/context/CurrentProfileProvider/types.ts (1 hunks)
  • packages/components/modules/profiles/graphql/mutations/OrganizationCreate.ts (1 hunks)
  • packages/components/modules/profiles/graphql/queries/ProfilesList.ts (1 hunks)
  • packages/components/modules/profiles/graphql/queries/UserProfile.ts (1 hunks)
  • packages/components/package.json (1 hunks)
  • packages/components/schema.graphql (60 hunks)
✅ Files skipped from review due to trivial changes (1)
  • packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/styled.tsx
🚧 Files skipped from review as they are similar to previous changes (11)
  • packages/components/modules/profiles/context/CurrentProfileProvider/tests/utils/TestComponent/index.tsx
  • packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/types.ts
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/types.ts
  • packages/components/package.json
  • packages/components/modules/profiles/context/CurrentProfileProvider/types.ts
  • packages/components/modules/navigations/Header/AccountMenu/AccountPopover/index.tsx
  • packages/components/modules/profiles/graphql/queries/UserProfile.ts
  • packages/components/modules/messages/SendMessage/index.tsx
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/ProfileMenuItem/types.ts
  • packages/components/modules/profiles/graphql/queries/ProfilesList.ts
  • packages/components/modules/profiles/ProfilePopover/CurrentProfile/index.tsx
🔇 Additional comments (18)
packages/components/modules/messages/MessagesList/MessagesGroup/MessageItem/index.tsx (2)

7-7: LGTM: Clean import addition

The ProfileItemFragment import is properly structured and necessary for the fragment-based profile data handling.


16-17: Consider adding null safety checks

While the current implementation uses optional chaining, it might benefit from more explicit null handling:

- const isOwnMessage = currentProfileData?.id === message?.profile?.id
+ const isOwnMessage = Boolean(currentProfileData?.id && message?.profile?.id && currentProfileData.id === message.profile.id)

Let's verify the fragment implementation:

✅ Verification successful

Let me verify the fragment implementation further by checking its definition.


Null safety suggestion is good but fragment implementation is correct

The fragment is properly defined with required fields and is consistently used across the codebase. While the suggested null safety improvement is beneficial, the current implementation with optional chaining is acceptable as:

  1. The fragment is well-defined with required fields including id
  2. The pattern of null checking is consistently applied across other components using this fragment
  3. Several components already implement explicit null checks (e.g., if (!profileData) return null)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify ProfileItemFragment implementation and usage

# Check if the fragment is properly defined
ast-grep --pattern 'export const ProfileItemFragment = graphql`
  fragment ProfileItem_profile on Profile {
    $$$
    id
    $$$
  }
`'

# Check for consistent fragment usage pattern across the codebase
rg -A 2 'useFragment\(ProfileItemFragment,'

Length of output: 5421


Script:

#!/bin/bash
# Search for ProfileItemFragment definition
rg -A 10 'export const ProfileItemFragment = graphql`'

Length of output: 1079

packages/components/modules/messages/MessagesList/index.tsx (1)

6-6: LGTM: Import changes align with new fragment usage

The addition of useFragment and ProfileItemFragment imports properly support the new fragment-based data fetching approach.

Also applies to: 12-12

packages/components/modules/messages/graphql/subscriptions/useRoomListSubscription.tsx (2)

3-3: LGTM: Import changes align with fragment-based approach

The addition of useFragment and ProfileItemFragment imports properly supports the transition to fragment-based data handling.

Also applies to: 7-7


Line range hint 9-34: Verify subscription compatibility with organization profiles

Given that this PR implements organization profiles, we should verify if the subscription GraphQL query needs to be updated to handle messages in the context of organization profiles.

✅ Verification successful

Let me gather more information about the profile types and organization profiles.


Based on the gathered information, I can now generate the final response:

No subscription changes needed for organization profiles

The subscription is already profile-agnostic as it:

  • Uses a generic profileId parameter that works for any profile type
  • Returns profile data through a generic profile interface that includes common fields (id, name, image)
  • Shows no special handling for different profile types in any of the related subscriptions

The current implementation will work correctly with organization profiles without modifications, as the GraphQL schema appears to handle profiles uniformly through a common interface.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any profile type definitions or interfaces that might need updating
ast-grep --pattern 'type Profile {
  $$$
}'

# Search for other subscription usages that might need similar updates
rg -A 5 'subscription.*profileId.*'

Length of output: 4767


Script:

#!/bin/bash
# Search for organization profile related types and interfaces
ast-grep --pattern 'type Organization'

# Search for profile interfaces
ast-grep --pattern 'interface Profile'

# Look for any organization-related fragments or queries
rg -A 5 'fragment.*Organization|query.*organization'

# Check if there are any organization-specific message handlers
rg -A 5 'organization.*message|message.*organization'

Length of output: 1189

packages/components/modules/profiles/context/CurrentProfileProvider/index.tsx (3)

8-8: LGTM: Import changes look good

The updated relay imports provide the necessary functionality for GraphQL operations.


27-30: ⚠️ Potential issue

Consider organization profile handling in data fetching

Given that this PR implements organization profiles, the current implementation might need enhancement to handle both user and organization profiles effectively.

Consider refactoring the profile fetching logic to handle both profile types:

const fetchUserProfile = async (environment: Environment) => {
  const data = await fetchQuery<UserProfileQueryType>(
    environment,
    UserProfileQuery,
    {},
    { fetchPolicy: 'store-or-network' },
  ).toPromise()

  // useFragment is a React hook and can't be used outside of a React component
  // We need to use the fragment node directly since this is in an async function
-  const userProfile = data?.me?.profile
+  const profile = data?.me?.profile
+  if (!profile) return null
+
+  // Add type discrimination for different profile types
+  const isOrgProfile = profile.__typename === 'OrganizationProfile'
+  const isUserProfile = profile.__typename === 'UserProfile'
+
+  if (!isOrgProfile && !isUserProfile) {
+    console.warn(`Unknown profile type: ${profile.__typename}`)
+    return null
+  }
+
+  return profile
}

Line range hint 1-93: Verify profile type handling across the application

Let's verify how profile types are handled across the codebase to ensure consistent implementation.

✅ Verification successful

Profile type handling is consistent and well-structured

The profile type handling across the application follows a consistent pattern:

  • Uses GraphQL fragments (ProfileItemFragment) as the source of truth for profile data structure
  • Maintains type safety through generated types (ProfileItemFragment$key)
  • Implements clean state management with well-defined interfaces (CurrentProfileState)
  • Properly handles profile data fetching and storage with appropriate type constraints
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for profile type handling across the codebase

# Check for profile type definitions
echo "Checking for profile type definitions..."
rg -A 5 "type.*Profile.*=|interface.*Profile.*{|enum.*Profile.*{" --type typescript

# Check for profile type discriminations
echo "Checking for profile type discriminations..."
rg "__typename.*Profile" --type typescript

# Check for profile-related GraphQL fragments
echo "Checking for profile-related GraphQL fragments..."
fd "\.graphql$" -x cat {} | rg "fragment.*on.*Profile"

Length of output: 699


Script:

#!/bin/bash
# Let's try a different approach to check profile-related code

# Check for Profile type definitions and usages
echo "Checking Profile types and interfaces..."
rg "type\s+.*Profile|interface\s+.*Profile" -g "*.ts*"

# Check profile-related GraphQL queries and fragments
echo "Checking GraphQL queries and fragments..."
rg "fragment|query" -g "UserProfile*.{ts,tsx,graphql}"

# Check for profile state management
echo "Checking profile state management..."
rg "profile" -g "*CurrentProfile*.{ts,tsx}"

# Check for profile-related components
echo "Checking profile components..."
fd "Profile" -e tsx -e ts

Length of output: 8898


Script:

#!/bin/bash
# Let's examine the actual implementation details

# Check the profile type implementation
echo "Checking ProfileItemFragment implementation..."
ast-grep --pattern 'fragment ProfileItemFragment on Profile {
  $$$
}'

# Check CurrentProfile state implementation
echo "Checking CurrentProfile state..."
cat packages/components/modules/profiles/context/CurrentProfileProvider/types.ts
cat packages/components/modules/profiles/context/CurrentProfileProvider/constants.ts

# Check profile-related GraphQL query
echo "Checking UserProfile query..."
cat packages/components/modules/profiles/graphql/queries/UserProfile.ts

Length of output: 1365

packages/components/modules/messages/graphql/subscriptions/useMessageCountUpdateSubscription.tsx (2)

3-3: LGTM: Fragment migration improves data consistency

The migration to using useFragment with ProfileItemFragment aligns with Relay's best practices for data consistency and type safety.

Also applies to: 6-6, 32-32


39-39: Verify null safety handling

While the optional chaining on profileData?.id handles the null case, consider adding error handling for when the subscription is attempted without a valid profile.

Consider adding error handling:

 const config = useMemo(
   () => ({
     subscription: MessageCountUpdateSubscription,
-    onError: console.error,
+    onError: (error) => {
+      console.error('Message count subscription error:', error)
+      // Add error reporting/handling here
+    },
     variables: {
       profileId: profileData?.id,
     },
   }),
   [profileData?.id],
 )

Also applies to: 42-42

packages/components/modules/messages/MessagesList/MessagesGroup/index.tsx (2)

8-8: LGTM: Clean import additions for relay fragment support

The new imports are properly organized and necessary for implementing the fragment-based data fetching pattern.

Also applies to: 11-11


33-33: LGTM: Proper implementation of useFragment hook

The fragment usage follows React Relay best practices and provides type-safe access to profile data.

Let's verify the fragment definition and its usage across the codebase:

✅ Verification successful

LGTM: Consistent usage of ProfileItemFragment across the codebase

The verification shows that the useFragment hook with ProfileItemFragment is consistently used across multiple components in the codebase, particularly within the messages and profiles modules. The implementation in MessagesList/MessagesGroup/index.tsx follows the same pattern as other components, maintaining consistency in how profile data is accessed.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify ProfileItemFragment definition and usage
# Check fragment definition
ast-grep --pattern 'export const ProfileItemFragment = graphql`
  fragment ProfileItem_profile on Profile {
    $$$
  }
`'

# Check consistent fragment usage
rg "useFragment\(ProfileItemFragment" -A 2

Length of output: 5405

packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/index.tsx (2)

21-29: Add ARIA attributes for improved accessibility

The menu item should include accessibility attributes for better screen reader support:

  • aria-haspopup="dialog"
  • aria-expanded={open}

13-15: Verify type safety of CreateProfileModal prop

The component allows customization of the modal through the CreateProfileModal prop, but we should verify that any custom implementation will maintain type safety with the required props (open and setOpen).

✅ Verification successful

Type safety of CreateProfileModal prop is properly enforced

The implementation is type-safe because:

  • CreateProfileModal prop is correctly typed as FC<AddProfileModalProps> in the AddProfileMenuItemProps interface
  • AddProfileModalProps interface properly defines the required open and setOpen props
  • The default AddProfileModal component implements this interface correctly
  • Any custom implementation must satisfy the same interface to compile successfully
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for CreateProfileModal type definition and usages
ast-grep --pattern 'interface $_Props {
  $$$
  CreateProfileModal?: $_
  $$$
}'

Length of output: 82


Script:

#!/bin/bash
# Let's try to find the type definition and usage in a different way
ast-grep --pattern 'type $_Props = {
  $$$
  CreateProfileModal?: $_
  $$$
}'

# Also search for the actual component definition to see how the props are typed
ast-grep --pattern 'const AddProfileMenuItem: FC<$_> = ({
  $$$
  CreateProfileModal = $_,
  $$$
}) => {'

# Let's also check for AddProfileModal since it's the default value
ast-grep --pattern 'const AddProfileModal: FC<$_> = '

Length of output: 243


Script:

#!/bin/bash
# Let's try to find the file content directly
cat packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/index.tsx

# Also check for AddProfileModal implementation
fd "AddProfileModal" --type f --exec cat {}

Length of output: 1015


Script:

#!/bin/bash
# Let's check the types file
cat packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/types.ts

# Let's also check for any other type definitions related to AddProfileModal
rg -l "AddProfileModal" --type ts

Length of output: 568


Script:

#!/bin/bash
# Let's check the AddProfileModal component to see its props definition
cat packages/components/modules/profiles/ProfilePopover/AddProfileModal/index.tsx

Length of output: 5870

packages/components/modules/profiles/graphql/mutations/OrganizationCreate.ts (1)

7-26: Verify GraphQL fragment dependencies

The mutation uses ProfileItemFragment but we should verify its definition and dependencies.

✅ Verification successful

GraphQL fragment dependencies are properly defined and used

The ProfileItemFragment is well-defined in packages/components/modules/profiles/graphql/queries/ProfileItem.ts and includes all necessary fields (id, name, image, urlPath). The fragment is consistently used across multiple queries and mutations, including the OrganizationCreateMutation. The generated GraphQL artifacts also confirm the correct fragment inclusion.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for ProfileItemFragment definition and its dependencies
rg -A 5 "fragment ProfileItemFragment"

Length of output: 8290

packages/components/modules/profiles/ProfilePopover/ProfilesList/ProfileMenuItem/index.tsx (2)

36-36: LGTM: Proper accessibility label

Good use of descriptive aria-label for the menu item.


35-35: Verify impact of callback signature change

The onProfileChange callback now receives an additional profileRef parameter. Ensure all component usages are updated accordingly.

✅ Verification successful

No changes needed for callback signature

Looking at the parent component's implementation, the handleProfileChange function already accepts both the profile data and profile key parameters:

const handleProfileChange = (
  profile: ProfileItemFragment$data,
  profileKey: ProfileItemFragment$key,
) => {

The change in the child component (ProfileMenuItem) correctly matches this signature by passing both profile and profileRef parameters, which align with the parent's expectations.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for onProfileChange usages
rg -A 3 "onProfileChange={" --type tsx

Length of output: 138


Script:

#!/bin/bash
# Search for onProfileChange usages with proper escaping
rg -A 3 'onProfileChange=\{' --type tsx

Length of output: 69


Script:

#!/bin/bash
# Search for onProfileChange usages with proper escaping, using typescript file type
rg -A 3 'onProfileChange=\{' -t ts

Length of output: 424


Script:

#!/bin/bash
# Let's check the handleProfileChange implementation in the parent component
ast-grep --pattern 'handleProfileChange = $_' packages/components/modules/profiles/ProfilePopover/ProfilesList/index.tsx

Length of output: 122


Script:

#!/bin/bash
# Let's check the entire content of the parent component to see the handleProfileChange implementation
cat packages/components/modules/profiles/ProfilePopover/ProfilesList/index.tsx

Length of output: 4707

packages/components/modules/profiles/ProfilePopover/ProfilesList/index.tsx (1)

3-3: LGTM: Improved data fetching with Relay hooks

The addition of useFragment and usePaginationFragment hooks from react-relay enhances data management and pagination capabilities.

Also applies to: 9-10

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Outside diff range and nitpick comments (8)
packages/components/modules/profiles/context/CurrentProfileProvider/index.tsx (1)

100-104: Optimize effect dependencies

The effect's dependency on user?.id could lead to unnecessary re-renders. Consider using user consistently.

 useEffect(() => {
   if (profileData && user) {
     storeRef.current?.setState({ profile: profileData, userId: user.id })
   }
-}, [profileData, user?.id])
+}, [profileData, user])
packages/components/modules/navigations/Header/AccountMenu/AccountPopover/__tests__/AccountPopover.cy.tsx (3)

67-69: Use constant in factory call for consistency.

The constant profilesListLength is defined but not used in the factory call. Use the constant instead of the magic number for better maintainability.

-const profileListData = mockProfilesListFactory(6, mockUserProfileData.data.me.profile)
+const profileListData = mockProfilesListFactory(profilesListLength, mockUserProfileData.data.me.profile)

90-94: Avoid duplicated test setup code.

The profile switching setup code is repeated multiple times. Consider extracting it to a helper function.

const switchToProfile = (resolveMostRecentOperation: Function, profileListData: any) => {
  cy.findByRole('menuitem', { name: /switch profile/i })
    .click()
    .then(() => {
      resolveMostRecentOperation({ data: profileListData })
    })
}

110-116: Add test for organization profile creation.

Based on the PR objectives, we need tests for the "Add new organization" functionality, including validation of unique usernames and form submission.

Would you like me to help create test cases for:

  • Organization creation modal
  • Username uniqueness validation
  • Form validation and submission
  • Toast notifications for successful creation
packages/components/modules/profiles/ProfilePopover/ProfilesList/index.tsx (4)

32-35: Consider using a more efficient fetch policy

The store-and-network fetch policy will always make a network request, even if the data is available locally. Consider using store-or-network instead, which will only make a network request if the data isn't available in the store.

  const { me } = useLazyLoadQuery<ProfilesListQueryType>(ProfilesListQuery, options, {
-   fetchPolicy: 'store-and-network',
+   fetchPolicy: 'store-or-network',
  })

32-32: Extract pagination constants

The page size (10) is hardcoded in multiple places. Consider extracting it to a constant to maintain consistency and make it easier to modify.

+ const PROFILES_PAGE_SIZE = 10
+
  const ProfilesList: FC<ProfilesListProps> = ({
    // ...
  }) => {
-   const options = { count: 10 }
+   const options = { count: PROFILES_PAGE_SIZE }
    // ...
    endReached={() => {
      if (hasNext) {
-       loadNext(10)
+       loadNext(PROFILES_PAGE_SIZE)
      }
    }

Also applies to: 104-106


92-93: Improve height calculation logic

The current height calculation assumes a fixed item height of 56px. Consider making this value configurable or calculating it dynamically based on the actual rendered content.

+ const PROFILE_ITEM_HEIGHT = 56
+ 
- const minHeight = profiles.length * 56 > listMaxHeight ? listMaxHeight : profiles.length * 56
+ const minHeight = Math.min(profiles.length * PROFILE_ITEM_HEIGHT, listMaxHeight)

Line range hint 123-141: Enhance accessibility for the sliding menu

The sliding menu implementation could benefit from improved accessibility features.

  return (
    <Slide direction={openSubmenu ? 'left' : 'right'} in={openSubmenu} mountOnEnter unmountOnExit>
-     <Box>
+     <Box
+       role="dialog"
+       aria-label="Profile selection menu"
+       onKeyDown={(e) => {
+         if (e.key === 'Escape') {
+           handleCloseSubmenu()
+         }
+       }}
+     >
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 97f1749d3850bad6058396e8d3719503198b39cc and 479322e57d5bbd7d9b54db53090077d239b7f4f9.

📒 Files selected for processing (12)
  • packages/components/modules/messages/MessagesList/MessagesGroup/MessageItem/index.tsx (0 hunks)
  • packages/components/modules/messages/SendMessage/index.tsx (3 hunks)
  • packages/components/modules/navigations/Header/AccountMenu/AccountPopover/__tests__/AccountPopover.cy.tsx (3 hunks)
  • packages/components/modules/navigations/Header/AccountMenu/AccountPopover/__tests__/__mocks__/profiles.ts (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/ProfileMenuItem/index.tsx (2 hunks)
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/ProfileMenuItem/types.ts (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/index.tsx (2 hunks)
  • packages/components/modules/profiles/context/CurrentProfileProvider/__tests__/CurrentProfileProvider.test.tsx (1 hunks)
  • packages/components/modules/profiles/context/CurrentProfileProvider/__tests__/__mock__/profiles.ts (1 hunks)
  • packages/components/modules/profiles/context/CurrentProfileProvider/__tests__/__utils__/TestComponent/index.tsx (2 hunks)
  • packages/components/modules/profiles/context/CurrentProfileProvider/index.tsx (5 hunks)
  • packages/components/modules/profiles/context/CurrentProfileProvider/types.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • packages/components/modules/messages/MessagesList/MessagesGroup/MessageItem/index.tsx
✅ Files skipped from review due to trivial changes (1)
  • packages/components/modules/profiles/context/CurrentProfileProvider/tests/CurrentProfileProvider.test.tsx
🚧 Files skipped from review as they are similar to previous changes (5)
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/ProfileMenuItem/index.tsx
  • packages/components/modules/profiles/context/CurrentProfileProvider/types.ts
  • packages/components/modules/messages/SendMessage/index.tsx
  • packages/components/modules/profiles/context/CurrentProfileProvider/tests/utils/TestComponent/index.tsx
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/ProfileMenuItem/types.ts
🔇 Additional comments (7)
packages/components/modules/profiles/context/CurrentProfileProvider/index.tsx (3)

37-41: Consider organization profile handling in data fetching


71-73: 🛠️ Refactor suggestion

Add type validation before setting profile

Before setting the profile, validate its type to ensure proper handling of organization profiles.

-if (userProfile) {
+if (userProfile && ['Profile', 'OrganizationProfile'].includes(userProfile.__typename)) {
   setCurrentProfile(userProfile)
+} else {
+  console.warn('Invalid profile type received:', userProfile?.__typename)
 }

Likely invalid or redundant comment.


48-50: 🛠️ Refactor suggestion

Consider type safety for organization profiles

The current type ProfileItemFragment$key might need to be updated to handle both user and organization profile types.

-const [currentProfile, setCurrentProfile] = useState<ProfileItemFragment$key | null>(null)
+type ProfileKey = ProfileItemFragment$key & {
+  readonly __typename: 'Profile' | 'OrganizationProfile'
+}
+const [currentProfile, setCurrentProfile] = useState<ProfileKey | null>(null)

Likely invalid or redundant comment.

packages/components/modules/navigations/Header/AccountMenu/AccountPopover/__tests__/__mocks__/profiles.ts (1)

26-42: LGTM! The mock data structure aligns with GraphQL best practices.

The refactoring to use the edges/node pattern is a good change as it:

  • Follows GraphQL connection specification
  • Supports pagination which will be needed for organization profiles
  • Maintains consistency with typical GraphQL response structure
packages/components/modules/navigations/Header/AccountMenu/AccountPopover/__tests__/AccountPopover.cy.tsx (2)

82-84: 🛠️ Refactor suggestion

Add null check for profile access.

The optional chaining is good, but we should add a test case to verify the behavior when the last profile is undefined.

Consider adding a test case:

it('should handle undefined profiles gracefully', () => {
  const emptyProfileListData = mockProfilesListFactory(0, null)
  // ... test implementation
})

77-80: 🛠️ Refactor suggestion

Add error handling for empty profiles list.

The test should handle the case where the profiles list might be empty. Consider adding a test case or guard clause.

-profileListData.data.me.profiles.edges.forEach((profile) => {
+if (profileListData.data.me.profiles.edges.length === 0) {
+  cy.findByText(/no profiles available/i).should('exist')
+} else {
+  profileListData.data.me.profiles.edges.forEach((profile) => {
     cy.findAllByText(profile.node.name).should('exist')
     cy.findAllByText(profile.node.urlPath.path).should('exist').scrollIntoView()
-})
+  })
+}
packages/components/modules/profiles/ProfilePopover/ProfilesList/index.tsx (1)

55-58: Verify organization profile handling

Please ensure that the profiles filtering logic correctly handles both user and organization profiles as mentioned in the PR objectives.

✅ Verification successful

Let me check the ProfileItemFragment to see what profile fields are being queried.


Let me check if there are any organization-specific types or fields in the GraphQL schema to verify if we need additional fields for organization profiles.


Let me check the complete schema definition for Organization type to understand its relationship with profiles.


Let me try a different approach to check the Organization type definition.


Profile filtering logic is correctly implemented for both user and organization profiles

The current implementation in ProfilesList is correct and doesn't need modification because:

  1. The GraphQL schema shows that Organization and Profile types are properly linked (Organization has a profile field of type Profile)
  2. The ProfileItemFragment queries all necessary fields (id, name, image, urlPath) which are common to both user and organization profiles
  3. The filtering logic edge?.node is type-agnostic and will work correctly for both profile types
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if the GraphQL query includes organization profile fields

# Check GraphQL query implementation
rg -A 10 "ProfilesListQuery = graphql\`" ./packages/components/modules/profiles/graphql/queries/ProfilesList.ts

# Check fragment implementation
rg -A 10 "ProfilesListFragment = graphql\`" ./packages/components/modules/profiles/graphql/queries/ProfilesList.ts

Length of output: 944


Script:

#!/bin/bash
# Check ProfileItemFragment implementation to see what fields are being queried
rg -A 15 "ProfileItemFragment = graphql\`" 

Length of output: 1208


Script:

#!/bin/bash
# Check for organization-related types and fields in GraphQL schema
rg -i "type.*organization|interface.*organization|union.*organization" ./packages/**/*.graphql

# Check for any organization-specific profile fields or types
rg -i "profile.*organization|organization.*profile" ./packages/**/*.graphql

Length of output: 293


Script:

#!/bin/bash
# Get the complete Organization type definition from schema
rg -A 20 "^type Organization implements Node {" ./packages/components/schema.graphql

# Also check if Profile type has any organization-related fields
rg -A 20 "^type Profile" ./packages/components/schema.graphql

Length of output: 2901


Script:

#!/bin/bash
# Use simpler pattern to get Organization type definition
rg "type Organization" -A 10 ./packages/components/schema.graphql

# Also check if there are any organization-profile connections in the schema
rg "organization.*profile|profile.*organization" -i ./packages/components/schema.graphql

Length of output: 693

Comment on lines 40 to 55
Copy link

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 profile switching

The profile switching logic should handle potential errors and provide appropriate feedback to users.

  const handleProfileChange = (profile: ProfileItemFragment$data) => {
    if (currentProfile?.id !== profile?.id) {
+     try {
        setCurrentProfile({
          profile,
        })
        sendToast(`Switched to ${profile?.name}`)
        handleCloseSubmenu()
+     } catch (error) {
+       sendToast('Failed to switch profile. Please try again.', { type: 'error' })
+       console.error('Profile switch failed:', error)
+     }
    }
  }
📝 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 handleProfileChange = (profile: ProfileItemFragment$data) => {
if (currentProfile?.id !== profile?.id) {
setCurrentProfile({
profile,
})
sendToast(`Switched to ${profile?.name}`)
handleCloseSubmenu()
}
}
const handleProfileChange = (profile: ProfileItemFragment$data) => {
if (currentProfile?.id !== profile?.id) {
try {
setCurrentProfile({
profile,
})
sendToast(`Switched to ${profile?.name}`)
handleCloseSubmenu()
} catch (error) {
sendToast('Failed to switch profile. Please try again.', { type: 'error' })
console.error('Profile switch failed:', error)
}
}
}

@priscilladeroode priscilladeroode force-pushed the BA-1832-fe-multiple-profiles-create-organization-profile branch 2 times, most recently from f31538d to 7b3079f Compare December 2, 2024 18:16
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (9)
packages/components/modules/profiles/ProfilePopover/ProfilesList/ProfileMenuItem/index.tsx (1)

Line range hint 27-50: Add type discrimination for organization profiles

Since this PR implements organization profiles, consider enhancing the component to handle different profile types distinctly:

  1. The display format might need to be different for organizations
  2. The aria-label could be more specific ("Switch to organization profile" vs "Switch to user profile")

Consider this enhancement:

  const isActiveProfile = profile.id === currentProfile?.id
+ const isOrganization = profile.__typename === 'OrganizationProfile'

  return (
    <StyledMenuItem
      tabIndex={0}
      active={isActiveProfile}
      onClick={() => onProfileChange(profile)}
-     aria-label={`Switch to ${profile.name ?? 'this profile'}`}
+     aria-label={`Switch to ${isOrganization ? 'organization' : 'user'} profile: ${profile.name ?? 'unnamed'}`}
    >
packages/components/modules/profiles/context/CurrentProfileProvider/index.tsx (1)

71-73: Add specific error handling for profile fetching

The current error handling silently ignores failures. Consider adding specific error handling for different scenarios.

Consider this enhancement:

  fetchUserProfile(environment)
    .then((userProfile) => {
      if (userProfile) {
        setCurrentProfile(userProfile)
      }
    })
-   .catch(() => {})
+   .catch((error) => {
+     console.error('Failed to fetch user profile:', error)
+     // Optionally show user-friendly error message
+   })
packages/components/package.json (2)

49-50: Consider broader React version compatibility

The peer dependencies specify exact versions for React and React DOM (18.3.1). This strict version requirement might cause compatibility issues with projects using slightly different React versions. Consider using a more flexible version range (e.g., "^18.2.0").


24-113: Recommend dependency audit and update documentation

Given the extensive dependency updates, please:

  1. Run npm audit to check for any security vulnerabilities
  2. Update the package's documentation to reflect any breaking changes
  3. Consider adding a CHANGELOG.md entry for these significant dependency updates
packages/components/modules/navigations/Header/AccountMenu/AccountPopover/__tests__/AccountPopover.cy.tsx (2)

67-68: Document the significance of profilesListLength

Consider adding a comment explaining why 6 was chosen as the length, especially since the test verifies that only 5 profiles are visible at a time.

+// Maximum number of profiles to test scrolling behavior, where only 5 are visible at a time
 const profilesListLength = 6

93-97: Consider adding scroll behavior test

While the test correctly verifies the 5-profile limit, consider adding a test to verify that users can scroll to see additional profiles.

cy.findByLabelText('List of available profiles').within(() => {
  cy.get('li').should('have.length.gt', 5) // Verify total profiles
  cy.get('li:visible').should('have.length', 5) // Verify visible profiles
  // Verify scroll functionality
  cy.get('li').last().scrollIntoView().should('be.visible')
})
packages/components/modules/profiles/ProfilePopover/AddProfileModal/index.tsx (3)

140-161: Enhance keyboard navigation

While the modal has good ARIA labels, consider improving keyboard navigation.

 <Dialog
   open={open}
   onClose={handleClose}
   aria-labelledby="organization-modal-title"
   aria-describedby="organization-modal-description"
+  onKeyDown={(e) => {
+    if (e.key === 'Enter' && !e.shiftKey) {
+      e.preventDefault()
+      form.handleSubmit(onSubmit)()
+    }
+  }}
   PaperProps={{

170-175: Optimize URL path auto-population

Consider debouncing the URL path auto-population to prevent unnecessary updates during rapid typing.

+import { useMemo } from 'react'
+import debounce from 'lodash/debounce'

+const debouncedSetUrlPath = useMemo(
+  () => debounce((value: string, form: any) => {
+    form.setValue('urlPath', slugify(value.toLowerCase()))
+    form.trigger('urlPath')
+  }, 300),
+  []
+)

 onBlur={() => {
   if (!form.getValues('urlPath')) {
-    form.setValue('urlPath', slugify(form.getValues('name').toLocaleLowerCase()))
-    form.trigger('urlPath')
+    debouncedSetUrlPath(form.getValues('name'), form)
   }
 }}

196-205: Enhance submission feedback

Consider adding a tooltip to explain why the button is disabled when there are validation errors.

+import { Tooltip } from '@mui/material'

+const getTooltipTitle = (errors: Record<string, any>) => {
+  if (Object.keys(errors).length > 0) {
+    return 'Please fix the form errors before submitting'
+  }
+  return ''
+}

+<Tooltip title={getTooltipTitle(form.formState.errors)}>
   <LoadingButton
     variant="contained"
     color="inherit"
     sx={{ width: 'auto' }}
     type="submit"
     loading={isMutationInFlight}
     disabled={isMutationInFlight || Object.keys(form.formState.errors).length > 0}
   >
     {submitLabel}
   </LoadingButton>
+</Tooltip>
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 479322e57d5bbd7d9b54db53090077d239b7f4f9 and b805e7ff533c860a988efafe63a15012a7d52c54.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (12)
  • packages/components/modules/messages/MessagesList/MessagesGroup/MessageItem/index.tsx (0 hunks)
  • packages/components/modules/navigations/Header/AccountMenu/AccountPopover/__tests__/AccountPopover.cy.tsx (2 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/index.tsx (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/ProfileMenuItem/index.tsx (2 hunks)
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/ProfileMenuItem/types.ts (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/index.tsx (2 hunks)
  • packages/components/modules/profiles/context/CurrentProfileProvider/__tests__/CurrentProfileProvider.test.tsx (1 hunks)
  • packages/components/modules/profiles/context/CurrentProfileProvider/__tests__/__mock__/profiles.ts (1 hunks)
  • packages/components/modules/profiles/context/CurrentProfileProvider/__tests__/__utils__/TestComponent/index.tsx (2 hunks)
  • packages/components/modules/profiles/context/CurrentProfileProvider/index.tsx (5 hunks)
  • packages/components/modules/profiles/context/CurrentProfileProvider/types.ts (1 hunks)
  • packages/components/package.json (1 hunks)
💤 Files with no reviewable changes (1)
  • packages/components/modules/messages/MessagesList/MessagesGroup/MessageItem/index.tsx
🚧 Files skipped from review as they are similar to previous changes (6)
  • packages/components/modules/profiles/context/CurrentProfileProvider/tests/CurrentProfileProvider.test.tsx
  • packages/components/modules/profiles/context/CurrentProfileProvider/types.ts
  • packages/components/modules/profiles/context/CurrentProfileProvider/tests/utils/TestComponent/index.tsx
  • packages/components/modules/profiles/context/CurrentProfileProvider/tests/mock/profiles.ts
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/ProfileMenuItem/types.ts
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/index.tsx
🔇 Additional comments (12)
packages/components/modules/profiles/ProfilePopover/ProfilesList/ProfileMenuItem/index.tsx (1)

20-24: LGTM: Improved error handling with null check

The migration to useFragment and addition of null check improves the robustness of the component.

packages/components/modules/profiles/context/CurrentProfileProvider/index.tsx (3)

37-41: Consider organization profile handling in data fetching

The current implementation needs enhancement to handle organization profiles effectively.


48-50: LGTM: Improved state management

The addition of local state management using useState and the migration to useFragment improves the component's data handling.


100-104: 🛠️ Refactor suggestion

Enhance error handling for profile state updates

The profile state update should handle organization-specific cases and validate profile types.

Consider this enhancement:

  useEffect(() => {
-   if (profileData && user) {
+   if (profileData && user && (
+     profileData.__typename === 'UserProfile' ||
+     profileData.__typename === 'OrganizationProfile'
+   )) {
      storeRef.current?.setState({ profile: profileData, userId: user.id })
+   } else if (profileData) {
+     console.warn(`Unexpected profile type: ${profileData.__typename}`)
    }
  }, [profileData, user?.id])

Likely invalid or redundant comment.

packages/components/package.json (4)

25-27: Verify MUI package version alignment

The MUI packages should maintain version alignment:

  • @mui/lab: ^5.0.0-alpha.170
  • @mui/material: ^5.15.19
  • @mui/system: ^5.15.19

The alpha version of @mui/lab might introduce instability.


29-29: ⚠️ Potential issue

Review breaking changes in major dependency updates

  1. @tanstack/react-query v5 introduces breaking changes from v4. Ensure all query/mutation hooks are updated accordingly.
  2. Using Next.js canary release (14.3.0-canary.24) in production could be risky. Consider using a stable version.

Also applies to: 34-34

✅ Verification successful

React Query v5 usage is already compatible with latest version

The codebase is already using the object-style syntax for both useQuery and useMutation hooks, which is the recommended approach in React Query v5. All instances follow the new patterns:

  • Query hooks use { queryFn, queryKey, ...options } syntax
  • Mutation hooks use { mutationFn, ...options } syntax
  • Query invalidation uses the new { queryKey } object syntax
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for react-query usage patterns that might need updates
ast-grep --pattern 'useQuery($$$)' 
ast-grep --pattern 'useMutation($$$)'

Length of output: 16204


72-75: Update test files for new Testing Library version

The significant version updates in testing libraries (@testing-library/react v16) might require updates to existing tests, particularly around:

  • Async testing patterns
  • Event handling
  • React 18 concurrent features

Also applies to: 97-98


62-70: ⚠️ Potential issue

Verify Storybook v8 migration requirements

The update to Storybook v8 requires migration steps and might break existing stories. Ensure that:

  1. All stories are compatible with the new format
  2. The webpack configuration is updated accordingly
  3. All addons are compatible with v8

Also applies to: 105-105

✅ Verification successful

No migration concerns found for Storybook v8 upgrade

Based on the inspection of the codebase:

  1. The Storybook configuration is already using modern patterns compatible with v8
  2. Stories are written in MDX format, which is the recommended approach
  3. No deprecated APIs like storiesOf or addDecorator are in use
  4. The webpack configuration and all addons are v8 compatible
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for potential incompatible story formats
rg -g "*.stories.*" "storiesOf\(" 
rg -g "*.stories.*" "addDecorator"

Length of output: 368


Script:

#!/bin/bash
# First, let's find all story files in the repository
fd "\.stories\.[tj]sx?$"

# Then, let's check the content of any existing Storybook configuration
fd "\.storybook" --type f --exec cat {}

# Check for any migration guides or documentation
fd "README|CHANGELOG" --type f --exec grep -i "storybook" {}

Length of output: 1471


Script:

#!/bin/bash
# Let's check specifically in the components package
fd . "packages/components" -t f -e mdx -e stories.tsx -e stories.ts -e stories.jsx -e stories.js

# Check for Storybook config in components package
fd . "packages/components/.storybook" -t f -x cat {}

# Look for package.json scripts related to Storybook
cat packages/components/package.json | grep -A 5 -B 5 "storybook"

Length of output: 17511

packages/components/modules/navigations/Header/AccountMenu/AccountPopover/__tests__/AccountPopover.cy.tsx (2)

80-87: LGTM! Comprehensive profile switching test implementation.

The test thoroughly verifies both the display of profile data and the switching functionality, with proper scrolling behavior.


113-119: LGTM! Good negative test case for toast notification.

The test correctly verifies that the success toast is not shown when the profile is not actually changed.

packages/components/modules/profiles/ProfilePopover/AddProfileModal/index.tsx (2)

25-37: ⚠️ Potential issue

Strengthen URL path validation

The current validation could be improved to prevent unfriendly URLs and ensure better user experience.

-const slugRegex = /^[a-z0-9]+(?:-[a-z0-9]+)*$/
+const slugRegex = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/

 const schema = z.object({
   name: z
     .string()
     .trim()
     .min(3, { message: 'Must have at least three character' })
     .max(255, { message: 'Must have at most 255 characters' }),
   urlPath: z
     .string()
+    .min(1, { message: 'URL path is required' })
+    .transform(str => str.toLowerCase())
     .regex(slugRegex, 'Must contain only lowercase letters, numbers, and hyphens')
     .max(500, { message: 'Must have at most 500 characters' }),
 })

81-137: ⚠️ Potential issue

Enhance mutation error handling

The current error handling could be more user-friendly and comprehensive.

 const onSubmit = (data: OrganizationCreateForm) => {
   if (isMutationInFlight) return

   nextClientMutationId += 1
   const clientMutationId = nextClientMutationId.toString()

+  const handleError = (error: Error) => {
+    console.error(error)
+    setFormRelayErrors(form, [{
+      field: 'name',
+      message: 'An unexpected error occurred. Please try again.'
+    }])
+  }

   commitMutation({
     variables: {
       input: {
         name: data.name,
         urlPath: data.urlPath,
         clientMutationId,
       },
     },
     updater: (store) => {
       const payload = store.getRootField('organizationCreate')
       if (!payload) {
+        handleError(new Error('Failed to create organization'))
         return
       }
       // ... rest of the updater
     },
     onCompleted: (response, errors) => {
       if (errors) {
-        console.error(errors)
+        handleError(new Error(errors[0]?.message || 'Failed to create organization'))
         return
       }
       // ... rest of onCompleted
     },
-    onError: console.error,
+    onError: handleError,
   })
 }

@priscilladeroode priscilladeroode force-pushed the BA-1832-fe-multiple-profiles-create-organization-profile branch from b805e7f to 061e251 Compare December 4, 2024 22:19
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Outside diff range and nitpick comments (8)
packages/components/package.json (1)

49-50: Consider using version range for React peer dependencies

Using an exact version (18.3.1) for React peer dependencies might be too restrictive. Consider using a version range (e.g., ^18.3.0) to allow for patch updates while maintaining compatibility.

-    "react": "18.3.1",
-    "react-dom": "18.3.1"
+    "react": "^18.3.0",
+    "react-dom": "^18.3.0"
packages/components/modules/profiles/ProfilePopover/AddProfileModal/styled.tsx (1)

3-9: Consider making the height more flexible

While the styling implementation is clean and follows MUI best practices, setting a fixed height: '100%' might cause layout issues in containers that don't have a defined height.

Consider this more flexible approach:

export const Form = styled('form')(({ theme }) => ({
-  height: '100%',
+  minHeight: 0,
+  flex: 1,
  justifyContent: 'space-between',
  display: 'flex',
  flexDirection: 'column',
  gap: theme.spacing(2),
}))
packages/components/modules/profiles/ProfilePopover/AddProfileModal/types.ts (1)

11-14: Consider adding validation types to OrganizationCreateForm

The interface could benefit from more specific types that reflect the validation rules defined in the schema.

Consider adding branded types for validated fields:

+ type ValidatedName = string & { _brand: 'ValidatedName' }
+ type ValidatedUrlPath = string & { _brand: 'ValidatedUrlPath' }

export interface OrganizationCreateForm {
-  name: string
-  urlPath: string
+  name: ValidatedName
+  urlPath: ValidatedUrlPath
}
packages/components/modules/profiles/ProfilePopover/AddProfileModal/constants.ts (2)

9-9: Fix typo in error message

There's a grammatical error in the validation message.

- .min(3, { message: 'Must have at least three character' })
+ .min(3, { message: 'Must have at least three characters' })

5-15: Consider additional validation rules for organization names

The current validation only checks length and whitespace. Consider adding more specific rules for organization names.

Example enhancement:

export const schema = z.object({
  name: z
    .string()
    .trim()
    .min(3, { message: 'Must have at least three characters' })
    .max(255, { message: 'Must have at most 255 characters' })
+   .regex(/^[a-zA-Z0-9\s\-_&()]+$/, {
+     message: 'Name can only contain letters, numbers, spaces, and common symbols (-, _, &, (, ))'
+   })
+   .refine((val) => !val.match(/\s{2,}/), {
+     message: 'Name cannot contain consecutive spaces'
+   }),
  urlPath: z
    .string()
    .regex(slugRegex, 'Must contain only lowercase letters, numbers, and hyphens')
    .max(500, { message: 'Must have at most 500 characters' }),
})
packages/components/modules/profiles/ProfilePopover/ProfilesList/index.tsx (1)

89-111: Optimize callback functions with useMemo

Consider memoizing the callback functions to prevent unnecessary re-renders in the virtualized list.

+const memoizedEndReached = useMemo(
+  () => () => {
+    if (hasNext) {
+      loadNext(10)
+    }
+  },
+  [hasNext, loadNext]
+)

 const renderContent = () => {
   if (profiles.length === 0) return <EmptyState />

   const minHeight = profiles.length * 56 > listMaxHeight ? listMaxHeight : profiles.length * 56

   return (
     <Box sx={{ backgroundColor: 'common.white' }}>
       <Virtuoso
         data={profiles}
         style={{ height: 'auto', minHeight }}
         itemContent={(index, profile) => renderProfileItem(profile, index)}
         components={{
           Footer: renderVirtuosoLoadingState,
         }}
-        endReached={() => {
-          if (hasNext) {
-            loadNext(10)
-          }
-        }}
+        endReached={memoizedEndReached}
       />
     </Box>
   )
 }
packages/components/modules/navigations/Header/AccountMenu/AccountPopover/__tests__/AccountPopover.cy.tsx (1)

67-87: Add error case testing for profile switching

While the happy path is well tested, consider adding test cases for error scenarios.

 it('should render the account popover with profile and be able to interact with it', () => {
   // ... existing test code ...

+  it('should handle profile switch errors gracefully', () => {
+    const { environment, rejectMostRecentOperation } = createTestEnvironment()
+
+    cy.mount(<AccountPopoverForTesting environment={environment} />)
+    cy.findByRole('menuitem', { name: /switch profile/i })
+      .click()
+      .then(() => {
+        rejectMostRecentOperation('Failed to switch profile')
+      })
+
+    cy.get('@sendToastSpy').should('have.been.calledWith', 
+      'Failed to switch profile. Please try again.',
+      { type: 'error' }
+    )
+  })
packages/components/schema.graphql (1)

780-780: Consider adding specific error types documentation

The organizationCreate mutation would benefit from documented error types to help clients handle specific error cases.

+"""
+Possible errors:
+- DUPLICATE_USERNAME: When the username is already taken
+- INVALID_PATH: When the URL path contains invalid characters
+- UNAUTHORIZED: When the user doesn't have permission to create organizations
+"""
 organizationCreate(input: OrganizationCreateInput!): OrganizationCreatePayload
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between b805e7ff533c860a988efafe63a15012a7d52c54 and 061e2518001a034fd14cf225ffdd94ea0ea548c2.

⛔ Files ignored due to path filters (7)
  • packages/components/__generated__/OrganizationCreateMutation.graphql.ts is excluded by !**/__generated__/**
  • packages/components/__generated__/ProfileItemInlineFragment.graphql.ts is excluded by !**/__generated__/**
  • packages/components/__generated__/ProfilesListFragment.graphql.ts is excluded by !**/__generated__/**
  • packages/components/__generated__/ProfilesListQuery.graphql.ts is excluded by !**/__generated__/**
  • packages/components/__generated__/UserProfileQuery.graphql.ts is excluded by !**/__generated__/**
  • packages/components/__generated__/profilesListRefetchable.graphql.ts is excluded by !**/__generated__/**
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (25)
  • packages/components/modules/messages/MessagesList/MessagesGroup/MessageItem/index.tsx (0 hunks)
  • packages/components/modules/navigations/Header/AccountMenu/AccountPopover/__tests__/AccountPopover.cy.tsx (2 hunks)
  • packages/components/modules/navigations/Header/AccountMenu/AccountPopover/__tests__/__mocks__/profiles.ts (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/index.tsx (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/styled.tsx (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/types.ts (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/constants.ts (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/index.tsx (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/styled.tsx (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/types.ts (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/ProfileMenuItem/index.tsx (2 hunks)
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/ProfileMenuItem/types.ts (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/index.tsx (2 hunks)
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/types.ts (2 hunks)
  • packages/components/modules/profiles/context/CurrentProfileProvider/__tests__/CurrentProfileProvider.test.tsx (1 hunks)
  • packages/components/modules/profiles/context/CurrentProfileProvider/__tests__/__mock__/profiles.ts (1 hunks)
  • packages/components/modules/profiles/context/CurrentProfileProvider/__tests__/__utils__/TestComponent/index.tsx (2 hunks)
  • packages/components/modules/profiles/context/CurrentProfileProvider/index.tsx (5 hunks)
  • packages/components/modules/profiles/context/CurrentProfileProvider/types.ts (1 hunks)
  • packages/components/modules/profiles/graphql/mutations/OrganizationCreate.ts (1 hunks)
  • packages/components/modules/profiles/graphql/queries/ProfileItemInline.ts (0 hunks)
  • packages/components/modules/profiles/graphql/queries/ProfilesList.ts (1 hunks)
  • packages/components/modules/profiles/graphql/queries/UserProfile.ts (1 hunks)
  • packages/components/package.json (1 hunks)
  • packages/components/schema.graphql (60 hunks)
💤 Files with no reviewable changes (2)
  • packages/components/modules/profiles/graphql/queries/ProfileItemInline.ts
  • packages/components/modules/messages/MessagesList/MessagesGroup/MessageItem/index.tsx
🚧 Files skipped from review as they are similar to previous changes (13)
  • packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/styled.tsx
  • packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/types.ts
  • packages/components/modules/profiles/context/CurrentProfileProvider/tests/CurrentProfileProvider.test.tsx
  • packages/components/modules/navigations/Header/AccountMenu/AccountPopover/tests/mocks/profiles.ts
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/types.ts
  • packages/components/modules/profiles/context/CurrentProfileProvider/types.ts
  • packages/components/modules/profiles/context/CurrentProfileProvider/tests/utils/TestComponent/index.tsx
  • packages/components/modules/profiles/graphql/queries/UserProfile.ts
  • packages/components/modules/profiles/context/CurrentProfileProvider/tests/mock/profiles.ts
  • packages/components/modules/profiles/graphql/mutations/OrganizationCreate.ts
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/index.tsx
  • packages/components/modules/profiles/graphql/queries/ProfilesList.ts
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/ProfileMenuItem/types.ts
🔇 Additional comments (14)
packages/components/package.json (2)

24-42: Review major version updates and canary dependencies

Several concerns need attention:

  1. @tanstack/react-query upgrade to v5.x includes breaking changes. Ensure all query/mutation implementations are updated accordingly.
  2. Using Next.js canary version (14.3.0-canary.24) in production could lead to instability. Consider using the latest stable version.

The addition of slugify aligns well with the organization profile URL generation requirements.

Let's verify the impact of react-query v5 upgrade:

✅ Verification successful

The previous results showed multiple files using react-query hooks. Now, let's examine the actual implementation patterns to identify if they're using features that have breaking changes in v5, such as:

  1. Changes in mutation API (no more mutateAsync)
  2. Changes in query key management
  3. Changes in error handling
  4. Changes in infinite query implementation

This will help us provide more specific guidance about the upgrade impact.


Based on the analysis of the codebase, I can now provide a final response as the code inspection reveals the actual usage patterns of react-query:

@tanstack/react-query v5 upgrade is safe to proceed

The codebase already follows v5-compatible patterns:

  • Uses object syntax for mutations: useMutation({ mutationFn: ... })
  • Uses object syntax for queries: useQuery({ queryFn: ... })
  • Uses modern cache invalidation with invalidateQueries({ queryKey: ... })
  • No usage of deprecated features like mutateAsync
  • Proper usage of QueryClient methods

The Next.js canary version concern remains valid, but the react-query upgrade is safe to proceed.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for react-query usage patterns that might need updates
rg -l "useQuery|useMutation|useInfiniteQuery"

Length of output: 1952


Script:

#!/bin/bash
# Let's check the actual implementation patterns to identify potential v5 breaking changes
rg "useQuery|useMutation|useInfiniteQuery" -A 5 packages/components/modules/ packages/authentication/modules/

Length of output: 38017


53-113: Verify Storybook v8 migration requirements

The update to Storybook v8.x is a major version upgrade that requires migration steps. Ensure that:

  1. All stories are compatible with the new format
  2. The webpack configuration is updated for v8
  3. All addons are compatible with v8

Let's check for potential migration issues:

✅ Verification successful

No migration concerns found for Storybook v8 upgrade

Based on the analysis of the codebase:

  1. All stories are using the modern Component Story Format (CSF) with proper imports from '@storybook/react'
  2. No usage of deprecated patterns like storiesOf, legacy decorators, or parameters
  3. Stories are properly structured with Meta type and proper component typing
  4. Stories are following v7+ standards which are compatible with v8

The update to Storybook v8 should be smooth as the codebase is already following modern practices and no deprecated patterns were found.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for story files that might need updates
fd -e stories.tsx -e stories.ts -e stories.js

# Check for deprecated Storybook patterns
rg "storiesOf|addDecorator|addParameters" 

Length of output: 91


Script:

#!/bin/bash
# Let's try a more comprehensive search for Storybook-related files and patterns
# First, find any .stories files with any extension
fd "\.stories\."

# Look for Storybook configuration files
fd "\.storybook"

# Search for CSF (Component Story Format) patterns
rg "export default \{|meta:|Story:" -A 3

# Check for potential deprecated imports from @storybook
rg "from '@storybook|from \"@storybook" -A 1

# Look for any Storybook decorators or configurations
rg "\.decorators|\.parameters|\.argTypes" -A 2

Length of output: 29674

packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/index.tsx (2)

21-29: Add ARIA attributes for improved accessibility

The menu item that triggers the modal should include accessibility attributes for better screen reader support.

 <MenuItem
   tabIndex={0}
   component={ButtonBase}
   sx={{ justifyContent: 'space-between' }}
   onClick={() => setOpen(true)}
+  aria-haspopup="dialog"
+  aria-expanded={open}
+  aria-label="Add new organization profile"
 >

12-15: LGTM! Props interface looks good

The component properly defines default values for props and uses appropriate types.

packages/components/modules/profiles/ProfilePopover/ProfilesList/ProfileMenuItem/index.tsx (2)

20-24: LGTM! Good error handling with fragment data

The null check for invalid fragment reference is a good defensive programming practice.


8-8: Verify type discrimination for organization profiles

Since this component now handles both user and organization profiles, consider adding type discrimination.

Also applies to: 20-20

packages/components/modules/profiles/context/CurrentProfileProvider/index.tsx (1)

37-38: Add type safety for organization profiles

The profile fetching and updating logic should handle organization-specific cases.

#!/bin/bash
# Description: Verify organization profile handling
# Expected: Find type checks and organization-specific logic

# Search for organization type checks
rg -A 5 "OrganizationProfile"

# Search for profile type handling in mutations
ast-grep --pattern 'mutation {
  $$$
  createOrganization($_) {
    $$$
  }
}'

Also applies to: 71-73

packages/components/modules/profiles/ProfilePopover/ProfilesList/index.tsx (4)

3-3: Great performance optimizations!

The addition of Virtuoso for virtualized list rendering and usePaginationFragment for paginated data fetching are excellent choices for handling large lists efficiently.

Also applies to: 9-10, 26-31


32-35: Good choice of fetch policy!

Using store-and-network fetch policy ensures the UI remains responsive while keeping data fresh.


136-138: Well-implemented Suspense boundary!

Good use of Suspense for handling loading states and providing a fallback UI.


40-48: 🛠️ Refactor suggestion

Add error handling for profile switching

The profile switching logic should handle potential errors to provide better user feedback.

 const handleProfileChange = (profile: ProfileItemFragment$data) => {
   if (currentProfile?.id !== profile?.id) {
+    try {
       setCurrentProfile({
         profile,
       })
       sendToast(`Switched to ${profile?.name}`)
       handleCloseSubmenu()
+    } catch (error) {
+      sendToast('Failed to switch profile. Please try again.', { type: 'error' })
+      console.error('Profile switch failed:', error)
+    }
   }
 }

Likely invalid or redundant comment.

packages/components/modules/navigations/Header/AccountMenu/AccountPopover/__tests__/AccountPopover.cy.tsx (1)

Line range hint 123-189: Excellent test coverage for custom props!

The tests thoroughly validate all custom props and their effects on the UI, including proper validation of avatar attributes and menu item visibility.

packages/components/schema.graphql (2)

1029-1069: Well-structured Organization type definitions!

The Organization type and related input/payload types are well-defined and follow GraphQL best practices with proper Node interface implementation.


1464-1477: Good interface design for profiles!

The ProfilesInterface provides a clean abstraction for profile-related operations, and the organization field addition maintains backward compatibility.

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance URL path validation regex

The current regex allows URLs that could start or end with hyphens, which might not be desirable.

Update the regex to prevent leading and trailing hyphens:

- const slugRegex = /^[a-z0-9]+(?:-[a-z0-9]+)*$/
+ const slugRegex = /^[a-z0-9]+(?:-[a-z0-9]+)*[a-z0-9]$/
📝 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 slugRegex = /^[a-z0-9]+(?:-[a-z0-9]+)*$/
const slugRegex = /^[a-z0-9]+(?:-[a-z0-9]+)*[a-z0-9]$/

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Consider potential race conditions in profile updates

The combination of local state and Zustand store updates could lead to race conditions. Consider using a single source of truth.

-  const [currentProfile, setCurrentProfile] = useState<ProfileItemFragment$key | null>(null)
-  const profileData = useFragment(ProfileItemFragment, currentProfile)
+  const [profileState, setProfileState] = useState<{
+    fragmentRef: ProfileItemFragment$key | null,
+    data: typeof ProfileItemFragment | null
+  }>({ fragmentRef: null, data: null })
+  
+  const profileData = useFragment(ProfileItemFragment, profileState.fragmentRef)

Also applies to: 49-49, 50-50

Comment on lines 100 to 104
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error boundary for profile state updates

The effect updating the Zustand store should handle potential errors to prevent inconsistent states.

 useEffect(() => {
-  if (profileData && user) {
-    storeRef.current?.setState({ profile: profileData, userId: user.id })
-  }
+  try {
+    if (profileData && user) {
+      storeRef.current?.setState({ profile: profileData, userId: user.id })
+    }
+  } catch (error) {
+    console.error('Failed to update profile state:', error)
+    // Reset to initial state on error
+    storeRef.current?.setState({ ...INITIAL_CURRENT_PROFILE_STATE })
+  }
 }, [profileData, user?.id])
📝 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 (profileData && user) {
storeRef.current?.setState({ profile: profileData, userId: user.id })
}
}, [profileData, user?.id])
useEffect(() => {
try {
if (profileData && user) {
storeRef.current?.setState({ profile: profileData, userId: user.id })
}
} catch (error) {
console.error('Failed to update profile state:', error)
// Reset to initial state on error
storeRef.current?.setState({ ...INITIAL_CURRENT_PROFILE_STATE })
}
}, [profileData, user?.id])

@priscilladeroode priscilladeroode force-pushed the BA-1832-fe-multiple-profiles-create-organization-profile branch from 061e251 to c2b63e2 Compare December 4, 2024 23:26
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Outside diff range and nitpick comments (4)
packages/components/modules/profiles/ProfilePopover/ProfilesList/index.tsx (1)

32-35: Consider implementing retry logic for failed queries

The query configuration should include retry logic for network failures.

 const options = { count: 10 }
 const { me } = useLazyLoadQuery<ProfilesListQueryType>(ProfilesListQuery, options, {
   fetchPolicy: 'store-and-network',
+  networkCacheConfig: {
+    retryAttempts: 3,
+    retryDelay: 1000,
+  },
 })
packages/components/schema.graphql (2)

1039-1043: Consider adding input validation constraints

While the input type includes the necessary fields, consider adding these validations:

  • String length limits for name and urlPath
  • URL path format validation
  • Reserved keywords check for urlPath

1562-1567: Consider adding more query options for organizations

While the basic ID lookup is implemented, consider adding these query capabilities:

  • Lookup by urlPath
  • Filter by creation date
  • Search by name
packages/components/modules/profiles/ProfilePopover/ProfilesList/ProfileMenuItem/index.tsx (1)

6-6: LGTM! Good architectural improvement

The switch from readInlineData to useFragment is a positive change as it provides better integration with React's rendering lifecycle and enables more efficient data updates.

Also applies to: 8-8

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 061e2518001a034fd14cf225ffdd94ea0ea548c2 and c2b63e2d6ef538a2d8e1fac9ccbcb9967f57c188.

⛔ Files ignored due to path filters (7)
  • packages/components/__generated__/OrganizationCreateMutation.graphql.ts is excluded by !**/__generated__/**
  • packages/components/__generated__/ProfileItemInlineFragment.graphql.ts is excluded by !**/__generated__/**
  • packages/components/__generated__/ProfilesListFragment.graphql.ts is excluded by !**/__generated__/**
  • packages/components/__generated__/ProfilesListQuery.graphql.ts is excluded by !**/__generated__/**
  • packages/components/__generated__/UserProfileQuery.graphql.ts is excluded by !**/__generated__/**
  • packages/components/__generated__/profilesListRefetchable.graphql.ts is excluded by !**/__generated__/**
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (25)
  • packages/components/modules/messages/MessagesList/MessagesGroup/MessageItem/index.tsx (0 hunks)
  • packages/components/modules/navigations/Header/AccountMenu/AccountPopover/__tests__/AccountPopover.cy.tsx (2 hunks)
  • packages/components/modules/navigations/Header/AccountMenu/AccountPopover/__tests__/__mocks__/profiles.ts (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/index.tsx (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/styled.tsx (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/types.ts (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/constants.ts (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/index.tsx (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/styled.tsx (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/types.ts (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/ProfileMenuItem/index.tsx (2 hunks)
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/ProfileMenuItem/types.ts (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/index.tsx (2 hunks)
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/types.ts (2 hunks)
  • packages/components/modules/profiles/context/CurrentProfileProvider/__tests__/CurrentProfileProvider.test.tsx (1 hunks)
  • packages/components/modules/profiles/context/CurrentProfileProvider/__tests__/__mock__/profiles.ts (1 hunks)
  • packages/components/modules/profiles/context/CurrentProfileProvider/__tests__/__utils__/TestComponent/index.tsx (2 hunks)
  • packages/components/modules/profiles/context/CurrentProfileProvider/index.tsx (5 hunks)
  • packages/components/modules/profiles/context/CurrentProfileProvider/types.ts (1 hunks)
  • packages/components/modules/profiles/graphql/mutations/OrganizationCreate.ts (1 hunks)
  • packages/components/modules/profiles/graphql/queries/ProfileItemInline.ts (0 hunks)
  • packages/components/modules/profiles/graphql/queries/ProfilesList.ts (1 hunks)
  • packages/components/modules/profiles/graphql/queries/UserProfile.ts (1 hunks)
  • packages/components/package.json (1 hunks)
  • packages/components/schema.graphql (61 hunks)
💤 Files with no reviewable changes (2)
  • packages/components/modules/profiles/graphql/queries/ProfileItemInline.ts
  • packages/components/modules/messages/MessagesList/MessagesGroup/MessageItem/index.tsx
✅ Files skipped from review due to trivial changes (2)
  • packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/styled.tsx
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/styled.tsx
🚧 Files skipped from review as they are similar to previous changes (15)
  • packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/types.ts
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/types.ts
  • packages/components/modules/profiles/context/CurrentProfileProvider/tests/CurrentProfileProvider.test.tsx
  • packages/components/modules/profiles/graphql/queries/UserProfile.ts
  • packages/components/modules/profiles/context/CurrentProfileProvider/types.ts
  • packages/components/modules/navigations/Header/AccountMenu/AccountPopover/tests/mocks/profiles.ts
  • packages/components/modules/profiles/context/CurrentProfileProvider/tests/mock/profiles.ts
  • packages/components/modules/profiles/context/CurrentProfileProvider/tests/utils/TestComponent/index.tsx
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/constants.ts
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/types.ts
  • packages/components/modules/profiles/graphql/mutations/OrganizationCreate.ts
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/ProfileMenuItem/types.ts
  • packages/components/modules/profiles/context/CurrentProfileProvider/index.tsx
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/index.tsx
  • packages/components/modules/profiles/graphql/queries/ProfilesList.ts
🔇 Additional comments (13)
packages/components/package.json (3)

24-113: Overall dependency updates look good with some considerations

The dependency updates align well with the PR objectives for implementing organization profiles. The changes include necessary packages for form handling, URL manipulation, and UI components. However, please consider the following:

  1. The Next.js canary version should be replaced with a stable release
  2. Verify React 18.3.1 compatibility across all dependencies
  3. Ensure proper migration of MSW v2 in tests
  4. Test the slugify implementation thoroughly for URL path generation

The remaining dependency updates look appropriate and include important security updates.


39-39: Verify slugify implementation for URL path handling

The new slugify dependency will be used for generating URL-friendly organization paths. Let's verify its implementation and security considerations.

✅ Verification successful

Slugify implementation is secure and properly used for URL path generation

The verification shows that:

  • Slugify is used only in AddProfileModal component to generate URL-friendly paths from organization names
  • The implementation correctly converts names to lowercase before slugification
  • No security vulnerabilities were found for the slugify package
  • Version 1.6.6 is consistently used across the project
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for slugify usage in the codebase
rg -A 3 "slugify" 

# Check for known vulnerabilities
gh api graphql -f query='
{
  securityVulnerabilities(first: 5, ecosystem: NPM, package: "slugify") {
    nodes {
      advisory {
        summary
        severity
        publishedAt
      }
      vulnerableVersionRange
      firstPatchedVersion {
        identifier
      }
    }
  }
}'

Length of output: 2557


99-100: Verify MSW v2 migration impact

The upgrade to MSW v2 (Mock Service Worker) includes breaking changes that might affect existing tests and Storybook mocks. Please ensure all mock handlers are updated to the new syntax.

packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/index.tsx (2)

21-29: Add ARIA attributes for improved accessibility

The menu item that triggers the modal should include accessibility attributes for better screen reader support.

 <MenuItem
   tabIndex={0}
   component={ButtonBase}
   sx={{ justifyContent: 'space-between' }}
   onClick={() => setOpen(true)}
+  aria-haspopup="dialog"
+  aria-expanded={open}
+  aria-label="Add new organization profile"
 >

31-31: Verify modal close handling

The modal's close functionality should handle both successful and unsuccessful profile creation scenarios.

packages/components/modules/profiles/ProfilePopover/ProfilesList/index.tsx (1)

40-48: ⚠️ Potential issue

Add error handling for profile switching

The profile switching logic should handle potential errors and provide appropriate feedback to users.

 const handleProfileChange = (profile: ProfileItemFragment$data) => {
   if (currentProfile?.id !== profile?.id) {
+    try {
       setCurrentProfile({
         profile,
       })
       sendToast(`Switched to ${profile?.name}`)
       handleCloseSubmenu()
+    } catch (error) {
+      sendToast('Failed to switch profile. Please try again.', { type: 'error' })
+      console.error('Profile switch failed:', error)
+    }
   }
 }
packages/components/modules/navigations/Header/AccountMenu/AccountPopover/__tests__/AccountPopover.cy.tsx (3)

67-72: LGTM: Profile list setup is well structured

The constant profilesListLength and mock data setup properly handle the profile list configuration, which is essential for testing organization profiles in the dropdown.


80-82: LGTM: Profile list assertions are comprehensive

The test correctly verifies both the organization name and URL path for each profile in the list, using the GraphQL edges pattern.


85-87: LGTM: Profile switching test is well implemented

The test properly verifies the profile switching functionality by selecting the last profile and checking for the success toast notification.

packages/components/schema.graphql (3)

1029-1037: LGTM: Organization type is well defined

The Organization type correctly implements the Node interface and includes all necessary fields for the organization profile feature.


1045-1054: LGTM: Create payload structure is comprehensive

The payload type properly handles both success and error cases, returning the created organization and its associated profile.


1328-1328: LGTM: Profile-Organization relationship is well defined

The organization field in the Profile type correctly establishes the relationship between profiles and organizations.

packages/components/modules/profiles/ProfilePopover/ProfilesList/ProfileMenuItem/index.tsx (1)

27-27: Verify URL path validation for organizations

Since organization profiles will have specific URL path requirements (e.g., airtab.com/BigAlex), ensure that proper validation is implemented for the urlPath field.

✅ Verification successful

URL path validation is properly implemented

The codebase has appropriate validation for organization URL paths through a schema that:

  • Enforces a slug format using regex pattern /^[a-z0-9]+(?:-[a-z0-9]+)*$/
  • Allows only lowercase letters, numbers, and hyphens
  • Requires a minimum of 3 characters and maximum of 500 characters
  • Validates the format using Zod schema validation
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for URL path validation implementation

# Look for URL validation logic
rg -A 10 'urlPath.*validation|validateUrl|validatePath' 'packages/components/**/*.ts{,x}'

# Check for related test cases
rg -A 5 'test.*url.*path|describe.*url.*path' 'packages/**/__tests__/**/*.ts{,x}'

Length of output: 321


Script:

#!/bin/bash
# Let's try a broader search for URL-related validation patterns
# Search for URL validation patterns in the codebase
rg -A 5 "urlPath|validateUrl|validatePath"

# Search for any URL validation utilities or helpers
rg -A 5 "isValidUrl|isValid.*Url|url.*validation"

# Look for URL-related types or interfaces
ast-grep --pattern 'interface $_ {
  $$$
  urlPath
  $$$
}'

# Check for URL-related test files
fd "url|profile" -e test.ts -e test.tsx

Length of output: 38997


Script:

#!/bin/bash
# Let's check for URL validation implementation in the AddProfileModal
# since we found it handles urlPath with validation schema

# Look for the validation schema and regex pattern
rg -A 10 "slugRegex|schema.*urlPath" "packages/components/modules/profiles/ProfilePopover/AddProfileModal/"

# Check for any URL-related constants or validation patterns
rg -A 5 "const.*url|const.*pattern|const.*regex" "packages/components/modules/profiles/ProfilePopover/"

Length of output: 2371

Comment on lines 49 to 50
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

React 18.3.1 version appears to be incorrect

React 18.3.1 does not exist as a valid React version. The latest stable version of React is 18.2.x. Additionally, most packages in the monorepo use catalog references (catalog:react18), suggesting a standardized version management system. The specified version 18.3.1 in @baseapp-frontend/components appears to be a mistake.

  • @baseapp-frontend/components should align with other packages using catalog:react18
  • Current dependencies show compatibility with Material UI 5.15.19 and React Relay 16.2.0, which are designed for React 18.2.x
🔗 Analysis chain

Verify React 18.3.1 compatibility with other dependencies

The update to React 18.3.1 is significant. Please ensure all dependencies, especially the @mui/* packages and react-relay, are compatible with this version.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check package.json files for React version requirements
fd package.json --exec jq -r 'select(.peerDependencies != null) | select(.peerDependencies.react != null) | "\(.name): React \(.peerDependencies.react)"'

Length of output: 550


Script:

#!/bin/bash
# Check the actual React version in the workspace root package.json
jq -r '.dependencies.react, .dependencies["react-dom"]' package.json

# Check for any direct React dependencies in other packages
fd package.json --exec jq -r 'select(.dependencies != null) | select(.dependencies.react != null or .dependencies["react-dom"] != null) | "\(.name): React \(.dependencies.react // "not specified"), React DOM \(.dependencies["react-dom"] // "not specified")"'

# Check for MUI and react-relay peer dependencies
fd package.json --exec jq -r 'select(.dependencies != null) | select(.dependencies["@mui/material"] != null or .dependencies["react-relay"] != null) | "\(.name): @mui/material \(.dependencies["@mui/material"] // "not found"), react-relay \(.dependencies["react-relay"] // "not found")"'

Length of output: 1045

Comment on lines +96 to +114
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider adding keyboard navigation support to Virtuoso list

The virtualized list should be keyboard accessible for better accessibility.

 <Virtuoso
   data={profiles}
   style={{ height: 'auto', minHeight }}
   itemContent={(index, profile) => renderProfileItem(profile, index)}
+  tabIndex={0}
+  onKeyDown={(e) => {
+    if (e.key === 'ArrowDown') {
+      // Focus next item
+    } else if (e.key === 'ArrowUp') {
+      // Focus previous item
+    }
+  }}
   components={{
     Footer: renderVirtuosoLoadingState,
   }}
   endReached={() => {
     if (hasNext) {
       loadNext(10)
     }
   }}
 />

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

Comment on lines 20 to 25
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Profile fragment is missing organization-specific fields

The fragment definition only includes basic profile fields (id, name, image, urlPath) but lacks organization-specific fields that would be important for proper organization profile handling, such as:

  • status (available in schema)
  • biography (available in schema)
  • owner (available in schema)

These fields might be necessary for proper organization profile display and management.

🔗 Analysis chain

Verify profile type handling for organizations

The null check is a good addition. However, since this component will now handle organization profiles as well, we should verify the fragment definition includes all necessary fields for both user and organization profiles.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify ProfileItemFragment definition includes organization-specific fields

# Check the fragment definition
ast-grep --pattern 'fragment ProfileItemFragment on Profile {
  $$$
}'

# Check for organization-specific field usage
rg -A 5 'ProfileItemFragment' 'packages/components/**/graphql/**/*.ts'

Length of output: 222


Script:

#!/bin/bash
# Let's try different approaches to locate the fragment definition and its usage

# Find files containing ProfileItemFragment
rg "ProfileItemFragment"

# Find any GraphQL fragment definitions in the codebase
fd ".graphql$|.gql$" .

# Look for TypeScript/TSX files that might contain the fragment
rg "fragment.*Profile.*on.*Profile" -t ts -t tsx

Length of output: 14597


Script:

#!/bin/bash
# Let's examine the fragment definition and schema

# Check the fragment definition in ProfileItem.ts
cat packages/components/modules/profiles/graphql/queries/ProfileItem.ts

# Check Profile type definition in schema
cat packages/components/schema.graphql | grep -A 20 "type Profile"

Length of output: 3077

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (3)
packages/components/modules/profiles/graphql/queries/AddProfilePopover.ts (1)

3-10: Add documentation for the GraphQL query

While the query is well-structured, adding documentation would improve maintainability. Consider adding:

  • A description of what the query does
  • An explanation of the permission being checked
  • The purpose of retrieving the user ID

Add JSDoc comment above the query:

+/**
+ * Fetches current user's permission to add organizations and their ID.
+ * Used by the AddProfileMenuItem component to conditionally render the add organization option.
+ */
export const AddProfilePopoverUserQuery = graphql`
packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/index.tsx (2)

16-18: Consider more descriptive prop naming

The prop name addNewProfileLabel could be more specific since it's specifically for organizations now.

-  addNewProfileLabel = 'New organization',
+  addNewOrganizationLabel = 'New organization',

42-45: Add loading fallback for Suspense

The Suspense wrapper should include a loading fallback to improve user experience during data fetching.

-  <Suspense>
+  <Suspense fallback={<MenuItem disabled>Loading...</MenuItem>}>
     <AddProfileMenuItem {...props} />
   </Suspense>
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between c2b63e2d6ef538a2d8e1fac9ccbcb9967f57c188 and e45df93ad6b3307eacff012408ace7399feb1f47.

⛔ Files ignored due to path filters (2)
  • packages/components/__generated__/AddProfilePopoverUserQuery.graphql.ts is excluded by !**/__generated__/**
  • packages/components/__generated__/OrganizationCreateMutation.graphql.ts is excluded by !**/__generated__/**
📒 Files selected for processing (5)
  • packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/index.tsx (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/index.tsx (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/types.ts (1 hunks)
  • packages/components/modules/profiles/graphql/mutations/OrganizationCreate.ts (1 hunks)
  • packages/components/modules/profiles/graphql/queries/AddProfilePopover.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/components/modules/profiles/graphql/mutations/OrganizationCreate.ts
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/types.ts
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/index.tsx
🔇 Additional comments (1)
packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/index.tsx (1)

27-35: Add ARIA attributes for improved accessibility

The menu item that triggers the modal should include accessibility attributes.

 <MenuItem
   tabIndex={0}
   component={ButtonBase}
   sx={{ justifyContent: 'space-between' }}
   onClick={() => setOpen(true)}
+  aria-haspopup="dialog"
+  aria-expanded={open}
+  aria-label="Add new organization profile"
 >

Comment on lines +19 to +22
Copy link

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 the GraphQL query

The component should handle potential query errors gracefully.

-  const { me } = useLazyLoadQuery<AddProfilePopoverUserQueryType>(AddProfilePopoverUserQuery, {})
+  const { me } = useLazyLoadQuery<AddProfilePopoverUserQueryType>(
+    AddProfilePopoverUserQuery,
+    {},
+    {
+      fetchPolicy: 'store-or-network',
+      onError: (error) => {
+        console.error('Failed to fetch user permissions:', error);
+        // Consider showing an error state instead of returning null
+      }
+    }
+  )
📝 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 [open, setOpen] = useState(false)
const { me } = useLazyLoadQuery<AddProfilePopoverUserQueryType>(AddProfilePopoverUserQuery, {})
if (!me?.canAdd) return null
const [open, setOpen] = useState(false)
const { me } = useLazyLoadQuery<AddProfilePopoverUserQueryType>(
AddProfilePopoverUserQuery,
{},
{
fetchPolicy: 'store-or-network',
onError: (error) => {
console.error('Failed to fetch user permissions:', error);
// Consider showing an error state instead of returning null
}
}
)
if (!me?.canAdd) return null

@priscilladeroode priscilladeroode force-pushed the BA-1832-fe-multiple-profiles-create-organization-profile branch from e45df93 to 460bb6d Compare December 5, 2024 18:34
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (1)
packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/index.tsx (1)

10-10: Consider using path aliases for generated GraphQL types

The deep import path ../../../../__generated__/AddProfilePopoverUserQuery.graphql could be simplified using path aliases to improve maintainability.

Consider configuring path aliases in your TypeScript config:

- import { AddProfilePopoverUserQuery as AddProfilePopoverUserQueryType } from '../../../../__generated__/AddProfilePopoverUserQuery.graphql'
+ import { AddProfilePopoverUserQuery as AddProfilePopoverUserQueryType } from '@generated/AddProfilePopoverUserQuery.graphql'
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between e45df93ad6b3307eacff012408ace7399feb1f47 and 460bb6d3a17c88e894cb5fbae4d837d3d1f6f8a7.

⛔ Files ignored due to path filters (2)
  • packages/components/__generated__/AddProfilePopoverUserQuery.graphql.ts is excluded by !**/__generated__/**
  • packages/components/__generated__/OrganizationCreateMutation.graphql.ts is excluded by !**/__generated__/**
📒 Files selected for processing (5)
  • packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/index.tsx (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/index.tsx (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/types.ts (1 hunks)
  • packages/components/modules/profiles/graphql/mutations/OrganizationCreate.ts (1 hunks)
  • packages/components/modules/profiles/graphql/queries/AddProfilePopover.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/components/modules/profiles/graphql/queries/AddProfilePopover.ts
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/types.ts
  • packages/components/modules/profiles/graphql/mutations/OrganizationCreate.ts
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/index.tsx
🔇 Additional comments (4)
packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/index.tsx (4)

14-18: LGTM! Well-structured component definition

The component is well-typed and the props are properly defined with sensible defaults. The CreateProfileModal prop allows for good component composition and testing.


37-37: Form validation implementation is partially complete but missing uniqueness checks

The current implementation needs to ensure all validation requirements from PR objectives are met.


19-22: ⚠️ Potential issue

Add error handling for the GraphQL query

The component should handle potential query errors gracefully.

-  const { me } = useLazyLoadQuery<AddProfilePopoverUserQueryType>(AddProfilePopoverUserQuery, {})
+  const { me } = useLazyLoadQuery<AddProfilePopoverUserQueryType>(
+    AddProfilePopoverUserQuery,
+    {},
+    {
+      fetchPolicy: 'store-or-network',
+      onError: (error) => {
+        console.error('Failed to fetch user permissions:', error);
+        // Consider showing an error state instead of returning null
+      }
+    }
+  )

27-35: 🛠️ Refactor suggestion

Add ARIA attributes for improved accessibility

The menu item that triggers the modal should include accessibility attributes.

 <MenuItem
   tabIndex={0}
   component={ButtonBase}
   sx={{ justifyContent: 'space-between' }}
   onClick={() => setOpen(true)}
+  aria-haspopup="dialog"
+  aria-expanded={open}
+  aria-label="Add new organization profile"
 >

Comment on lines +42 to +45
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Based on the search results, I can generate the final response as we have sufficient information about loading state implementations across the codebase. The search reveals that other similar components consistently use the LoadingState component from the design system as a fallback prop in their Suspense wrappers.

Add loading state fallback to Suspense wrapper

The Suspense wrapper should include a fallback prop using the LoadingState component from the design system, similar to other components in the codebase:

const AddProfileMenuItemSuspended: FC<AddProfileMenuItemProps> = (props) => (
  <Suspense fallback={<LoadingState />}>
    <AddProfileMenuItem {...props} />
  </Suspense>
)
🔗 Analysis chain

Verify loading state handling

While the Suspense wrapper is correctly implemented, ensure that a proper loading state is shown to users during data fetching.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for loading state implementations in similar components
# Expected: Find patterns of loading state handling with Suspense

# Search for Suspense fallback props
rg -A 3 "Suspense.*fallback"

# Search for loading components or skeletons
rg -A 3 "Skeleton|LoadingState|Spinner"

Length of output: 32824

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (4)
packages/components/package.json (1)

24-42: Consider maintaining catalog references for version consistency

Moving from catalog references to specific versions may lead to version inconsistencies across the monorepo. Catalog references help ensure that all packages use the same versions of shared dependencies.

Consider:

  1. Keeping catalog references for shared dependencies
  2. Managing versions centrally through the catalog
  3. Only specify exact versions for package-specific dependencies
packages/components/modules/navigations/Header/AccountMenu/AccountPopover/__tests__/AccountPopover.cy.tsx (3)

71-87: Consider adding operation payload verification.

While the test correctly handles the profile switching flow, it could be more robust by verifying the operation payload before resolving it.

Consider adding assertions for the operation payload:

 .then(() => {
+  const operation = environment.mock.getMostRecentOperation()
+  expect(operation.request.variables).to.deep.equal({
+    first: 10,  // or whatever the expected limit is
+    after: null
+  })
   resolveMostRecentOperation({ data: mockAddProfileData() })
 })

93-97: Remove unnecessary empty lines.

The code block could be more concise by removing the empty lines between the chain operations.

 cy.findByRole('menuitem', { name: /switch profile/i })
-  .click()
-  .then(() => {
-    resolveMostRecentOperation({ data: profileListData })
-  })
+  .click().then(() => {
+    resolveMostRecentOperation({ data: profileListData })
+  })

171-180: Consider extracting repeated profile list resolution into a custom command.

The pattern of clicking and resolving operations is repeated throughout the tests. This could be extracted into a custom Cypress command for better maintainability.

Create a custom command in your cypress/support/commands.ts:

Cypress.Commands.add('openProfileList', (environment, profileListData) => {
  cy.findByRole('menuitem', { name: /change profile/i })
    .click()
    .then(() => {
      resolveMostRecentOperation({ data: mockAddProfileData() })
    })
    .then(() => {
      resolveMostRecentOperation({ data: profileListData })
    })
})

Then use it in your tests:

-cy.findByRole('menuitem', { name: /change profile/i })
-  .click()
-  .then(() => {
-    resolveMostRecentOperation({ data: mockAddProfileData() })
-  })
-  .then(() => {
-    resolveMostRecentOperation({ data: profileListData })
-  })
+cy.openProfileList(environment, profileListData)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 460bb6d3a17c88e894cb5fbae4d837d3d1f6f8a7 and 1cf35fea7897d54f84359d3e5c0fbaa737d4f9bb.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (3)
  • packages/components/modules/navigations/Header/AccountMenu/AccountPopover/__tests__/AccountPopover.cy.tsx (5 hunks)
  • packages/components/modules/navigations/Header/AccountMenu/AccountPopover/__tests__/__mocks__/profiles.ts (2 hunks)
  • packages/components/package.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/components/modules/navigations/Header/AccountMenu/AccountPopover/tests/mocks/profiles.ts
🔇 Additional comments (6)
packages/components/package.json (5)

Line range hint 12-14: LGTM: Script naming follows conventions

The relay-related script names have been updated to use colons as separators, which is a common npm script naming convention.


39-39: LGTM: Slugify addition aligns with requirements

The addition of slugify (^1.6.6) aligns with the PR objectives for handling URL path generation for organization profiles.


53-113: LGTM: Dev dependencies properly updated

The development dependencies have been updated to stable versions with proper compatibility between related packages (e.g., Storybook ecosystem, testing libraries).


34-34: ⚠️ Potential issue

Critical: Update Next.js from canary to stable version

Using Next.js canary version (14.3.0-canary.24) in production is risky as it may contain unstable features.

Consider updating to the latest stable version of Next.js.


49-50: ⚠️ Potential issue

Critical: Invalid React version specified

React 18.3.1 is not a valid version. The latest stable version is 18.2.x.

Update the React and React DOM peer dependencies to use version 18.2.x.

packages/components/modules/navigations/Header/AccountMenu/AccountPopover/__tests__/AccountPopover.cy.tsx (1)

7-11: LGTM: Import statements are well-organized.

The new imports for mock data functions are properly structured and align with the test requirements.

Comment on lines 154 to 144
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove debug console.log statements.

Debug console.log statements should not be committed to the codebase. These statements don't provide any value to the tests and should be removed.

-    console.log('ITEM0')
-
     cy.findByRole('button').click()
-    console.log('ITEM1')
     cy.findByRole('menuitem', { name: /close/i }).should('exist')

Also applies to: 181-181

@priscilladeroode priscilladeroode force-pushed the BA-1832-fe-multiple-profiles-create-organization-profile branch from 1cf35fe to 2fdc5a6 Compare December 6, 2024 14:31
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (4)
packages/components/package.json (2)

Line range hint 11-14: Maintain consistent script naming convention

The relay-related scripts use inconsistent naming patterns:

  • relay uses no prefix
  • relay-download-schema and relay-update-schema use hyphenated names

Consider updating all relay scripts to use the same colon-based naming convention:

  "scripts": {
-   "relay": "relay-compiler",
-   "relay-download-schema": "dotenv -- bash -c 'get-graphql-schema \"$NEXT_PUBLIC_RELAY_ENDPOINT\" > schema.graphql'",
-   "relay-update-schema": "pnpm relay-download-schema && pnpm relay",
+   "relay:compile": "relay-compiler",
+   "relay:download-schema": "dotenv -- bash -c 'get-graphql-schema \"$NEXT_PUBLIC_RELAY_ENDPOINT\" > schema.graphql'",
+   "relay:update-schema": "pnpm relay:download-schema && pnpm relay:compile",

72-75: Consider enhancing test configuration

The package includes both Jest and Testing Library setups. To ensure comprehensive testing:

  1. Consider adding test coverage thresholds
  2. Add configuration for component integration tests

Add the following to your Jest configuration:

module.exports = {
  coverageThreshold: {
    global: {
      branches: 80,
      functions: 80,
      lines: 80,
      statements: 80
    }
  }
}

Also applies to: 97-98

packages/components/modules/navigations/Header/AccountMenu/AccountPopover/__tests__/__mocks__/profiles.ts (1)

23-175: Consider parameterizing the mock profiles list.

The current implementation hardcodes 10 profiles with fixed data. Consider making it more flexible:

  1. Accept a size parameter to control the number of profiles
  2. Generate dynamic IDs and cursors
  3. Allow customization of pageInfo
-export const mockProfilesListFactory = (userProfile: any) => {
+export const mockProfilesListFactory = (
+  userProfile: any,
+  size: number = 10,
+  hasNextPage: boolean = true
+) => {
   return {
     data: {
       me: {
         id: 'user-profile-1',
         profiles: {
-          edges: [/* ... hardcoded profiles ... */],
+          edges: Array.from({ length: size }, (_, i) => ({
+            cursor: Buffer.from(`arrayconnection:${i}`).toString('base64'),
+            node: i === 0 ? {
+              ...userProfile,
+              __typename: 'Profile',
+            } : {
+              id: `Profile${i + 1}`,
+              name: `Profile ${i + 1}`,
+              image: {
+                url: faker.image.avatar(),
+              },
+              urlPath: {
+                path: `profile-${i + 1}`,
+                id: `URLPath${i + 1}`,
+              },
+              __typename: 'Profile',
+            },
+          })),
           pageInfo: {
-            hasNextPage: true,
+            hasNextPage,
             endCursor: 'YXJyYXljb25uZWN0aW9uOjk=',
           },
packages/components/modules/navigations/Header/AccountMenu/AccountPopover/__tests__/AccountPopover.cy.tsx (1)

71-77: Improve test readability by extracting repeated test setup.

The profile list resolution setup is repeated multiple times. Consider extracting it into a helper function to improve maintainability.

const setupProfileList = (environment) => {
  const profileListData = mockProfilesListFactory(mockUserProfileData.data.me.profile)
  return cy
    .findByRole('menuitem', { name: /switch profile/i })
    .click()
    .then(() => {
      resolveMostRecentOperation({ data: mockAddProfileData() })
    })
    .then(() => {
      resolveMostRecentOperation({ data: profileListData })
    })
    .then(() => profileListData)
}

Also applies to: 93-97, 113-117

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 1cf35fea7897d54f84359d3e5c0fbaa737d4f9bb and 2fdc5a64cee0da3afdb99073120d9c0b6e3a2978.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (3)
  • packages/components/modules/navigations/Header/AccountMenu/AccountPopover/__tests__/AccountPopover.cy.tsx (4 hunks)
  • packages/components/modules/navigations/Header/AccountMenu/AccountPopover/__tests__/__mocks__/profiles.ts (2 hunks)
  • packages/components/package.json (1 hunks)
🔇 Additional comments (7)
packages/components/package.json (3)

34-34: Update Next.js from canary version to stable release

Using a canary version (14.3.0-canary.24) in production could lead to stability issues.


49-50: React 18.3.1 version appears to be incorrect

React 18.3.1 is not a valid React version. The latest stable version is 18.2.x.


39-39: Verify slugify implementation for URL path validation

The addition of slugify aligns with the PR objective of handling organization URL paths. However, ensure consistent usage across the codebase.

✅ Verification successful

slugify usage is properly scoped and implemented

The package is correctly used in ProfilePopover/AddProfileModal component to generate URL-friendly paths from profile names. The implementation is consistent and localized to this specific use case, with no other instances requiring similar URL path validation.

  • Used in: packages/components/modules/profiles/ProfilePopover/AddProfileModal/index.tsx
  • Implementation: Converts profile names to URL-safe strings using slugify(form.getValues('name').toLocaleLowerCase())
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for slugify usage in the codebase
rg "slugify" --type ts --type tsx

Length of output: 61


Script:

#!/bin/bash
# Search for slugify usage in the codebase with correct ripgrep options
rg "slugify" -t js -t jsx -t ts

# Also check for any files that might contain slugify in their name
fd slugify

Length of output: 72


Script:

#!/bin/bash
# Search for slugify usage with simpler ripgrep pattern, without file type restrictions
rg "slugify"

# Look for package.json files that might reference slugify
fd "package.json" --exec grep -l "slugify" {}

Length of output: 638

packages/components/modules/navigations/Header/AccountMenu/AccountPopover/__tests__/__mocks__/profiles.ts (2)

191-201: LGTM: New mock for organization creation feature.

The mockAddProfileData function correctly models the GraphQL response for checking if a user can create an organization profile.


8-8: Consider handling null image URLs consistently.

While most mock profiles have avatar URLs, the ninth profile has a null image. This inconsistency might not adequately test the component's handling of missing images.

Also applies to: 147-147

✅ Verification successful

The null image case is properly handled in the component

The component already implements proper null-safe handling through optional chaining in the avatar source:

src={profile?.image?.url ?? user?.avatar?.small}

This implementation:

  • Safely handles null/undefined image objects
  • Provides a fallback to user's avatar when profile image is not available
  • Makes the null image test case in the mock data valuable for testing this fallback behavior
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if the component handles null images properly
ast-grep --pattern 'image: null' # Find other instances of null images
ast-grep --pattern 'image?.url' # Check for null-safe access

Length of output: 179


Script:

#!/bin/bash
# Let's check how avatar/image is handled in the AccountPopover component
ast-grep --pattern 'avatar' -l typescript
rg -A 2 -B 2 'avatar|image' packages/components/modules/navigations/Header/AccountMenu/AccountPopover/

Length of output: 13852

packages/components/modules/navigations/Header/AccountMenu/AccountPopover/__tests__/AccountPopover.cy.tsx (2)

179-179: Remove debug console.log statement.

Debug console.log statements should be removed from test files.

-    console.log('ITEM1')

82-84: Consider testing edge cases in profile list.

The test verifies the presence of profile names and URLs but doesn't test:

  1. Profile switching with null images
  2. Very long profile names
  3. Special characters in URLs

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add test coverage for organization creation feature.

While the mocks support the new organization creation feature, there are no tests verifying:

  1. The "Add organization" button functionality
  2. Form validation for organization name and URL
  3. Error handling for duplicate usernames
  4. Success toast for organization creation

This coverage is important given the PR objectives.

Would you like me to help generate the missing test cases?

@priscilladeroode priscilladeroode force-pushed the BA-1832-fe-multiple-profiles-create-organization-profile branch from 2fdc5a6 to 802b834 Compare December 10, 2024 14:46
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Outside diff range and nitpick comments (12)
package.json (1)

49-53: LGTM! Consider documenting the reason for version resolutions.

The addition of version resolutions for wrap-ansi and string-width looks good. However, it would be helpful to add a comment explaining why these specific versions are required.

  "packageManager": "pnpm@9.12.0",
  "resolutions": {
+   // Fixed versions to resolve conflicts with dependency X
    "wrap-ansi": "7.0.0",
    "string-width": "4.1.0"
  }
packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/index.tsx (3)

19-20: Consider using a more descriptive state variable name.

The open state variable could be more descriptive to better reflect its purpose.

-  const [open, setOpen] = useState(false)
+  const [isModalOpen, setIsModalOpen] = useState(false)

22-22: Add error state handling for missing permissions.

The current implementation silently returns null when canAdd permission is missing. Consider showing a message or disabling the menu item instead.

-  if (!me?.canAdd) return null
+  if (!me?.canAdd) {
+    return (
+      <MenuItem disabled title="You don't have permission to add organizations">
+        {addNewProfileLabel}
+      </MenuItem>
+    )
+  }

42-45: Add error boundary for better error handling.

Consider wrapping the suspended component with an error boundary to gracefully handle rendering errors.

 const AddProfileMenuItemSuspended: FC<AddProfileMenuItemProps> = (props) => (
-  <Suspense>
+  <ErrorBoundary fallback={<ErrorMessage />}>
+    <Suspense>
       <AddProfileMenuItem {...props} />
-  </Suspense>
+    </Suspense>
+  </ErrorBoundary>
 )
packages/components/modules/profiles/ProfilePopover/CurrentProfile/index.tsx (2)

18-18: Remove redundant optional chaining

The optional chaining operator (?.) is unnecessary here since there's already a null check at line 13 that returns early if profile is null. This creates redundant null checking.

-        src={profile?.image ?? ''}
+        src={profile.image ?? ''}

Line range hint 27-31: Simplify conditional rendering with early return

Similar to the previous comment, the optional chaining operator is redundant here due to the early return. The code can be simplified while maintaining the same behavior.

-        {profile?.urlPath && (
+        {profile.urlPath && (
           <Typography variant="body2" sx={{ color: 'text.secondary' }} noWrap>
             {profile.urlPath}
           </Typography>
         )}
packages/components/modules/profiles/ProfilePopover/ProfilesList/index.tsx (3)

33-35: Consider adding retry policy for network failures

The fetchPolicy is set to 'store-and-network', but there's no handling for network failures. This could lead to a degraded user experience if the network request fails.

 const { me } = useLazyLoadQuery<ProfilesListQueryType>(ProfilesListQuery, options, {
   fetchPolicy: 'store-and-network',
+  networkCacheConfig: {
+    retry: 2,
+    retryDelay: 1000,
+  },
 })

Line range hint 41-56: Add error boundary for image path construction

The image path construction could fail if the environment variable is not set or if the URL is malformed.

 const handleProfileChange = (profile: ProfileItemFragment$data) => {
   if (currentProfile?.id !== profile.id) {
-    // TODO: handle the absolute image path on the backend
-    const baseUrl = process.env.NEXT_PUBLIC_API_BASE_URL?.replace('/v1', '')
-    const absoluteImagePath = profile.image ? `${baseUrl}${profile.image?.url}` : null
+    let absoluteImagePath = null
+    try {
+      if (profile.image?.url) {
+        const baseUrl = process.env.NEXT_PUBLIC_API_BASE_URL
+        if (!baseUrl) {
+          throw new Error('API base URL not configured')
+        }
+        absoluteImagePath = `${baseUrl.replace('/v1', '')}${profile.image.url}`
+      }
+    } catch (error) {
+      console.error('Failed to construct image path:', error)
+      // Fallback to null, which is handled by the UI
+    }

     setCurrentProfile({
       id: profile.id,
       name: profile.name ?? null,
       image: absoluteImagePath,
       urlPath: profile.urlPath?.path ?? null,
     })
     sendToast(`Switched to ${profile.name}`)
     handleCloseSubmenu()
   }
 }

121-121: Add aria-live region for dynamic content updates

The list content updates dynamically during pagination, but screen readers might not announce these changes.

-<StyledList disablePadding maxHeight={listMaxHeight} aria-label="List of available profiles">
+<StyledList 
+  disablePadding 
+  maxHeight={listMaxHeight} 
+  aria-label="List of available profiles"
+  aria-live="polite"
+  aria-atomic="false"
+>
packages/components/modules/navigations/Header/AccountMenu/AccountPopover/__tests__/__mocks__/profiles.ts (2)

Line range hint 5-12: Add type safety to mock factory function

The mock factory function should enforce type safety to catch potential type mismatches early in tests.

+interface UserProfile {
+  id: string
+  name: string | null
+  image: string | null
+  urlPath: string | null
+}

-export const mockUserProfileFactory = (id: string = 'user-profile-1') => {
+export const mockUserProfileFactory = (id: string = 'user-profile-1'): UserProfile => {
   return {
     id,
     name: faker.person.fullName(),
     image: faker.image.avatar(),
     urlPath: faker.internet.url(),
   }
 }

145-145: Add test coverage for null image handling

Good practice to include a profile with null image in test data, but consider adding more edge cases.

Add more edge cases to test data:

 image: null,
+// Add profiles with:
+// - null name
+// - null urlPath
+// - empty string name
+// - malformed image URL
packages/components/modules/navigations/Header/AccountMenu/AccountPopover/__tests__/AccountPopover.cy.tsx (1)

76-78: Add assertions for profile accessibility attributes

The test should verify that profiles are keyboard accessible and have proper ARIA attributes.

 profileListData.data.me.profiles.edges.forEach((profile) => {
   cy.findAllByText(profile.node?.name!).should('exist')
   cy.findAllByText(profile.node?.urlPath?.path!).should('exist').scrollIntoView()
+  cy.findByLabelText(`Switch to ${profile.node?.name}`)
+    .should('have.attr', 'role', 'menuitem')
+    .should('have.attr', 'tabindex', '0')
 })
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 2fdc5a64cee0da3afdb99073120d9c0b6e3a2978 and 802b834b16abe078f9a0bf9f2822f278d142acd5.

⛔ Files ignored due to path filters (7)
  • packages/components/__generated__/AddProfilePopoverUserQuery.graphql.ts is excluded by !**/__generated__/**
  • packages/components/__generated__/OrganizationCreateMutation.graphql.ts is excluded by !**/__generated__/**
  • packages/components/__generated__/ProfilesListFragment.graphql.ts is excluded by !**/__generated__/**
  • packages/components/__generated__/ProfilesListQuery.graphql.ts is excluded by !**/__generated__/**
  • packages/components/__generated__/UserProfileQuery.graphql.ts is excluded by !**/__generated__/**
  • packages/components/__generated__/profilesListRefetchable.graphql.ts is excluded by !**/__generated__/**
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (23)
  • package.json (1 hunks)
  • packages/components/modules/messages/MessagesList/MessagesGroup/MessageItem/index.tsx (0 hunks)
  • packages/components/modules/navigations/Header/AccountMenu/AccountPopover/__tests__/AccountPopover.cy.tsx (4 hunks)
  • packages/components/modules/navigations/Header/AccountMenu/AccountPopover/__tests__/__mocks__/profiles.ts (3 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/index.tsx (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/styled.tsx (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/types.ts (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/constants.ts (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/index.tsx (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/styled.tsx (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/types.ts (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/CurrentProfile/index.tsx (2 hunks)
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/ProfileMenuItem/index.tsx (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/ProfileMenuItem/types.ts (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/index.tsx (3 hunks)
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/types.ts (2 hunks)
  • packages/components/modules/profiles/graphql/mutations/OrganizationCreate.ts (1 hunks)
  • packages/components/modules/profiles/graphql/queries/AddProfilePopover.ts (1 hunks)
  • packages/components/modules/profiles/graphql/queries/ProfilesList.ts (1 hunks)
  • packages/components/modules/profiles/graphql/queries/UserProfile.ts (1 hunks)
  • packages/components/package.json (1 hunks)
  • packages/components/schema.graphql (61 hunks)
  • pnpm-workspace.yaml (1 hunks)
💤 Files with no reviewable changes (1)
  • packages/components/modules/messages/MessagesList/MessagesGroup/MessageItem/index.tsx
🚧 Files skipped from review as they are similar to previous changes (13)
  • packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/styled.tsx
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/types.ts
  • packages/components/modules/profiles/graphql/queries/AddProfilePopover.ts
  • packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/types.ts
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/styled.tsx
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/constants.ts
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/ProfileMenuItem/index.tsx
  • packages/components/modules/profiles/graphql/mutations/OrganizationCreate.ts
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/ProfileMenuItem/types.ts
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/types.ts
  • packages/components/modules/profiles/graphql/queries/ProfilesList.ts
  • packages/components/modules/profiles/graphql/queries/UserProfile.ts
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/index.tsx
🔇 Additional comments (9)
pnpm-workspace.yaml (1)

75-75: LGTM! Cypress version update looks good.

The update to Cypress version ^13.16.1 is a safe change that includes bug fixes and improvements.

packages/components/package.json (2)

39-39: LGTM: Appropriate addition of slugify package

The addition of slugify is well-suited for generating URL-friendly paths for organization profiles.


Line range hint 12-13: LGTM: Improved script naming convention

The renaming of relay scripts to use colons follows better npm script naming conventions.

packages/components/schema.graphql (4)

1029-1069: LGTM: Well-structured Organization types

The Organization-related types are well-designed:

  • Organization type correctly implements the Node interface
  • OrganizationCreateInput includes essential fields
  • OrganizationCreatePayload follows the mutation response pattern

1328-1328: LGTM: Profile type properly extended

The addition of the organization field to Profile type creates a proper bidirectional relationship with Organization type.


1469-1482: LGTM: Well-designed ProfilesInterface

The ProfilesInterface is well-implemented with:

  • Proper pagination support
  • Name-based filtering
  • Clear and consistent field definitions

755-755: LGTM: Proper mutation and query implementation

The organization-related operations are well-implemented:

  • organizationCreate mutation follows the established mutation pattern
  • organization query provides proper ID-based lookup

Also applies to: 1562-1567

packages/components/modules/profiles/ProfilePopover/CurrentProfile/index.tsx (1)

Line range hint 13-33: Verify handling of organization profiles

Given that this PR implements organization profiles, we should verify that the component correctly handles both user and organization profile types.

✅ Verification successful

Based on the gathered information, I can now provide the final response:

The CurrentProfile component already handles both user and organization profiles correctly

The component is profile-type agnostic and renders the common fields (name, image, urlPath) that are present in the Profile type definition, regardless of whether it's a user or organization profile. The GraphQL schema shows that the Profile type is a unified type that can represent both user and organization profiles, with common fields being rendered by this component. No changes are needed to support organization profiles.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check how profile types are handled across the codebase

# Look for profile type definitions
echo "Checking for profile type definitions..."
ast-grep --pattern 'type $_ = {
  $$$
  type?: $_
  $$$
}'

# Check for profile type handling in components
echo "Checking for profile type-specific rendering..."
rg -A 5 'profile\.type|profileType'

Length of output: 294


Script:

#!/bin/bash
# Let's try a different approach to understand profile handling

# Check for profile-related types and interfaces
echo "Checking for profile-related type definitions..."
rg -A 10 "type\s+Profile|interface\s+Profile|type\s+.*Profile"

# Look for profile-related imports and usages
echo "Checking profile imports and context..."
rg -A 3 "import.*Profile|useProfile"

# Check the specific component's implementation
echo "Examining CurrentProfile component..."
fd "CurrentProfile" --exec cat {}

Length of output: 57981


Script:

#!/bin/bash
# Let's check the Profile type definition and organization-related fields

# Look for Profile type definition in schema
echo "Checking Profile type definition..."
rg -A 15 "type Profile.*Node" packages/components/schema.graphql

# Check for organization-related fields or types
echo "Checking organization-related types..."
rg "type.*Organization|organization" packages/components/schema.graphql

# Check CurrentProfile component implementation
echo "Checking CurrentProfile implementation..."
fd -t f "CurrentProfile/index.tsx" -x cat {}

Length of output: 1415


Script:

#!/bin/bash
# Let's check the specific CurrentProfile implementation and its usage

# Find and display CurrentProfile component
echo "Looking for CurrentProfile component..."
find . -type f -name "index.tsx" -path "*/CurrentProfile/*" -exec cat {} \;

# Check for organization type fields in Profile
echo "Checking Profile type fields..."
rg -A 20 "type Profile.*Node" packages/components/schema.graphql

# Check for profile type discriminators
echo "Checking profile type fields..."
rg "profileType|type:.*profile" --type ts

Length of output: 2419

packages/components/modules/profiles/ProfilePopover/ProfilesList/index.tsx (1)

102-114: 🛠️ Refactor suggestion

Add keyboard navigation support to Virtuoso list

The virtualized list should be keyboard accessible for better accessibility.

 <Virtuoso
   data={profiles}
   style={{ height: 'auto', minHeight }}
   itemContent={(index, profile) => renderProfileItem(profile, index)}
+  tabIndex={0}
+  onKeyDown={(e) => {
+    if (e.key === 'ArrowDown') {
+      e.preventDefault()
+      // Focus next item
+      const nextItem = document.querySelector(`[data-index="${e.currentTarget.dataset.index + 1}"]`)
+      nextItem?.querySelector('button')?.focus()
+    } else if (e.key === 'ArrowUp') {
+      e.preventDefault()
+      // Focus previous item
+      const prevItem = document.querySelector(`[data-index="${e.currentTarget.dataset.index - 1}"]`)
+      prevItem?.querySelector('button')?.focus()
+    }
+  }}
   components={{
     Footer: renderVirtuosoLoadingState,
   }}
   endReached={() => {
     if (hasNext) {
       loadNext(10)
     }
   }}
 />

Likely invalid or redundant comment.

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Critical version issues in dependencies

  1. The Next.js version 14.3.0-canary.24 is a pre-release version that may contain unstable features. Consider using the latest stable version instead.
  2. React version 18.3.1 is invalid as it doesn't exist. The latest stable version is 18.2.x. This could cause compatibility issues with other dependencies.

Apply this diff to fix the versions:

-    "next": "14.3.0-canary.24",
+    "next": "^14.1.0",
-    "react": "18.3.1",
-    "react-dom": "18.3.1"
+    "react": "^18.2.0",
+    "react-dom": "^18.2.0"

Also applies to: 49-50

Comment on lines +183 to +193
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add validation for canAdd field

The mockAddProfileData function should include test cases for when users cannot add profiles.

-export const mockAddProfileData = (userId: string = 'user-profile-1') => {
+export const mockAddProfileData = (
+  userId: string = 'user-profile-1',
+  canAdd: boolean = true,
+) => {
   return {
     data: {
       me: {
-        canAdd: true,
+        canAdd,
         id: userId,
         __typename: '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
export const mockAddProfileData = (userId: string = 'user-profile-1') => {
return {
data: {
me: {
canAdd: true,
id: userId,
__typename: 'User',
},
},
}
}
export const mockAddProfileData = (
userId: string = 'user-profile-1',
canAdd: boolean = true,
) => {
return {
data: {
me: {
canAdd,
id: userId,
__typename: 'User',
},
},
}
}

Comment on lines +160 to 168
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add test for pagination loading state

The test should verify that loading states are displayed during pagination.

 const profileListData = mockProfilesListFactory(mockUserProfileData)
 cy.findByRole('menuitem', { name: /change profile/i })
   .click()
   .then(() => {
     resolveMostRecentOperation({ data: mockAddProfileData() })
   })
   .then(() => {
     resolveMostRecentOperation({ data: profileListData })
   })
+
+// Test loading state during pagination
+cy.step('should show loading state during pagination')
+cy.scrollTo('bottom')
+cy.get('[role="progressbar"]').should('be.visible')
+cy.then(() => {
+  resolveMostRecentOperation({ 
+    data: { 
+      ...profileListData,
+      pageInfo: { hasNextPage: false }
+    }
+  })
+})
+cy.get('[role="progressbar"]').should('not.exist')

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

Comment on lines +69 to +71
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error case testing for profile creation

The test should verify error handling when profile creation fails.

 .then(() => {
   resolveMostRecentOperation({ data: mockAddProfileData() })
 })
+
+// Add error case test
+cy.step('should handle profile creation error')
+cy.findByRole('menuitem', { name: /switch profile/i })
+  .click()
+  .then(() => {
+    resolveMostRecentOperation({
+      errors: [{ message: 'Failed to create profile' }],
+    })
+  })
+cy.get('@sendToastSpy').should('have.been.calledWith', 
+  'Failed to create profile',
+  { type: 'error' }
+)

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

Copy link
Collaborator

Choose a reason for hiding this comment

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

could we add a TODO: add a fallback loading state here?

Copy link
Collaborator

Choose a reason for hiding this comment

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

is that specified in the designs? I'm asking because we do have a dialog component under packages/design-system/components/dialogs/Dialog so if that's a style that applies to all our dialogs, we should consider doing these changes on that component

Copy link
Collaborator

Choose a reason for hiding this comment

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

just curious why we have this trigger, if we set useForm({mode: "onChange"}) wouldn't that be enough?

Copy link
Collaborator

Choose a reason for hiding this comment

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

would it be a case where if formState.isValid is true there would still be formState.errors? asking because we normally only use isValid

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 802b834b16abe078f9a0bf9f2822f278d142acd5 and d81d09a44590b1079a8c6a835c3139dc0853be8b.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (3)
  • packages/components/CHANGELOG.md (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/index.tsx (1 hunks)
  • packages/components/package.json (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/components/package.json
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/index.tsx

Comment on lines 3 to 8
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Expand the changelog entry to better reflect the scope of changes.

The current changelog entry is too brief considering the significant changes implemented. Consider expanding it to include key additions and changes:

 ## 0.0.25

 ### Patch Changes

-Allow users to create new organization profile.
+- Add organization profile creation functionality:
+  - New `AddProfileModal` component with form validation
+  - Support for custom URL paths (e.g., airtab.com/BigAlex)
+  - Automatic owner role assignment
+  - Toast notifications for successful creation
+  - GraphQL mutations for organization creation
+  - Username uniqueness validation
📝 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
## 0.0.25
### Patch Changes
- Allow users to create new organization profile.
## 0.0.25
### Patch Changes
- Add organization profile creation functionality:
- New `AddProfileModal` component with form validation
- Support for custom URL paths (e.g., airtab.com/BigAlex)
- Automatic owner role assignment
- Toast notifications for successful creation
- GraphQL mutations for organization creation
- Username uniqueness validation

@priscilladeroode priscilladeroode force-pushed the BA-1832-fe-multiple-profiles-create-organization-profile branch from d81d09a to b7f1edc Compare December 11, 2024 16:27
@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
28.0% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (5)
packages/components/modules/profiles/ProfilePopover/ProfilesList/index.tsx (3)

33-35: Consider using a more conservative fetch policy

The store-and-network fetch policy will always make a network request, even if the data is available in the store. This could lead to unnecessary API calls and potential flickering of the UI. Consider using store-or-network instead, which will only make a network request if the data isn't available in the store.

  const { me } = useLazyLoadQuery<ProfilesListQueryType>(ProfilesListQuery, options, {
-   fetchPolicy: 'store-and-network',
+   fetchPolicy: 'store-or-network',
  })

Line range hint 41-54: Move image path handling to backend

The TODO comment indicates that image path handling should be moved to the backend. This is correct as:

  1. Frontend URL manipulation is fragile and could break if the backend URL structure changes
  2. The environment variable dependency makes the code less portable
  3. This logic might need to be duplicated in other frontend components

Would you like me to help create a GitHub issue to track moving the image path handling to the backend?


98-99: Extract magic number into a named constant

The value 56 represents the height of each profile item. This should be extracted into a named constant for better maintainability and clarity.

+const PROFILE_ITEM_HEIGHT = 56
+
 const minHeight = profiles.length * 56 > listMaxHeight 
   ? listMaxHeight 
-  : profiles.length * 56
+  : profiles.length * PROFILE_ITEM_HEIGHT
packages/components/schema.graphql (2)

1029-1037: Consider adding audit and status fields to Organization type.

The Organization type could benefit from additional fields for better management and auditing:

  • created and modified timestamps
  • status field to manage organization lifecycle (active/inactive/suspended)
type Organization implements Node {
  profile: Profile
+ created: DateTime!
+ modified: DateTime!
+ status: OrganizationStatus!

  """
  The ID of the object
  """
  id: ID!
  pk: Int!
}

+"""
+An enumeration.
+"""
+enum OrganizationStatus {
+  ACTIVE
+  INACTIVE
+  SUSPENDED
+}

755-755: Consider adding permission checks for organization operations.

The schema should enforce permission checks for organization operations:

  1. The organizationCreate mutation should verify if the user has permission to create organizations
  2. The organization query should include permission checks for viewing organizations
type Mutation {
- organizationCreate(input: OrganizationCreateInput!): OrganizationCreatePayload
+ organizationCreate(input: OrganizationCreateInput!): OrganizationCreatePayload
+   @requireAuth(permission: "organization.create")

type Query {
  organization(
    """
    The ID of the object
    """
    id: ID!
- ): Organization
+ ): Organization @requireAuth(permission: "organization.view")

Also applies to: 1562-1567

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between d81d09a44590b1079a8c6a835c3139dc0853be8b and b7f1edc.

⛔ Files ignored due to path filters (7)
  • packages/components/__generated__/AddProfilePopoverUserQuery.graphql.ts is excluded by !**/__generated__/**
  • packages/components/__generated__/OrganizationCreateMutation.graphql.ts is excluded by !**/__generated__/**
  • packages/components/__generated__/ProfilesListFragment.graphql.ts is excluded by !**/__generated__/**
  • packages/components/__generated__/ProfilesListQuery.graphql.ts is excluded by !**/__generated__/**
  • packages/components/__generated__/UserProfileQuery.graphql.ts is excluded by !**/__generated__/**
  • packages/components/__generated__/profilesListRefetchable.graphql.ts is excluded by !**/__generated__/**
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (24)
  • package.json (1 hunks)
  • packages/components/CHANGELOG.md (1 hunks)
  • packages/components/modules/messages/MessagesList/MessagesGroup/MessageItem/index.tsx (0 hunks)
  • packages/components/modules/navigations/Header/AccountMenu/AccountPopover/__tests__/AccountPopover.cy.tsx (4 hunks)
  • packages/components/modules/navigations/Header/AccountMenu/AccountPopover/__tests__/__mocks__/profiles.ts (3 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/index.tsx (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/styled.tsx (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/types.ts (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/constants.ts (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/index.tsx (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/styled.tsx (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/types.ts (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/CurrentProfile/index.tsx (2 hunks)
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/ProfileMenuItem/index.tsx (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/ProfileMenuItem/types.ts (1 hunks)
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/index.tsx (3 hunks)
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/types.ts (2 hunks)
  • packages/components/modules/profiles/graphql/mutations/OrganizationCreate.ts (1 hunks)
  • packages/components/modules/profiles/graphql/queries/AddProfilePopover.ts (1 hunks)
  • packages/components/modules/profiles/graphql/queries/ProfilesList.ts (1 hunks)
  • packages/components/modules/profiles/graphql/queries/UserProfile.ts (1 hunks)
  • packages/components/package.json (2 hunks)
  • packages/components/schema.graphql (61 hunks)
  • pnpm-workspace.yaml (1 hunks)
💤 Files with no reviewable changes (1)
  • packages/components/modules/messages/MessagesList/MessagesGroup/MessageItem/index.tsx
🚧 Files skipped from review as they are similar to previous changes (19)
  • packages/components/modules/profiles/graphql/queries/UserProfile.ts
  • package.json
  • packages/components/modules/profiles/ProfilePopover/CurrentProfile/index.tsx
  • packages/components/CHANGELOG.md
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/ProfileMenuItem/index.tsx
  • packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/types.ts
  • pnpm-workspace.yaml
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/types.ts
  • packages/components/modules/profiles/ProfilePopover/ProfilesList/ProfileMenuItem/types.ts
  • packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/styled.tsx
  • packages/components/modules/profiles/graphql/queries/AddProfilePopover.ts
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/styled.tsx
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/constants.ts
  • packages/components/package.json
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/types.ts
  • packages/components/modules/profiles/graphql/mutations/OrganizationCreate.ts
  • packages/components/modules/profiles/ProfilePopover/AddProfileModal/index.tsx
  • packages/components/modules/profiles/graphql/queries/ProfilesList.ts
  • packages/components/modules/navigations/Header/AccountMenu/AccountPopover/tests/mocks/profiles.ts
🔇 Additional comments (9)
packages/components/modules/profiles/ProfilePopover/ProfilesList/index.tsx (3)

102-114: Consider adding keyboard navigation support to Virtuoso list

The virtualized list should be keyboard accessible for better accessibility.


Line range hint 128-147: LGTM! Well-structured component with good UX

The ProfilesListSuspended component is well-implemented with:

  • Proper Suspense boundary for loading states
  • Smooth slide animations for transitions
  • Accessible cancel button with clear labeling

Line range hint 41-54: Add error handling for profile switching

The profile switching logic should handle potential errors and provide appropriate feedback to users.

packages/components/schema.graphql (1)

1328-1328: LGTM: Profile-Organization relationship is well defined.

The one-to-one relationship between Profile and Organization is correctly implemented.

packages/components/modules/profiles/ProfilePopover/AddProfileMenuItem/index.tsx (3)

19-20: ⚠️ Potential issue

Add error handling for the GraphQL query

The query should handle potential errors gracefully to prevent runtime issues.

Apply this diff:

-  const { me } = useLazyLoadQuery<AddProfilePopoverUserQueryType>(AddProfilePopoverUserQuery, {})
+  const { me } = useLazyLoadQuery<AddProfilePopoverUserQueryType>(
+    AddProfilePopoverUserQuery,
+    {},
+    {
+      fetchPolicy: 'store-or-network',
+      onError: (error) => {
+        console.error('Failed to fetch user data:', error)
+        // Consider showing an error state
+      }
+    }
+  )

27-35: 🛠️ Refactor suggestion

Add ARIA attributes for improved accessibility

The menu item that triggers the modal should include accessibility attributes.

Apply this diff:

 <MenuItem
   tabIndex={0}
   component={ButtonBase}
   sx={{ justifyContent: 'space-between' }}
   onClick={() => setOpen(true)}
+  aria-haspopup="dialog"
+  aria-expanded={open}
+  aria-label="Add new organization profile"
 >

42-45: 🛠️ Refactor suggestion

Add loading state fallback to Suspense wrapper

The Suspense wrapper should include a fallback prop using the LoadingState component.

Apply this diff:

 const AddProfileMenuItemSuspended: FC<AddProfileMenuItemProps> = (props) => (
-  <Suspense>
+  <Suspense fallback={<LoadingState />}>
     <AddProfileMenuItem {...props} />
   </Suspense>
 )
packages/components/modules/navigations/Header/AccountMenu/AccountPopover/__tests__/AccountPopover.cy.tsx (2)

160-169: Add test for pagination loading state

The test should verify that loading states are displayed during pagination.

Apply this diff:

 const profileListData = mockProfilesListFactory(mockUserProfileData)
 cy.findByRole('menuitem', { name: /change profile/i })
   .click()
   .then(() => {
     resolveMostRecentOperation({ data: mockAddProfileData() })
   })
   .then(() => {
     resolveMostRecentOperation({ data: profileListData })
   })
+
+// Test loading state during pagination
+cy.step('should show loading state during pagination')
+cy.scrollTo('bottom')
+cy.get('[role="progressbar"]').should('be.visible')
+cy.then(() => {
+  resolveMostRecentOperation({ 
+    data: { 
+      ...profileListData,
+      pageInfo: { hasNextPage: false }
+    }
+  })
+})
+cy.get('[role="progressbar"]').should('not.exist')

65-91: ⚠️ Potential issue

Add test coverage for organization creation feature

While the mocks support the new organization creation feature, there are no tests verifying:

  1. The "Add organization" button functionality
  2. Form validation for organization name and URL
  3. Error handling for duplicate usernames
  4. Success toast for organization creation

Would you like me to help generate the missing test cases?

@priscilladeroode priscilladeroode merged commit 9333c71 into master Dec 11, 2024
8 of 9 checks passed
This was referenced Dec 12, 2024
@coderabbitai coderabbitai bot mentioned this pull request Jan 25, 2025
@coderabbitai coderabbitai bot mentioned this pull request Apr 1, 2025
@nossila nossila deleted the BA-1832-fe-multiple-profiles-create-organization-profile branch May 26, 2025 21:15
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.

5 participants