Skip to content

Add onboarding for new accounts#514

Merged
braginini merged 2 commits intomainfrom
feature/onboarding
Nov 27, 2025
Merged

Add onboarding for new accounts#514
braginini merged 2 commits intomainfrom
feature/onboarding

Conversation

@heisbrot
Copy link
Copy Markdown
Contributor

@heisbrot heisbrot commented Nov 27, 2025

Summary by CodeRabbit

Release Notes

  • New Features
    • Added guided multi-step onboarding experience for new users with peer-to-peer and remote network access options.
    • Includes initial survey, setup wizard, device and resource configuration, and connection validation.
    • Integrated policy management controls and video guides for successful deployment.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Nov 27, 2025

Walkthrough

A comprehensive onboarding module has been introduced with multi-step flows for P2P and NETWORKS intents, including new React components, type definitions, and API integration. Workflow CI trigger has been restricted, and Account/Peer interfaces have been extended with new optional properties.

Changes

Cohort / Cohort / File(s) Summary
Workflow Configuration
.github/workflows/build_and_push.yml
Removed push trigger for feature/** branch; workflow now triggers on main and tags only.
Type & Interface Definitions
src/contexts/AnalyticsProvider.tsx, src/interfaces/Account.ts, src/interfaces/Peer.ts
Added new exported type HubspotFormField; added optional onboarding property and new AccountOnboarding interface to Account; added optional disapproval_reason field to Peer.
Layout & Provider
src/layouts/DashboardLayout.tsx, src/modules/onboarding/OnboardingProvider.tsx
Integrated OnboardingProvider with conditional rendering in DashboardLayout when not NetBird-hosted; provider orchestrates lifecycle, state, analytics, and visibility logic.
Core Onboarding Module
src/modules/onboarding/Onboarding.tsx
Introduced multi-step onboarding modal with reducer-based state management, dual intent paths (P2P and NETWORKS), API interactions for network/resource creation, polling mechanism for device connectivity, and dynamic step progression.
Onboarding UI Components
src/modules/onboarding/OnboardingDevices.tsx, src/modules/onboarding/OnboardingEnd.tsx, src/modules/onboarding/OnboardingIntent.tsx
Added components for device/resource visualization with connectors, onboarding completion screen with video guides, and intent selection UI with recommendation logic.
Onboarding Form & Policy
src/modules/onboarding/OnboardingPolicy.tsx, src/modules/onboarding/OnboardingSurvey.tsx
Introduced policy toggle component and multi-field survey form with business/personal modes, company size, use-case checkboxes, and referral source dropdown integrated with HubspotFormField.
P2P Onboarding Flow
src/modules/onboarding/p2p/OnboardingFirstDevice.tsx, src/modules/onboarding/p2p/OnboardingSecondDevice.tsx, src/modules/onboarding/p2p/OnboardingTestP2P.tsx, src/modules/onboarding/p2p/OnboardingExplainDefaultPolicy.tsx
Added components for first device installation, second device setup with one-off setup key generation, P2P connection testing, and default policy explanation.
NETWORKS Onboarding Flow
src/modules/onboarding/networks/OnboardingAddResource.tsx, src/modules/onboarding/networks/OnboardingAddRoutingPeer.tsx, src/modules/onboarding/networks/OnboardingAddUserDevice.tsx, src/modules/onboarding/networks/OnboardingTestResource.tsx, src/modules/onboarding/networks/OnboardingExplainPolicy.tsx
Added components for resource creation, routing peer setup with setup key generation, user device installation, resource connectivity testing, and policy explanation.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant OnboardingProvider
    participant Onboarding
    participant API as API/Backend
    participant Storage as LocalStorage

    User->>OnboardingProvider: Load Dashboard
    OnboardingProvider->>Storage: Check onboarding state
    OnboardingProvider->>API: Fetch peers, accounts, user
    alt showOnboarding true
        OnboardingProvider->>Onboarding: Render with initial state
    else
        OnboardingProvider-->>User: Skip Onboarding
    end

    alt P2P Intent
        Onboarding->>User: Select P2P intent
        Onboarding->>Onboarding: Step 1-6: Device setup & testing
    else NETWORKS Intent
        Onboarding->>User: Select NETWORKS intent
        Onboarding->>Onboarding: Step 1-7: Resource & peer setup
    end

    Onboarding->>API: Create networks, resources, policies
    Onboarding->>API: Poll peers for device connectivity (5s interval)
    alt Device Connected
        API-->>Onboarding: Device detected
        Onboarding->>User: Progress to next step
    end

    User->>Onboarding: Complete final step
    Onboarding->>API: updateAccountMeta (clear onboarding flags)
    Onboarding->>OnboardingProvider: Call onFinish
    OnboardingProvider->>API: Emit analytics events
    OnboardingProvider-->>User: Navigate/Close
    OnboardingProvider->>Storage: Update onboarding state
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

  • High-complexity orchestration: The core Onboarding.tsx implements a sophisticated reducer-based state machine with dual branching intent paths, polling logic, API orchestration, and interdependent step progression—requires careful review of state transitions and side effects.
  • Multi-component interaction chains: P2P and NETWORKS modules each contain 4–5 interdependent components with callbacks, async operations (setup key generation, group creation, policy creation), and conditional rendering—cross-component flow must be verified.
  • API mutation patterns: Frequent use of SWR mutate for cache invalidation after resource/network/policy creation across multiple components; ensure correct mutation triggers and data consistency.
  • State synchronization: The polling mechanism in Onboarding.tsx (every 5 seconds) to detect device connectivity requires careful review of termination conditions and potential race conditions.
  • Provider orchestration: OnboardingProvider.tsx coordinates analytics, localStorage migration, meta updates, and callbacks—state transitions and side effect ordering need verification.

Specific areas requiring attention:

  • Polling logic termination conditions in Onboarding.tsx (lines checking device connectivity and step progression)
  • Setup key generation and clipboard operations in OnboardingAddRoutingPeer.tsx and OnboardingSecondDevice.tsx
  • Group creation/update PUT requests and SWR cache invalidation sequence
  • Reducer action handlers and edge cases in Onboarding.tsx (especially reset and skip paths)
  • Analytics event emission timing in OnboardingProvider.tsx relative to state updates

Poem

🐰 Behold! A grand onboarding stage so fine,
With P2P paths and networks in line,
Step by step, the flow takes flight,
From device one to policies so right,
A rabbit hops through each modal bright! 🚀✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'Add onboarding for new accounts' accurately summarizes the main objective of this changeset, which introduces comprehensive onboarding flows and related components.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/onboarding

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown

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

🧹 Nitpick comments (16)
src/modules/onboarding/p2p/OnboardingSecondDevice.tsx (4)

27-27: Potential SSR issue with navigator access.

Accessing navigator.share directly in the component body can throw during server-side rendering where navigator is undefined. Consider using a safe check:

-  const isShareSupported = navigator.share !== undefined;
+  const isShareSupported = typeof navigator !== "undefined" && navigator.share !== undefined;

36-44: Unhandled promise from navigator.share.

navigator.share() returns a promise that can reject (e.g., user cancels the share dialog). Consider handling the rejection to avoid unhandled promise warnings.

   const openNavigatorShare = () => {
     if (navigator.share) {
-      navigator.share({
+      navigator.share({
         title: "Install NetBird",
         text: "Install NetBird on another device using this link.",
         url: getInstallUrl(),
-      });
+      }).catch(() => {
+        // User cancelled or share failed - no action needed
+      });
     }
   };

57-71: Mixed async patterns and silent error handling.

The function uses await with .then() chaining, which is inconsistent. Also, since useApiCall is initialized with ignoreError=true, API failures are silently ignored—the user won't know if setup key creation failed.

Consider using consistent async/await and providing user feedback on failure:

-    await setupKeyRequest
-      .post({
+    const result = await setupKeyRequest.post({
         name: "Onboarding (Second Device)",
         type: "one-off",
         expires_in: 24 * 60 * 60, // 1 day expiration
         revoked: false,
         auto_groups: [],
         usage_limit: 1,
         ephemeral: false,
         allow_extra_dns_labels: false,
-      })
-      .then((setupKey) => {
-        setOpen(true);
-        setSetupKey(setupKey);
       });
+    if (result) {
+      setOpen(true);
+      setSetupKey(result);
+    }

115-118: Prefer button semantics for click-only actions.

Using href="#" with onClick is an anti-pattern—it can cause unwanted scroll behavior and doesn't convey proper semantics. Since this triggers an action rather than navigation, consider using a Button with link styling or adding e.preventDefault().

-        <InlineLink onClick={installUsingSetupKey} href={"#"}>
+        <InlineLink
+          onClick={(e) => {
+            e.preventDefault();
+            installUsingSetupKey();
+          }}
+          href={"#"}
+        >
           Install with a setup key
           <ArrowUpRightIcon size={12} />
         </InlineLink>{" "}

Or better, replace with a styled button component if available.

src/modules/onboarding/p2p/OnboardingTestP2P.tsx (1)

10-16: Consider making firstDevice/secondDevice required or guarding the test step

The copy and ping command assume both devices exist; if either is missing you end up with blank names and ping without an IP. Either:

  • make firstDevice and secondDevice required in Props, or
  • guard the paragraph and Code block behind a presence check and show a fallback when a device is missing.

Also applies to: 41-51

src/modules/onboarding/OnboardingPolicy.tsx (2)

12-13: Return null explicitly for conditional rendering.

React components should return null instead of undefined when rendering nothing. While React handles undefined gracefully, returning null is the idiomatic pattern.

 export const OnboardingPolicy = ({ policy, onToggle }: Props) => {
-  if (!policy) return;
+  if (!policy) return null;

25-35: Unnecessary optional chaining after guard clause.

After the if (!policy) return guard on line 13, policy is guaranteed to be defined. The optional chaining (policy?.name, policy?.description, policy?.enabled) is redundant.

-          <ShieldIcon size={12} className={"shrink-0"} />
-          {policy?.name} Policy
+          <ShieldIcon size={12} className={"shrink-0"} />
+          {policy.name} Policy
         </div>
         <div className={"text-nb-gray-300 text-[0.8rem] text-left mt-0.5"}>
-          {policy?.name.includes("Default")
+          {policy.name.includes("Default")
             ? "Allows connections between all your devices"
-            : policy?.description}
+            : policy.description}
         </div>
       </div>
       <ToggleSwitch
         onCheckedChange={() => onToggle?.(policy)}
-        checked={policy?.enabled || false}
+        checked={policy.enabled}
       />
src/modules/onboarding/OnboardingIntent.tsx (1)

21-56: Consider extracting duplicate matching logic.

Both isNetworksRecommended and isP2PRecommended use the same pattern. A helper function would reduce duplication and make adding new intent categories easier.

+const matchesAnyIntent = (useCases: string | undefined, intents: string[]): boolean => {
+  if (!useCases) return false;
+  const lowerCases = useCases.toLowerCase();
+  return intents.some((intent) => lowerCases.includes(intent.toLowerCase()));
+};
+
+const NETWORK_INTENTS = [
+  "Zero Trust Security",
+  "Employee Remote Access",
+  "Business VPN",
+  "Site-to-Site Connectivity",
+  "IoT (Internet of Things)",
+  "MSP (Managed Service Provider)",
+];
+
+const P2P_INTENTS = [
+  "Homelab Automation",
+  "Home Remote Access",
+  "File Access",
+  "Gaming",
+];

 export const OnboardingIntent = ({onSelect, useCases, isBusiness}: Props) => {
-    const isNetworksRecommended = useMemo(() => {
-        if (!useCases) return false;
-        const intents = [...];
-        for (const intent of intents) {
-            if (useCases.toLowerCase().includes(intent.toLowerCase())) {
-                return true;
-            }
-        }
-        return false;
-    }, [useCases]);
-
-    const isP2PRecommended = useMemo(() => { ... }, [useCases]);
+    const isNetworksRecommended = useMemo(
+        () => matchesAnyIntent(useCases, NETWORK_INTENTS),
+        [useCases]
+    );
+    const isP2PRecommended = useMemo(
+        () => matchesAnyIntent(useCases, P2P_INTENTS),
+        [useCases]
+    );
src/modules/onboarding/networks/OnboardingAddRoutingPeer.tsx (1)

98-108: Empty catch block silently swallows errors.

The clipboard write error is silently ignored. At minimum, log the error for debugging purposes.

   const copySetupKey = async (key: string, showMessage = false) => {
     try {
       await navigator.clipboard.writeText(key || "");
       if (showMessage) {
         notify({
           title: "Setup Key Copied",
           description: "Successfully copied to clipboard.",
         });
       }
-    } catch (e) {}
+    } catch (e) {
+      console.error("Failed to copy setup key to clipboard:", e);
+    }
   };
src/modules/onboarding/OnboardingProvider.tsx (2)

48-57: Side effect in render body may cause issues.

The localStorage migration logic runs during render, which is a React anti-pattern. While it's guarded by conditions, this could cause unexpected behavior or hydration mismatches in SSR/Next.js contexts.

Move this logic into a useEffect to ensure it only runs once after mount:

+  // Migrate old onboarding state to new key if needed
+  useEffect(() => {
+    if (typeof window === "undefined" || !account?.id) return;
+    const oldKey = "netbird-onboarding-flow";
+    const newValue = window.localStorage.getItem(onboardingKey);
+    const oldValue = window.localStorage.getItem(oldKey);
+    if (oldValue && !newValue) {
+      window.localStorage.setItem(onboardingKey, oldValue);
+      window.localStorage.removeItem(oldKey);
+    }
+  }, [account?.id, onboardingKey]);

-  // Migrate old onboarding state to new key if needed
-  if (typeof window !== "undefined" && account?.id) {
-    const oldKey = "netbird-onboarding-flow";
-    const oldValue = window.localStorage.getItem(oldKey);
-    const newValue = window.localStorage.getItem(onboardingKey);
-    if (oldValue && !newValue) {
-      window.localStorage.setItem(onboardingKey, oldValue);
-      window.localStorage.removeItem(oldKey);
-    }
-  }

117-119: Remove commented-out code.

The commented line suggests an alternative navigation path. If this is intentional for future use, consider adding a TODO comment explaining the rationale. Otherwise, remove the dead code.

    if (n) {
-      // router.push(`/network?id=${n.id}`);
      router.push("/control-center?tab=networks");
    } else {
src/modules/onboarding/OnboardingSurvey.tsx (1)

153-155: Use strict equality operator.

Using != instead of !== can lead to unexpected type coercion. Prefer strict equality for consistency.

     return [hl, hra, fa, g, zt, ra, bv, st, iot, mp, ou]
-      .filter((s) => s != "")
+      .filter((s) => s !== "")
       .join(", ");
src/modules/onboarding/OnboardingDevices.tsx (1)

177-179: Return null instead of implicit undefined for React components.

When a React component has nothing to render, it should explicitly return null rather than undefined. While this works in practice, returning undefined is technically incorrect and may cause issues with TypeScript strict mode or future React versions.

 export const DeviceCard = ({ device, resource }: DeviceCardProps) => {
-  if (!device && !resource) return;
+  if (!device && !resource) return null;
   return (
src/modules/onboarding/Onboarding.tsx (3)

219-233: Missing dependencies in useEffect may cause stale closure issues.

The effect uses routerRequest.get and peers but neither is listed in the dependency array. This could cause:

  1. The effect to not re-run when peers updates
  2. Stale peers reference being used in the callback

Consider adding the missing dependencies or extracting routerRequest outside the component if it's stable:

   useEffect(() => {
     if (firstNetwork && intent === Intent.NETWORKS && !firstRoutingPeer) {
       const firstRouterId = firstNetwork?.routers?.[0];
       if (firstRouterId) {
         routerRequest
           .get(`/${firstNetwork?.id}/routers/${firstRouterId}`)
           .then((r) => {
             const routingPeer =
               peers?.find((p) => p.id === r.peer) ?? undefined;
             if (!routingPeer) return;
             setFirstRoutingPeer(routingPeer);
           });
       }
     }
-  }, [intent, firstNetwork, peers]);
+  }, [intent, firstNetwork, peers, routerRequest]);

Note: If routerRequest is a new object on each render (from useApiCall), you may need to memoize it or restructure the API call approach.


606-608: Return null instead of implicit undefined.

Same issue as noted in DeviceCard - React components should explicitly return null when rendering nothing.

 const Stepper = ({ step, maxSteps }: { step: number; maxSteps: number }) => {
-  if (step <= 0) return;
+  if (step <= 0) return null;

235-246: Missing resourceRequest dependency in useEffect.

Similar to the previous effect, resourceRequest is used but not listed in dependencies.

   useEffect(() => {
     if (firstNetwork && intent === Intent.NETWORKS) {
       const firstResourceId = firstNetwork?.resources?.[0];
       if (firstResourceId) {
         resourceRequest
           .get(`/${firstNetwork?.id}/resources/${firstResourceId}`)
           .then((r) => {
             setResource(r);
           });
       }
     }
-  }, [intent, firstNetwork]);
+  }, [intent, firstNetwork, resourceRequest]);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 43bc069 and 1fb33e5.

⛔ Files ignored due to path filters (3)
  • src/assets/onboarding/acl.png is excluded by !**/*.png
  • src/assets/onboarding/activity.png is excluded by !**/*.png
  • src/assets/onboarding/posture.png is excluded by !**/*.png
