From 02d0d840ec913f2b81cdba4b55e93643de213f0f Mon Sep 17 00:00:00 2001 From: openhands Date: Thu, 19 Mar 2026 16:51:49 +0000 Subject: [PATCH] refactor: show confirmation modal on homepage after form submit - Update form to navigate to '/' with state { showRequestSubmittedModal: true } - Update HomeScreen to check location state and show modal - Modal renders on homepage, not on form page - Clear location state after modal closes to prevent re-showing on refresh - Update tests for new navigation flow Flow: 1. User fills form and clicks Submit 2. Navigate to homepage with state 3. Homepage renders modal overlay 4. User clicks Done/Close -> modal closes, state cleared Co-authored-by: openhands --- .../information-request-form.test.tsx | 29 ++++--------------- .../onboarding/information-request-form.tsx | 12 ++------ frontend/src/routes/home.tsx | 23 ++++++++++++++- 3 files changed, 29 insertions(+), 35 deletions(-) diff --git a/frontend/__tests__/components/features/onboarding/information-request-form.test.tsx b/frontend/__tests__/components/features/onboarding/information-request-form.test.tsx index bf2fc710a4..c2fba958e9 100644 --- a/frontend/__tests__/components/features/onboarding/information-request-form.test.tsx +++ b/frontend/__tests__/components/features/onboarding/information-request-form.test.tsx @@ -180,18 +180,17 @@ describe("InformationRequestForm", () => { expect(messageInput).toHaveAttribute("aria-invalid", "false"); }); - it("should not show modal when form is submitted with empty fields", async () => { + it("should not navigate when form is submitted with empty fields", async () => { const user = userEvent.setup(); renderWithRouter(); const submitButton = screen.getByRole("button", { name: "ENTERPRISE$FORM_SUBMIT" }); await user.click(submitButton); - expect(screen.queryByTestId("request-submitted-modal")).not.toBeInTheDocument(); expect(mockNavigate).not.toHaveBeenCalled(); }); - it("should show modal when form is submitted with all fields filled", async () => { + it("should navigate to homepage with modal state when form is submitted with all fields filled", async () => { const user = userEvent.setup(); renderWithRouter(); @@ -203,27 +202,9 @@ describe("InformationRequestForm", () => { const submitButton = screen.getByRole("button", { name: "ENTERPRISE$FORM_SUBMIT" }); await user.click(submitButton); - expect(screen.getByTestId("request-submitted-modal")).toBeInTheDocument(); - }); - - it("should navigate to homepage when modal Done button is clicked", async () => { - const user = userEvent.setup(); - renderWithRouter(); - - // Fill form and submit - await user.type(screen.getByTestId("form-input-name"), "John Doe"); - await user.type(screen.getByTestId("form-input-company"), "Acme Inc"); - await user.type(screen.getByTestId("form-input-email"), "john@example.com"); - await user.type(screen.getByTestId("form-input-message"), "Hello world"); - - const submitButton = screen.getByRole("button", { name: "ENTERPRISE$FORM_SUBMIT" }); - await user.click(submitButton); - - // Click Done on modal - const doneButton = screen.getByRole("button", { name: "ENTERPRISE$DONE_BUTTON" }); - await user.click(doneButton); - - expect(mockNavigate).toHaveBeenCalledWith("/"); + expect(mockNavigate).toHaveBeenCalledWith("/", { + state: { showRequestSubmittedModal: true }, + }); }); it("should have valid aria-invalid state when field has value", async () => { diff --git a/frontend/src/components/features/onboarding/information-request-form.tsx b/frontend/src/components/features/onboarding/information-request-form.tsx index 088754ccbf..604ed63606 100644 --- a/frontend/src/components/features/onboarding/information-request-form.tsx +++ b/frontend/src/components/features/onboarding/information-request-form.tsx @@ -5,7 +5,6 @@ import { I18nKey } from "#/i18n/declaration"; import { Card } from "#/ui/card"; import { Text } from "#/ui/typography"; import { FormInput } from "./form-input"; -import { RequestSubmittedModal } from "./request-submitted-modal"; import OpenHandsLogoWhite from "#/assets/branding/openhands-logo-white.svg?react"; import CloudIcon from "#/icons/cloud-minimal.svg?react"; import StackedIcon from "#/icons/stacked.svg?react"; @@ -30,7 +29,6 @@ export function InformationRequestForm({ message: "", }); const [hasAttemptedSubmit, setHasAttemptedSubmit] = useState(false); - const [showModal, setShowModal] = useState(false); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); @@ -48,12 +46,8 @@ export function InformationRequestForm({ } // TODO: Implement actual form submission API call - setShowModal(true); - }; - - const handleModalClose = () => { - setShowModal(false); - navigate("/"); + // Navigate to homepage with state to show confirmation modal + navigate("/", { state: { showRequestSubmittedModal: true } }); }; const isSaas = requestType === "saas"; @@ -192,8 +186,6 @@ export function InformationRequestForm({ {cardDescription} - - {showModal && } ); } diff --git a/frontend/src/routes/home.tsx b/frontend/src/routes/home.tsx index 0d6069d62b..f87306ac13 100644 --- a/frontend/src/routes/home.tsx +++ b/frontend/src/routes/home.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { PrefetchPageLinks } from "react-router"; +import { PrefetchPageLinks, useLocation, useNavigate } from "react-router"; import { HomeHeader } from "#/components/features/home/home-header/home-header"; import { RepoConnector } from "#/components/features/home/repo-connector"; import { TaskSuggestions } from "#/components/features/home/tasks/task-suggestions"; @@ -7,14 +7,23 @@ import { GitRepository } from "#/types/git"; import { NewConversation } from "#/components/features/home/new-conversation/new-conversation"; import { RecentConversations } from "#/components/features/home/recent-conversations/recent-conversations"; import { HomepageCTA } from "#/components/features/home/homepage-cta"; +import { RequestSubmittedModal } from "#/components/features/onboarding/request-submitted-modal"; import { isCTADismissed } from "#/utils/local-storage"; import { useConfig } from "#/hooks/query/use-config"; import { ENABLE_PROJ_USER_JOURNEY } from "#/utils/feature-flags"; ; +interface LocationState { + showRequestSubmittedModal?: boolean; +} + function HomeScreen() { const { data: config } = useConfig(); + const location = useLocation(); + const navigate = useNavigate(); + const locationState = location.state as LocationState | null; + const [selectedRepo, setSelectedRepo] = React.useState( null, ); @@ -23,8 +32,18 @@ function HomeScreen() { () => !isCTADismissed("homepage"), ); + const [showRequestModal, setShowRequestModal] = React.useState( + () => locationState?.showRequestSubmittedModal ?? false, + ); + const isSaasMode = config?.app_mode === "saas"; + const handleModalClose = () => { + setShowRequestModal(false); + // Clear the state from location to prevent modal showing on refresh + navigate(location.pathname, { replace: true, state: {} }); + }; + return (
)} + + {showRequestModal && } ); }