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 <openhands@all-hands.dev>
This commit is contained in:
openhands
2026-03-19 16:51:49 +00:00
parent 1304306173
commit 02d0d840ec
3 changed files with 29 additions and 35 deletions

View File

@@ -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 () => {

View File

@@ -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({
<Text className="text-[#8C8C8C]">{cardDescription}</Text>
</Card>
</div>
{showModal && <RequestSubmittedModal onClose={handleModalClose} />}
</div>
);
}

View File

@@ -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";
<PrefetchPageLinks page="/conversations/:conversationId" />;
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<GitRepository | null>(
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 (
<div
data-testid="home-screen"
@@ -57,6 +76,8 @@ function HomeScreen() {
<HomepageCTA setShouldShowCTA={setShouldShowCTA} />
</div>
)}
{showRequestModal && <RequestSubmittedModal onClose={handleModalClose} />}
</div>
);
}