📒 Files selected for processing (21)
  • .github/workflows/build_and_push.yml (0 hunks)
  • src/contexts/AnalyticsProvider.tsx (1 hunks)
  • src/interfaces/Account.ts (1 hunks)
  • src/interfaces/Peer.ts (1 hunks)
  • src/layouts/DashboardLayout.tsx (3 hunks)
  • src/modules/onboarding/Onboarding.tsx (1 hunks)
  • src/modules/onboarding/OnboardingDevices.tsx (1 hunks)
  • src/modules/onboarding/OnboardingEnd.tsx (1 hunks)
  • src/modules/onboarding/OnboardingIntent.tsx (1 hunks)
  • src/modules/onboarding/OnboardingPolicy.tsx (1 hunks)
  • src/modules/onboarding/OnboardingProvider.tsx (1 hunks)
  • src/modules/onboarding/OnboardingSurvey.tsx (1 hunks)
  • src/modules/onboarding/networks/OnboardingAddResource.tsx (1 hunks)
  • src/modules/onboarding/networks/OnboardingAddRoutingPeer.tsx (1 hunks)
  • src/modules/onboarding/networks/OnboardingAddUserDevice.tsx (1 hunks)
  • src/modules/onboarding/networks/OnboardingExplainPolicy.tsx (1 hunks)
  • src/modules/onboarding/networks/OnboardingTestResource.tsx (1 hunks)
  • src/modules/onboarding/p2p/OnboardingExplainDefaultPolicy.tsx (1 hunks)
  • src/modules/onboarding/p2p/OnboardingFirstDevice.tsx (1 hunks)
  • src/modules/onboarding/p2p/OnboardingSecondDevice.tsx (1 hunks)
  • src/modules/onboarding/p2p/OnboardingTestP2P.tsx (1 hunks)
