From 0476a87afd0f86845697cc5e566831c3e82fbe8e Mon Sep 17 00:00:00 2001 From: Arthur Belleville Date: Sat, 25 Apr 2026 10:28:29 +0200 Subject: [PATCH] fix onboarding modal --- apps/clients/tsconfig.tsbuildinfo | 2 +- apps/main/src/components/Layout.test.tsx | 70 +++++++++++++++++++++++- apps/main/src/components/Layout.tsx | 12 +++- 3 files changed, 79 insertions(+), 5 deletions(-) diff --git a/apps/clients/tsconfig.tsbuildinfo b/apps/clients/tsconfig.tsbuildinfo index f2629d6..0266a11 100644 --- a/apps/clients/tsconfig.tsbuildinfo +++ b/apps/clients/tsconfig.tsbuildinfo @@ -1 +1 @@ -{"root":["./src/app.tsx","./src/envproduction.test.ts","./src/i18n.test.ts","./src/i18n.ts","./src/main.tsx","./src/maincss.test.ts","./src/routes.tsx","./src/setuptests.ts","./src/vite-env.d.ts","./src/viteconfig.test.ts","./src/components/clientauthgate.tsx","./src/components/clientlayout.test.tsx","./src/components/clientlayout.tsx","./src/lib/supabase.ts","./src/pages/authcallback.tsx","./src/pages/clienttablolistpage.tsx","./src/pages/clienttablopage.test.tsx","./src/pages/clienttablopage.tsx","./src/pages/loginpage.test.tsx","./src/pages/loginpage.tsx","./src/pages/resetpasswordpage.test.tsx","./src/pages/resetpasswordpage.tsx","./src/pages/setpasswordpage.test.tsx","./src/pages/setpasswordpage.tsx","./src/test/testhelpers.test.tsx","./src/test/testhelpers.tsx"],"version":"5.9.3"} \ No newline at end of file +{"root":["./src/app.tsx","./src/envproduction.test.ts","./src/i18n.test.ts","./src/i18n.ts","./src/main.tsx","./src/maincss.test.ts","./src/routes.tsx","./src/setuptests.ts","./src/vite-env.d.ts","./src/viteconfig.test.ts","./src/components/clientauthgate.tsx","./src/components/clientlayout.test.tsx","./src/components/clientlayout.tsx","./src/lib/rum.ts","./src/lib/supabase.ts","./src/pages/authcallback.tsx","./src/pages/clienttablolistpage.tsx","./src/pages/clienttablopage.test.tsx","./src/pages/clienttablopage.tsx","./src/pages/loginpage.test.tsx","./src/pages/loginpage.tsx","./src/pages/resetpasswordpage.test.tsx","./src/pages/resetpasswordpage.tsx","./src/pages/setpasswordpage.test.tsx","./src/pages/setpasswordpage.tsx","./src/test/testhelpers.test.tsx","./src/test/testhelpers.tsx"],"version":"5.9.3"} \ No newline at end of file diff --git a/apps/main/src/components/Layout.test.tsx b/apps/main/src/components/Layout.test.tsx index 7dcf293..c8298dc 100644 --- a/apps/main/src/components/Layout.test.tsx +++ b/apps/main/src/components/Layout.test.tsx @@ -1,11 +1,55 @@ import { fireEvent, screen } from "@testing-library/react"; import { Layout } from "@ui/components/Layout"; -import { beforeEach } from "vitest"; +import { beforeEach, vi } from "vitest"; import { renderWithProviders } from "../utils/testHelpers"; +vi.mock("../hooks/organization", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + useOrganization: vi.fn(), + }; +}); + +vi.mock("./OnboardingModal", () => ({ + OnboardingModal: ({ open }: { open: boolean }) => ( +
{open ? "open" : "closed"}
+ ), +})); + +import { useOrganization } from "../hooks/organization"; + +const mockUseOrganization = vi.mocked(useOrganization); + +const baseOrganizationData = { + organization: { + id: 1, + name: "Org", + plan: "none", + member_count: 1, + tablo_count: 0, + logo_url: null, + }, + members: [], + invites_sent: [], + trial_starts_at: "2026-01-01", + trial_ends_at: "2026-02-01", + is_trial_expired: false, + required_plan: "solo" as const, + required_team_quantity: 1, + active_subscription_plan: null, + active_subscription_quantity: 0, + is_billing_owner: true, +}; + describe("Layout", () => { beforeEach(() => { localStorage.setItem("xtablo-onboarding-completed", "true"); + mockUseOrganization.mockReturnValue({ + data: baseOrganizationData, + isLoading: false, + error: null, + } as ReturnType); }); it("renders the layout with children", () => { @@ -80,4 +124,28 @@ describe("Layout", () => { // Should have transition and mobile/desktop behavior expect(navParent).toHaveClass("fixed", "md:relative", "transition-transform"); }); + + it("keeps onboarding closed when there is no paid plan", () => { + localStorage.removeItem("xtablo-onboarding-completed"); + + renderWithProviders(); + + expect(screen.getByTestId("onboarding-modal")).toHaveTextContent("closed"); + }); + + it("opens onboarding when there is an active paid plan and onboarding is incomplete", () => { + localStorage.removeItem("xtablo-onboarding-completed"); + mockUseOrganization.mockReturnValue({ + data: { + ...baseOrganizationData, + active_subscription_plan: "solo", + }, + isLoading: false, + error: null, + } as ReturnType); + + renderWithProviders(); + + expect(screen.getByTestId("onboarding-modal")).toHaveTextContent("open"); + }); }); diff --git a/apps/main/src/components/Layout.tsx b/apps/main/src/components/Layout.tsx index cf96f1b..a8fe0fe 100644 --- a/apps/main/src/components/Layout.tsx +++ b/apps/main/src/components/Layout.tsx @@ -3,6 +3,7 @@ import { MenuIcon, XIcon } from "lucide-react"; import { useCallback, useEffect, useState } from "react"; import { Outlet, useLocation } from "react-router-dom"; import { twMerge } from "tailwind-merge"; +import { useOrganization } from "../hooks/organization"; import { SideNavigation } from "./NavigationBar"; import { OnboardingModal } from "./OnboardingModal"; import { TopBar } from "./TopBar"; @@ -13,14 +14,19 @@ export function Layout() { const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); const [showOnboarding, setShowOnboarding] = useState(false); const location = useLocation(); + const { data: organizationData } = useOrganization(); useEffect(() => { - // Check if user has completed onboarding const hasCompletedOnboarding = localStorage.getItem(ONBOARDING_STORAGE_KEY); - if (!hasCompletedOnboarding) { + const hasPaidPlan = Boolean(organizationData?.active_subscription_plan); + + if (!hasCompletedOnboarding && hasPaidPlan) { setShowOnboarding(true); + return; } - }, []); + + setShowOnboarding(false); + }, [organizationData?.active_subscription_plan]); // Close mobile menu on route change useEffect(() => {