Преглед изворни кода

chore(frontend): Remove initial analytics modal and update waitlist modal (#5416)

sp.wack пре 1 година
родитељ
комит
910b2a9b9e

+ 45 - 0
frontend/__tests__/components/features/waitlist-modal.test.tsx

@@ -0,0 +1,45 @@
+import { render, screen } from "@testing-library/react";
+import { it, describe, expect, vi } from "vitest";
+import userEvent from "@testing-library/user-event";
+import { WaitlistModal } from "#/components/features/waitlist/waitlist-modal";
+import * as CaptureConsent from "#/utils/handle-capture-consent";
+
+describe("WaitlistModal", () => {
+  it("should render a tos checkbox that is unchecked by default", () => {
+    render(<WaitlistModal ghToken={null} githubAuthUrl={null} />);
+    const checkbox = screen.getByRole("checkbox");
+
+    expect(checkbox).not.toBeChecked();
+  });
+
+  it("should only enable the GitHub button if the tos checkbox is checked", async () => {
+    const user = userEvent.setup();
+    render(<WaitlistModal ghToken={null} githubAuthUrl={null} />);
+    const checkbox = screen.getByRole("checkbox");
+    const button = screen.getByRole("button", { name: "Connect to GitHub" });
+
+    expect(button).toBeDisabled();
+
+    await user.click(checkbox);
+
+    expect(button).not.toBeDisabled();
+  });
+
+  it("should set user analytics consent to true when the user checks the tos checkbox", async () => {
+    const handleCaptureConsentSpy = vi.spyOn(
+      CaptureConsent,
+      "handleCaptureConsent",
+    );
+
+    const user = userEvent.setup();
+    render(<WaitlistModal ghToken={null} githubAuthUrl="mock-url" />);
+
+    const checkbox = screen.getByRole("checkbox");
+    await user.click(checkbox);
+
+    const button = screen.getByRole("button", { name: "Connect to GitHub" });
+    await user.click(button);
+
+    expect(handleCaptureConsentSpy).toHaveBeenCalledWith(true);
+  });
+});

+ 27 - 2
frontend/__tests__/routes/_oh.test.tsx

@@ -4,8 +4,9 @@ import { screen, waitFor, within } from "@testing-library/react";
 import { renderWithProviders } from "test-utils";
 import userEvent from "@testing-library/user-event";
 import MainApp from "#/routes/_oh/route";
-import * as CaptureConsent from "#/utils/handle-capture-consent";
 import i18n from "#/i18n";
+import * as CaptureConsent from "#/utils/handle-capture-consent";
+import OpenHands from "#/api/open-hands";
 
 describe("frontend/routes/_oh", () => {
   const RouteStub = createRoutesStub([{ Component: MainApp, path: "/" }]);
@@ -60,13 +61,20 @@ describe("frontend/routes/_oh", () => {
     });
   });
 
-  it("should capture the user's consent", async () => {
+  it("should render and capture the user's consent if oss mode", async () => {
     const user = userEvent.setup();
+    const getConfigSpy = vi.spyOn(OpenHands, "getConfig");
     const handleCaptureConsentSpy = vi.spyOn(
       CaptureConsent,
       "handleCaptureConsent",
     );
 
+    getConfigSpy.mockResolvedValue({
+      APP_MODE: "oss",
+      GITHUB_CLIENT_ID: "test-id",
+      POSTHOG_CLIENT_KEY: "test-key",
+    });
+
     renderWithProviders(<RouteStub />);
 
     // The user has not consented to tracking
@@ -87,6 +95,23 @@ describe("frontend/routes/_oh", () => {
     ).not.toBeInTheDocument();
   });
 
+  it("should not render the user consent form if saas mode", async () => {
+    const getConfigSpy = vi.spyOn(OpenHands, "getConfig");
+    getConfigSpy.mockResolvedValue({
+      APP_MODE: "saas",
+      GITHUB_CLIENT_ID: "test-id",
+      POSTHOG_CLIENT_KEY: "test-key",
+    });
+
+    renderWithProviders(<RouteStub />);
+
+    await waitFor(() => {
+      expect(
+        screen.queryByTestId("user-capture-consent-form"),
+      ).not.toBeInTheDocument();
+    });
+  });
+
   it("should not render the user consent form if the user has already made a decision", async () => {
     localStorage.setItem("analytics-consent", "true");
     renderWithProviders(<RouteStub />);

+ 22 - 0
frontend/src/components/features/waitlist/tos-checkbox.tsx

@@ -0,0 +1,22 @@
+interface TOSCheckboxProps {
+  onChange: () => void;
+}
+
+export function TOSCheckbox({ onChange }: TOSCheckboxProps) {
+  return (
+    <label className="flex items-center gap-2">
+      <input type="checkbox" onChange={onChange} />
+      <span>
+        I accept the{" "}
+        <a
+          href="https://www.all-hands.dev/tos"
+          target="_blank"
+          rel="noopener noreferrer"
+          className="underline underline-offset-2 text-blue-500 hover:text-blue-700"
+        >
+          terms of service
+        </a>
+      </span>
+    </label>
+  );
+}

+ 16 - 5
frontend/src/components/features/waitlist/waitlist-modal.tsx

@@ -1,3 +1,4 @@
+import React from "react";
 import GitHubLogo from "#/assets/branding/github-logo.svg?react";
 import AllHandsLogo from "#/assets/branding/all-hands-logo.svg?react";
 import { JoinWaitlistAnchor } from "./join-waitlist-anchor";
@@ -5,6 +6,8 @@ import { WaitlistMessage } from "./waitlist-message";
 import { ModalBackdrop } from "#/components/shared/modals/modal-backdrop";
 import { ModalButton } from "#/components/shared/buttons/modal-button";
 import { ModalBody } from "#/components/shared/modals/modal-body";
+import { TOSCheckbox } from "./tos-checkbox";
+import { handleCaptureConsent } from "#/utils/handle-capture-consent";
 
 interface WaitlistModalProps {
   ghToken: string | null;
@@ -12,22 +15,30 @@ interface WaitlistModalProps {
 }
 
 export function WaitlistModal({ ghToken, githubAuthUrl }: WaitlistModalProps) {
+  const [isTosAccepted, setIsTosAccepted] = React.useState(false);
+
+  const handleGitHubAuth = () => {
+    if (githubAuthUrl) {
+      handleCaptureConsent(true);
+      window.location.href = githubAuthUrl;
+    }
+  };
+
   return (
     <ModalBackdrop>
       <ModalBody>
         <AllHandsLogo width={68} height={46} />
         <WaitlistMessage content={ghToken ? "waitlist" : "sign-in"} />
 
+        <TOSCheckbox onChange={() => setIsTosAccepted((prev) => !prev)} />
+
         {!ghToken && (
           <ModalButton
+            disabled={!isTosAccepted}
             text="Connect to GitHub"
             icon={<GitHubLogo width={20} height={20} />}
             className="bg-[#791B80] w-full"
-            onClick={() => {
-              if (githubAuthUrl) {
-                window.location.href = githubAuthUrl;
-              }
-            }}
+            onClick={handleGitHubAuth}
           />
         )}
         {ghToken && <JoinWaitlistAnchor />}

+ 3 - 2
frontend/src/routes/_oh/route.tsx

@@ -6,9 +6,9 @@ import { useIsAuthed } from "#/hooks/query/use-is-authed";
 import { useAuth } from "#/context/auth-context";
 import { useUserPrefs } from "#/context/user-prefs-context";
 import { useConfig } from "#/hooks/query/use-config";
-import { AnalyticsConsentFormModal } from "#/components/features/analytics/analytics-consent-form-modal";
 import { Sidebar } from "#/components/features/sidebar/sidebar";
 import { WaitlistModal } from "#/components/features/waitlist/waitlist-modal";
+import { AnalyticsConsentFormModal } from "#/components/features/analytics/analytics-consent-form-modal";
 
 export function ErrorBoundary() {
   const error = useRouteError();
@@ -90,7 +90,8 @@ export default function MainApp() {
       {isInWaitlist && (
         <WaitlistModal ghToken={gitHubToken} githubAuthUrl={gitHubAuthUrl} />
       )}
-      {consentFormIsOpen && (
+
+      {config.data?.APP_MODE === "oss" && consentFormIsOpen && (
         <AnalyticsConsentFormModal
           onClose={() => setConsentFormIsOpen(false)}
         />