💤 Files with no reviewable changes (1)
  • .github/workflows/build_and_push.yml
🧰 Additional context used
🧬 Code graph analysis (12)
src/modules/onboarding/networks/OnboardingAddResource.tsx (8)
src/interfaces/Network.ts (2)
  • Network (3-11)
  • NetworkResource (22-30)
src/contexts/GroupsProvider.tsx (1)
  • useGroups (163-163)
src/utils/api.tsx (1)
  • useApiCall (169-212)
src/interfaces/Policy.ts (1)
  • Policy (4-12)
src/interfaces/Group.ts (1)
  • Group (1-12)
src/components/Notification.tsx (1)
  • notify (151-155)
src/components/RadioCard.tsx (2)
  • RadioCardGroup (50-67)
  • RadioCard (13-40)
src/modules/networks/resources/ResourceSingleAddressInput.tsx (1)
  • ResourceSingleAddressInput (19-82)
src/modules/onboarding/OnboardingSurvey.tsx (5)
src/contexts/AnalyticsProvider.tsx (1)
  • HubspotFormField (20-24)
src/contexts/UsersProvider.tsx (1)
  • useLoggedInUser (87-111)
src/components/SegmentedTabs.tsx (1)
  • SegmentedTabs (99-99)
src/components/Checkbox.tsx (1)
  • Checkbox (54-54)
