fix onboarding modal
This commit is contained in:
parent
3fd0f3dcde
commit
0476a87afd
3 changed files with 79 additions and 5 deletions
|
|
@ -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"}
|
||||
{"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"}
|
||||
|
|
@ -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<typeof import("../hooks/organization")>();
|
||||
return {
|
||||
...actual,
|
||||
useOrganization: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("./OnboardingModal", () => ({
|
||||
OnboardingModal: ({ open }: { open: boolean }) => (
|
||||
<div data-testid="onboarding-modal">{open ? "open" : "closed"}</div>
|
||||
),
|
||||
}));
|
||||
|
||||
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<typeof useOrganization>);
|
||||
});
|
||||
|
||||
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(<Layout />);
|
||||
|
||||
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<typeof useOrganization>);
|
||||
|
||||
renderWithProviders(<Layout />);
|
||||
|
||||
expect(screen.getByTestId("onboarding-modal")).toHaveTextContent("open");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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(() => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue