Conversation
… self-hosted users - Updated OrganizationDropdown to conditionally render based on self-hosted type. - Improved loading state handling in OrganizationSwitcherComponent. - Adjusted OrganizationContextProvider to manage organization loading state more effectively. - Modified user resource to handle null user state and updated user creation logic from JWT.
✅ Deploy preview added
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
Hey there and thank you for opening this pull request! 👋 We require pull request titles to follow specific formatting rules and it looks like your proposed title needs to be adjusted. Your PR title is: Requirements:
Expected format: Details: PR title must end with 'fixes TICKET-ID' (e.g., 'fixes NOV-123') or include ticket ID in branch name |
WalkthroughThis change refactors the self-hosted dashboard utilities to improve loading state handling and null safety. The organization switcher component now displays a loading skeleton when data is not yet loaded. The user context has been updated to accept null user values instead of initialized defaults. The createUserFromJwt function now explicitly returns null when decoding fails and adds organizationMemberships and passwordEnabled fields. Organization resource loading logic is revised to check for token presence. Additionally, the Vite configuration makes the @ alias universally available while keeping the self-hosted region alias conditionally gated. 🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 📜 Recent review detailsConfiguration used: Repository UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🧰 Additional context used📓 Path-based instructions (8)**/*.{ts,tsx}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
**/*.{ts,tsx,js,jsx}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
**/*.{tsx,ts}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
apps/{dashboard,web}/**/*.{ts,tsx}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
apps/dashboard/**/*.{ts,tsx}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
apps/dashboard/**/*.{tsx,ts}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
apps/dashboard/**/*📄 CodeRabbit inference engine (.cursor/rules/figma.mdc)
Files:
**📄 CodeRabbit inference engine (.cursor/rules/novu.mdc)
Files:
🧠 Learnings (1)📚 Learning: 2026-01-07T13:09:45.895ZApplied to files:
🧬 Code graph analysis (1)apps/dashboard/vite.config.ts (2)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
🔇 Additional comments (1)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/dashboard/src/utils/self-hosted/user.types.ts (1)
32-33: Address hardcoded property values in self-hosted user initialization.The
organizationMembershipsarray with a single empty object[{}]is problematic—it's neither an empty array nor populated with data, which could cause logic errors in organizational membership checks (e.g.,organizationMemberships.length > 0would incorrectly evaluate to true). For self-hosted deployments without organization data, this should likely be[], or it should be populated from the JWT if organization data is available.Similarly,
passwordEnabled: trueis unconditionally hardcoded without considering authentication method variations. In self-hosted scenarios with SAML-only or OAuth-only authentication, this value may be incorrect. Verify whether this should be extracted from the JWT or configured based on the self-hosted deployment's authentication method.
🧹 Nitpick comments (1)
apps/dashboard/src/utils/self-hosted/organization-switcher.tsx (1)
6-9: Consider improving type safety by properly typing the context.The type assertion
as { organization: { name: string } | undefined; isLoaded: boolean; }bypasses TypeScript's type checking. SinceuseOrganizationis created viacreateContextHook(OrganizationContext), the return type should be properly inferred from the context value.♻️ Suggested approach
Define a proper type for the organization context value in
organization.resource.tsxand export it:// In organization.resource.tsx export type OrganizationContextValue = { organization: { name: string; // ... other fields } | undefined; isLoaded: boolean; };Then use it without assertion:
- const { organization, isLoaded } = useOrganization() as { - organization: { name: string } | undefined; - isLoaded: boolean; - }; + const { organization, isLoaded } = useOrganization();
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
apps/dashboard/src/components/side-navigation/organization-dropdown.tsxapps/dashboard/src/utils/self-hosted/organization-switcher.tsxapps/dashboard/src/utils/self-hosted/organization.resource.tsxapps/dashboard/src/utils/self-hosted/user.resource.tsxapps/dashboard/src/utils/self-hosted/user.types.ts
🧰 Additional context used
📓 Path-based instructions (10)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Write concise, technical TypeScript code with accurate examples
Use descriptive variable names with auxiliary verbs (isLoading, hasError)
Add blank lines before return statements
Import motion components from 'motion/react' instead of 'motion-react'
**/*.{ts,tsx}: Write concise, technical TypeScript code with accurate examples
Use functional and declarative programming patterns; avoid classes
Prefer iteration and modularization over code duplication, minimize code duplication as possible
Use descriptive variable names with auxiliary verbs (e.g., isLoading, hasError)
Structure files: exported component, subcomponents, helpers, static content, types
Don't leave comments in code, unless they explain something complex and not trivial
Don't use nested ternaries
Favor named exports for components
Use TypeScript for all code; prefer interfaces over types
In front end code, use types over interfaces
Use functional components with TypeScript types
Use the "function" keyword for pure functions
Avoid unnecessary curly braces in conditionals; use concise syntax for simple statements
Add blank lines before return statements
When importing "motion-react" package, import it from "motion/react"
Files:
apps/dashboard/src/utils/self-hosted/user.resource.tsxapps/dashboard/src/components/side-navigation/organization-dropdown.tsxapps/dashboard/src/utils/self-hosted/organization.resource.tsxapps/dashboard/src/utils/self-hosted/user.types.tsapps/dashboard/src/utils/self-hosted/organization-switcher.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use functional and declarative programming patterns; avoid classes
Files:
apps/dashboard/src/utils/self-hosted/user.resource.tsxapps/dashboard/src/components/side-navigation/organization-dropdown.tsxapps/dashboard/src/utils/self-hosted/organization.resource.tsxapps/dashboard/src/utils/self-hosted/user.types.tsapps/dashboard/src/utils/self-hosted/organization-switcher.tsx
**/*.{tsx,ts}
📄 CodeRabbit inference engine (CLAUDE.md)
Favor named exports for components
Files:
apps/dashboard/src/utils/self-hosted/user.resource.tsxapps/dashboard/src/components/side-navigation/organization-dropdown.tsxapps/dashboard/src/utils/self-hosted/organization.resource.tsxapps/dashboard/src/utils/self-hosted/user.types.tsapps/dashboard/src/utils/self-hosted/organization-switcher.tsx
apps/{dashboard,web}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Prefer types over interfaces in frontend code
Files:
apps/dashboard/src/utils/self-hosted/user.resource.tsxapps/dashboard/src/components/side-navigation/organization-dropdown.tsxapps/dashboard/src/utils/self-hosted/organization.resource.tsxapps/dashboard/src/utils/self-hosted/user.types.tsapps/dashboard/src/utils/self-hosted/organization-switcher.tsx
apps/dashboard/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use lowercase with dashes for directories and files in dashboard (e.g., components/auth-wizard)
Files:
apps/dashboard/src/utils/self-hosted/user.resource.tsxapps/dashboard/src/components/side-navigation/organization-dropdown.tsxapps/dashboard/src/utils/self-hosted/organization.resource.tsxapps/dashboard/src/utils/self-hosted/user.types.tsapps/dashboard/src/utils/self-hosted/organization-switcher.tsx
apps/dashboard/**/*.{tsx,ts}
📄 CodeRabbit inference engine (CLAUDE.md)
Favor named exports for components in dashboard
Files:
apps/dashboard/src/utils/self-hosted/user.resource.tsxapps/dashboard/src/components/side-navigation/organization-dropdown.tsxapps/dashboard/src/utils/self-hosted/organization.resource.tsxapps/dashboard/src/utils/self-hosted/user.types.tsapps/dashboard/src/utils/self-hosted/organization-switcher.tsx
apps/dashboard/**/*
📄 CodeRabbit inference engine (.cursor/rules/figma.mdc)
apps/dashboard/**/*: Use the assets endpoint from Figma Dev Mode MCP Server to serve image and SVG assets
If the Figma Dev Mode MCP Server returns a localhost source for an image or SVG, use that image or SVG source directly without modification
Do not import or add new icon packages; all assets should come from the Figma payload
Do not use or create placeholders if a localhost source is provided by the Figma Dev Mode MCP ServerUse lowercase with dashes for directory and file names (e.g., components/auth-wizard)
Files:
apps/dashboard/src/utils/self-hosted/user.resource.tsxapps/dashboard/src/components/side-navigation/organization-dropdown.tsxapps/dashboard/src/utils/self-hosted/organization.resource.tsxapps/dashboard/src/utils/self-hosted/user.types.tsapps/dashboard/src/utils/self-hosted/organization-switcher.tsx
**
📄 CodeRabbit inference engine (.cursor/rules/novu.mdc)
Use lowercase with dashes for directories and files (e.g., components/auth-wizard)
Files:
apps/dashboard/src/utils/self-hosted/user.resource.tsxapps/dashboard/src/components/side-navigation/organization-dropdown.tsxapps/dashboard/src/utils/self-hosted/organization.resource.tsxapps/dashboard/src/utils/self-hosted/user.types.tsapps/dashboard/src/utils/self-hosted/organization-switcher.tsx
**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/novu.mdc)
**/*.tsx: Use declarative JSX
Wrap client components in Suspense with fallback
Use dynamic loading for non-critical components
Files:
apps/dashboard/src/utils/self-hosted/user.resource.tsxapps/dashboard/src/components/side-navigation/organization-dropdown.tsxapps/dashboard/src/utils/self-hosted/organization.resource.tsxapps/dashboard/src/utils/self-hosted/organization-switcher.tsx
apps/dashboard/**/components/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/dashboard.mdc)
Favor named exports for components
Files:
apps/dashboard/src/components/side-navigation/organization-dropdown.tsx
🧠 Learnings (3)
📚 Learning: 2025-11-25T11:29:52.304Z
Learnt from: CR
Repo: novuhq/novu PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T11:29:52.304Z
Learning: Applies to apps/dashboard/**/*.{tsx,ts} : Favor named exports for components in dashboard
Applied to files:
apps/dashboard/src/components/side-navigation/organization-dropdown.tsxapps/dashboard/src/utils/self-hosted/organization-switcher.tsx
📚 Learning: 2025-12-22T14:14:49.363Z
Learnt from: CR
Repo: novuhq/novu PR: 0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-12-22T14:14:49.363Z
Learning: Applies to apps/dashboard/**/components/**/*.{ts,tsx} : Favor named exports for components
Applied to files:
apps/dashboard/src/components/side-navigation/organization-dropdown.tsxapps/dashboard/src/utils/self-hosted/organization-switcher.tsx
📚 Learning: 2026-01-07T13:09:45.895Z
Learnt from: CR
Repo: novuhq/novu PR: 0
File: .cursor/rules/novu.mdc:0-0
Timestamp: 2026-01-07T13:09:45.895Z
Learning: Applies to **/*.tsx : Use dynamic loading for non-critical components
Applied to files:
apps/dashboard/src/utils/self-hosted/organization-switcher.tsx
🧬 Code graph analysis (5)
apps/dashboard/src/utils/self-hosted/user.resource.tsx (1)
apps/dashboard/src/utils/self-hosted/user.types.ts (1)
SelfHostedUser(3-15)
apps/dashboard/src/components/side-navigation/organization-dropdown.tsx (2)
apps/dashboard/src/config/index.ts (1)
IS_ENTERPRISE(39-39)apps/dashboard/src/components/side-navigation/organization-dropdown-clerk.tsx (1)
OrganizationDropdown(96-252)
apps/dashboard/src/utils/self-hosted/organization.resource.tsx (1)
apps/dashboard/src/utils/self-hosted/jwt-manager.tsx (1)
getJwtToken(3-5)
apps/dashboard/src/utils/self-hosted/user.types.ts (1)
apps/dashboard/src/utils/self-hosted/index.tsx (1)
DecodedJwt(83-94)
apps/dashboard/src/utils/self-hosted/organization-switcher.tsx (2)
apps/dashboard/src/utils/self-hosted/organization.resource.tsx (1)
useOrganization(45-45)apps/dashboard/src/utils/self-hosted/index.tsx (1)
useOrganization(28-28)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: E2E test API / Test E2E
- GitHub Check: Redirect rules - dashboard-v2-novu-staging
- GitHub Check: Header rules - dashboard-v2-novu-staging
- GitHub Check: Pages changed - dashboard-v2-novu-staging
- GitHub Check: Analyze (javascript)
- GitHub Check: Analyze (typescript)
🔇 Additional comments (10)
apps/dashboard/src/utils/self-hosted/user.resource.tsx (1)
6-12: LGTM! Type consistency improved.The nullable user type aligns with the updated
createUserFromJwtsignature, and simplifying the default value fromcreateUserFromJwt(null)tonullis cleaner since the function would returnnullanyway.apps/dashboard/src/utils/self-hosted/user.types.ts (2)
17-20: LGTM! Nullable return type properly implemented.The early null check and nullable return type improve type safety and align with the context updates in
user.resource.tsx.
25-28: LGTM! Appropriate use of direct property access.After the null check on line 18, direct property access is safe and more appropriate than optional chaining, especially since
DecodedJwtfields are non-optional.apps/dashboard/src/utils/self-hosted/organization.resource.tsx (3)
19-19: LGTM! Token-based query gating improves efficiency.The
hasTokenflag provides clear intent and prevents unnecessary API calls when no authentication token is present.Also applies to: 23-23
26-38: LGTM! More explicit handling of missing organization.Returning
undefinedinstead of a default organization object is more semantically correct and aligns with how consumers likeorganization-switcher.tsxhandle the missing organization case.
39-39: Verify loading state behavior when no token is present.The
isLoadedlogic returnstrueimmediately whenhasTokenisfalse. This means consumers won't see a loading state even on initial render when there's no token. Confirm this is the intended UX:
- Should users see a loading skeleton before we determine there's no token?
- Does this align with how
organization-switcher.tsx(line 11) expects the loading state?The current implementation assumes "no token = fully loaded state with no data," which may be correct but should be verified against the intended user experience.
apps/dashboard/src/components/side-navigation/organization-dropdown.tsx (1)
1-9: LGTM! Clean conditional export pattern.The feature-flag-based component selection is well-structured with clear naming and helpful comments. The const export for the component is an acceptable pattern here since it's aliasing one of two concrete component implementations.
As per coding guidelines, named exports are properly used.
apps/dashboard/src/utils/self-hosted/organization-switcher.tsx (3)
11-18: LGTM! Loading state enhances user experience.The loading skeleton provides appropriate visual feedback and matches the pattern used in the Clerk variant for consistency.
23-28: LGTM! Simplified UI appropriate for non-interactive display.The change from Button to a plain div structure aligns with the non-interactive nature of the community self-hosted organization display. Setting
shining={false}is also appropriate for a static display component.
32-32: LGTM! Dual export aliases provide flexibility.Exporting the component as both
OrganizationDropdownandOrganizationSwitcherallows consumers to import using the name that best fits their context, which aligns with the conditional import pattern inorganization-dropdown.tsx.
- Removed conditional rendering logic for OrganizationDropdown. - Directly exported OrganizationDropdown from the Clerk component for clarity and maintainability.
- Reintroduced the alias mapping for '@' to point to the './src' directory. - Removed unnecessary conditional alias mappings for self-hosted components.
What changed? Why was the change needed?
Screenshots
Expand for optional sections
Related enterprise PR
Special notes for your reviewer