src/utils/helpers.ts (1)
  • cn (6-8)
src/modules/onboarding/networks/OnboardingAddUserDevice.tsx (6)
src/interfaces/Peer.ts (1)
  • Peer (4-33)
src/interfaces/Policy.ts (1)
  • Policy (4-12)
src/utils/api.tsx (1)
  • useApiCall (169-212)
src/interfaces/Group.ts (2)
  • Group (1-12)
  • GroupPeer (14-17)
src/components/modal/Modal.tsx (2)
  • Modal (209-209)
  • ModalContent (211-211)
src/modules/setup-netbird-modal/SetupModal.tsx (1)
  • SetupModalContent (70-237)
src/modules/onboarding/p2p/OnboardingExplainDefaultPolicy.tsx (2)
src/interfaces/Policy.ts (1)
  • Policy (4-12)
src/modules/onboarding/OnboardingPolicy.tsx (1)
  • OnboardingPolicy (12-39)
src/modules/onboarding/OnboardingDevices.tsx (6)
src/interfaces/Network.ts (1)
  • NetworkResource (22-30)
src/interfaces/Peer.ts (1)
  • Peer (4-33)
src/utils/helpers.ts (1)
  • cn (6-8)
src/components/ui/TruncatedText.tsx (1)
  • TruncatedText (13-92)
