feat: add PostHog tracking for enterprise lead form submission

- Add trackEnterpriseLeadFormSubmitted function to use-tracking.ts
- Track form data on submit: requestType (saas/self-hosted), name, company, email, message
- Add inline styles to onboarding-layout.tsx to prevent FOUC
- Add 4 new tests for tracking behavior (25 total tests)

PostHog event: 'enterprise_lead_form_submitted'
Properties: request_type, name, company, email, message, plus common properties

Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
openhands
2026-03-19 17:41:29 +00:00
parent 1773afdcd7
commit a515a96f0a
3 changed files with 117 additions and 0 deletions

View File

@@ -14,6 +14,14 @@ vi.mock("react-router", async () => {
};
});
// Mock useTracking
const mockTrackEnterpriseLeadFormSubmitted = vi.fn();
vi.mock("#/hooks/use-tracking", () => ({
useTracking: () => ({
trackEnterpriseLeadFormSubmitted: mockTrackEnterpriseLeadFormSubmitted,
}),
}));
describe("InformationRequestForm", () => {
const defaultProps = {
requestType: "saas" as RequestType,
@@ -190,6 +198,16 @@ describe("InformationRequestForm", () => {
expect(mockNavigate).not.toHaveBeenCalled();
});
it("should not call tracking 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(mockTrackEnterpriseLeadFormSubmitted).not.toHaveBeenCalled();
});
it("should navigate to homepage with modal state when form is submitted with all fields filled", async () => {
const user = userEvent.setup();
renderWithRouter();
@@ -207,6 +225,70 @@ describe("InformationRequestForm", () => {
});
});
it("should call tracking with form data when form is submitted successfully", async () => {
const user = userEvent.setup();
renderWithRouter({ ...defaultProps, requestType: "saas" });
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);
expect(mockTrackEnterpriseLeadFormSubmitted).toHaveBeenCalledTimes(1);
expect(mockTrackEnterpriseLeadFormSubmitted).toHaveBeenCalledWith({
requestType: "saas",
name: "John Doe",
company: "Acme Inc",
email: "john@example.com",
message: "Hello world",
});
});
it("should call tracking with self-hosted request type", async () => {
const user = userEvent.setup();
renderWithRouter({ ...defaultProps, requestType: "self-hosted" });
await user.type(screen.getByTestId("form-input-name"), "Jane Smith");
await user.type(screen.getByTestId("form-input-company"), "Tech Corp");
await user.type(screen.getByTestId("form-input-email"), "jane@techcorp.com");
await user.type(screen.getByTestId("form-input-message"), "Interested in self-hosted");
const submitButton = screen.getByRole("button", { name: "ENTERPRISE$FORM_SUBMIT" });
await user.click(submitButton);
expect(mockTrackEnterpriseLeadFormSubmitted).toHaveBeenCalledWith({
requestType: "self-hosted",
name: "Jane Smith",
company: "Tech Corp",
email: "jane@techcorp.com",
message: "Interested in self-hosted",
});
});
it("should trim whitespace from form fields before tracking", async () => {
const user = userEvent.setup();
renderWithRouter();
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);
expect(mockTrackEnterpriseLeadFormSubmitted).toHaveBeenCalledWith({
requestType: "saas",
name: "John Doe",
company: "Acme Inc",
email: "john@example.com",
message: "Hello world",
});
});
it("should have valid aria-invalid state when field has value", async () => {
const user = userEvent.setup();
renderWithRouter();

View File

@@ -2,6 +2,7 @@ import { useState } from "react";
import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router";
import { I18nKey } from "#/i18n/declaration";
import { useTracking } from "#/hooks/use-tracking";
import { Card } from "#/ui/card";
import { Text } from "#/ui/typography";
import { FormInput } from "./form-input";
@@ -22,6 +23,7 @@ export function InformationRequestForm({
}: InformationRequestFormProps) {
const { t } = useTranslation();
const navigate = useNavigate();
const { trackEnterpriseLeadFormSubmitted } = useTracking();
const [formData, setFormData] = useState({
name: "",
company: "",
@@ -46,6 +48,15 @@ export function InformationRequestForm({
}
// TODO: Implement actual form submission API call
// Track form submission in PostHog
trackEnterpriseLeadFormSubmitted({
requestType,
name: formData.name.trim(),
company: formData.company.trim(),
email: formData.email.trim(),
message: formData.message.trim(),
});
// Navigate to homepage with state to show confirmation modal
navigate("/", { state: { showRequestSubmittedModal: true } });
};

View File

@@ -129,6 +129,29 @@ export const useTracking = () => {
});
};
const trackEnterpriseLeadFormSubmitted = ({
requestType,
name,
company,
email,
message,
}: {
requestType: "saas" | "self-hosted";
name: string;
company: string;
email: string;
message: string;
}) => {
posthog.capture("enterprise_lead_form_submitted", {
request_type: requestType,
name,
company,
email,
message,
...commonProperties,
});
};
return {
trackLoginButtonClick,
trackConversationCreated,
@@ -142,5 +165,6 @@ export const useTracking = () => {
trackAddTeamMembersButtonClick,
trackOnboardingCompleted,
trackSaasSelfhostedInquiry,
trackEnterpriseLeadFormSubmitted,
};
};