src/hooks/useOperatingSystem.ts (1)
  • getOperatingSystem (25-40)
src/modules/peers/PeerOSCell.tsx (1)
  • OSLogo (82-99)
src/modules/onboarding/p2p/OnboardingTestP2P.tsx (4)
src/interfaces/Peer.ts (1)
  • Peer (4-33)
src/interfaces/Policy.ts (1)
  • Policy (4-12)
src/components/Steps.tsx (1)
  • Steps (9-19)
src/components/InlineLink.tsx (1)
  • InlineLink (35-41)
src/modules/onboarding/p2p/OnboardingSecondDevice.tsx (9)
src/interfaces/Peer.ts (1)
  • Peer (4-33)
src/utils/api.tsx (1)
  • useApiCall (169-212)
src/interfaces/SetupKey.ts (1)
  • SetupKey (3-20)
src/contexts/DialogProvider.tsx (1)
  • useDialog (132-132)
src/utils/netbird.ts (1)
  • getInstallUrl (14-16)
src/utils/helpers.ts (1)
  • cn (6-8)
src/components/InlineLink.tsx (1)
  • InlineLink (35-41)
src/components/modal/Modal.tsx (2)
  • Modal (209-209)
  • ModalContent (211-211)
src/modules/setup-netbird-modal/SetupModal.tsx (1)
  • SetupModalContent (70-237)
src/modules/onboarding/OnboardingProvider.tsx (8)
src/contexts/AnalyticsProvider.tsx (2)
  • HubspotFormField (20-24)
  • useAnalytics (154-154)
src/interfaces/Account.ts (1)
  • Account (1-27)
src/modules/account/useAccount.tsx (1)
  • useAccount (6-21)
src/contexts/UsersProvider.tsx (1)
  • useLoggedInUser (87-111)
src/hooks/useLocalStorage.tsx (1)
  • useLocalStorage (20-124)
src/modules/onboarding/Onboarding.tsx (2)
  • OnboardingState (33-39)
  • Onboarding (89-604)
src/utils/netbird.ts (2)
  • isNetBirdHosted (18-23)
  • isLocalDev (25-27)
src/interfaces/Network.ts (1)
  • Network (3-11)
src/modules/onboarding/OnboardingPolicy.tsx (3)
src/interfaces/Policy.ts (1)
  • Policy (4-12)
src/utils/helpers.ts (1)
  • cn (6-8)
src/components/ToggleSwitch.tsx (1)
  • ToggleSwitch (71-71)
src/modules/onboarding/networks/OnboardingTestResource.tsx (7)
src/interfaces/Network.ts (1)
  • NetworkResource (22-30)
src/interfaces/Peer.ts (1)
  • Peer (4-33)
src/components/Steps.tsx (1)
  • Steps (9-19)
src/utils/helpers.ts (1)
  • cn (6-8)
src/components/InlineLink.tsx (1)
  • InlineLink (35-41)
src/components/modal/Modal.tsx (2)
  • Modal (209-209)
  • ModalContent (211-211)
src/modules/setup-netbird-modal/SetupModal.tsx (1)
  • SetupModalContent (70-237)
src/modules/onboarding/Onboarding.tsx (8)
src/interfaces/Peer.ts (1)
  • Peer (4-33)
src/contexts/AnalyticsProvider.tsx (1)
  • HubspotFormField (20-24)
src/interfaces/Network.ts (3)
  • Network (3-11)
  • NetworkResource (22-30)
  • NetworkRouter (13-20)
src/interfaces/Policy.ts (1)
  • Policy (4-12)
src/components/modal/Modal.tsx (2)
  • Modal (209-209)
  • ModalPortal (215-215)
src/utils/helpers.ts (1)
  • cn (6-8)
src/utils/netbird.ts (1)
  • isNetBirdHosted (18-23)
src/components/InlineLink.tsx (1)
  • InlineLink (35-41)
src/layouts/DashboardLayout.tsx (2)
src/utils/netbird.ts (1)
  • isNetBirdHosted (18-23)
src/modules/onboarding/OnboardingProvider.tsx (1)
  • OnboardingProvider (30-170)
⏰ 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). (1)
  • GitHub Check: build_n_push
🔇 Additional comments (13)
src/modules/onboarding/p2p/OnboardingSecondDevice.tsx (1)

121-130: LGTM!

The modal implementation correctly conditionally renders based on setupKey state and properly passes setupKey.key to SetupModalContent.

src/modules/onboarding/OnboardingEnd.tsx (1)

15-37: Onboarding completion copy and OIDC personalization look solid

Title derivation from the OIDC user with a neutral fallback is correct, and the copy/layout align with existing onboarding patterns.

src/modules/onboarding/p2p/OnboardingTestP2P.tsx (1)

24-39: P2P test flow and copy are clear and consistent

Step layout, messaging, and wiring of the primary CTA to onNext fit well with the rest of the onboarding flow.

Also applies to: 53-75

src/interfaces/Peer.ts (1)

26-32: Optional disapproval_reason addition looks safe

Making disapproval_reason optional preserves compatibility with existing Peer payloads while enabling richer approval/disapproval UX.

src/layouts/DashboardLayout.tsx (1)

8-9: Confirm intent of gating OnboardingProvider with !isNetBirdHosted()

This moves onboarding rendering behind an environment check, so NetBird‑hosted tenants will never mount OnboardingProvider at all (even if onboarding flags are set). If that’s the intended product behavior, the wiring is fine; otherwise consider relying solely on OnboardingProvider’s internal logic for hosted vs self‑hosted handling.

Also applies to: 24-25, 33-45

src/contexts/AnalyticsProvider.tsx (1)

20-24: Form field type addition looks good

The HubspotFormField shape is minimal and correctly requires name and value, which should keep onboarding form payloads well-typed.

src/interfaces/Account.ts (1)

26-32: Account onboarding metadata model looks reasonable

Adding an optional onboarding block with onboarding_flow_pending and signup_form_pending flags cleanly separates onboarding state from core account fields.

src/modules/onboarding/networks/OnboardingExplainPolicy.tsx (1)

12-50: Networks policy explanation component is consistent and focused

Conditional extra guidance when policy is present plus delegation to OnboardingPolicy keeps this step lightweight and aligned with the overall onboarding UX.

src/modules/onboarding/p2p/OnboardingExplainDefaultPolicy.tsx (1)

12-50: P2P default-policy explanation mirrors the networks variant nicely

Shared layout with tailored copy and reuse of OnboardingPolicy keeps the P2P path consistent with the networks onboarding experience.

src/modules/onboarding/p2p/OnboardingFirstDevice.tsx (1)

15-61: LGTM!

The component structure is clean with clear separation of state management, side effects, and rendering. The modal integration with SetupModalContent follows the existing patterns in the codebase.

src/modules/onboarding/networks/OnboardingTestResource.tsx (1)

33-40: LGTM!

The pingAddress computation correctly handles the different resource types (host with /32 suffix, wildcard domains, and subnets) with appropriate user-friendly messages.

src/modules/onboarding/OnboardingIntent.tsx (1)

109-175: LGTM!

The IntentCard component is well-structured with clear props, accessible button semantics, and good hover/transition styling. The tooltip for recommendations provides helpful context.

src/modules/onboarding/networks/OnboardingAddRoutingPeer.tsx (1)

37-67: LGTM with a note on the setup key generation flow.

The workflow for creating/finding the "Routing Peers" group and generating the setup key is well-structured. The notify wrapper with promise handling provides good UX feedback.

Comment thread src/modules/onboarding/networks/OnboardingAddResource.tsx
Comment thread src/modules/onboarding/networks/OnboardingAddResource.tsx
Comment thread src/modules/onboarding/networks/OnboardingAddRoutingPeer.tsx
Comment thread src/modules/onboarding/networks/OnboardingAddUserDevice.tsx
Comment thread src/modules/onboarding/networks/OnboardingAddUserDevice.tsx
Comment thread src/modules/onboarding/OnboardingEnd.tsx
Comment thread src/modules/onboarding/OnboardingSurvey.tsx
Comment thread src/modules/onboarding/p2p/OnboardingFirstDevice.tsx
Comment thread src/modules/onboarding/p2p/OnboardingSecondDevice.tsx
Comment thread src/modules/onboarding/p2p/OnboardingTestP2P.tsx
@braginini braginini self-requested a review November 27, 2025 13:49
@braginini braginini merged commit 240a96f into main Nov 27, 2025
4 checks passed
@braginini braginini deleted the feature/onboarding branch November 27, 2025 13:50
@coderabbitai coderabbitai Bot mentioned this pull request Feb 2, 2026
@coderabbitai coderabbitai Bot mentioned this pull request Feb 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants