From 5b450f22cf06cb5de5f8ca6bfb17fd48dc1e3d18 Mon Sep 17 00:00:00 2001 From: Saksham Das Date: Wed, 5 Aug 2026 21:07:32 -0400 Subject: [PATCH] Prefill notification email in Add Company wizard with the account's own email Was always blank by default. Now prefilled from useCurrentUser() once it loads (defaultValues can't do this since the query resolves after mount), gated on the field not being dirty yet so it never clobbers something the user already typed - still fully editable either way. Co-Authored-By: Claude Sonnet 5 --- apps/web/app/(app)/companies/new/page.tsx | 17 +++++++++-- apps/web/tests/add-company-wizard.test.tsx | 35 ++++++++++++++++++++++ 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/apps/web/app/(app)/companies/new/page.tsx b/apps/web/app/(app)/companies/new/page.tsx index 7c5c0c3..4e76868 100644 --- a/apps/web/app/(app)/companies/new/page.tsx +++ b/apps/web/app/(app)/companies/new/page.tsx @@ -3,7 +3,7 @@ import { zodResolver } from "@hookform/resolvers/zod"; import Link from "next/link"; import { useRouter, useSearchParams } from "next/navigation"; -import { Suspense, useState } from "react"; +import { Suspense, useEffect, useState } from "react"; import { Controller, useForm } from "react-hook-form"; import { Loader2 } from "lucide-react"; import { z } from "zod"; @@ -13,7 +13,7 @@ import { api } from "@/lib/api-client"; import { useCompanies, useCreateCompany } from "@/hooks/use-companies"; import { useCreateNotificationDestination } from "@/hooks/use-notification-destinations"; import { useDiscoverCompany } from "@/hooks/use-discovery"; -import { authErrorMessage, useSystemStatus } from "@/hooks/use-auth"; +import { authErrorMessage, useCurrentUser, useSystemStatus } from "@/hooks/use-auth"; import { FREQUENCY_LABELS, MONITORING_FREQUENCIES, @@ -126,6 +126,7 @@ function AddCompanyWizard() { const createDestination = useCreateNotificationDestination(); const discoverCompany = useDiscoverCompany(); const { data: systemStatus } = useSystemStatus(); + const { data: currentUser } = useCurrentUser(); const { register, @@ -135,7 +136,7 @@ function AddCompanyWizard() { setValue, getValues, trigger, - formState: { errors }, + formState: { errors, dirtyFields }, } = useForm({ resolver: zodResolver(companySchema), defaultValues: { @@ -146,6 +147,16 @@ function AddCompanyWizard() { }, }); + // Prefill with the account's own email once it loads - `defaultValues` + // can't do this since useCurrentUser resolves after the form has already + // mounted. Only sets it while the field is still untouched, so it never + // clobbers something the user already typed. + useEffect(() => { + if (currentUser?.email && !dirtyFields.notificationEmail) { + setValue("notificationEmail", currentUser.email); + } + }, [currentUser?.email, dirtyFields.notificationEmail, setValue]); + const frequencyType = watch("frequencyType"); const goNext = async () => { diff --git a/apps/web/tests/add-company-wizard.test.tsx b/apps/web/tests/add-company-wizard.test.tsx index efcb9f6..dee7e79 100644 --- a/apps/web/tests/add-company-wizard.test.tsx +++ b/apps/web/tests/add-company-wizard.test.tsx @@ -51,6 +51,16 @@ const EXISTING_COMPANIES = [ }, ]; +const CURRENT_USER = { + id: "user-1", + email: "team@example.com", + display_name: "Team Person", + timezone: "America/New_York", + is_active: true, + is_admin: false, + auth_mode: "local", +}; + function mockFetchImplementation(existingCompanies: unknown[] = []) { return vi.fn().mockImplementation((url: string, init?: RequestInit) => { const path = url.replace("http://localhost:8000", ""); @@ -60,6 +70,9 @@ function mockFetchImplementation(existingCompanies: unknown[] = []) { if (path === "/api/v1/companies" && (!init?.method || init.method === "GET")) { return Promise.resolve({ ok: true, status: 200, json: async () => existingCompanies }); } + if (path === "/api/v1/auth/me") { + return Promise.resolve({ ok: true, status: 200, json: async () => CURRENT_USER }); + } return Promise.resolve({ ok: false, status: 404, json: async () => ({ detail: "not found" }) }); }); } @@ -128,4 +141,26 @@ describe("AddCompanyWizardPage", () => { }); expect(screen.queryByText(/you might already be monitoring/i)).not.toBeInTheDocument(); }); + + it("pre-fills the notification email with the account's own email, still editable", async () => { + const user = userEvent.setup(); + renderWithQueryClient(); + + await user.type(screen.getByLabelText("Company name"), "Acme Widgets"); + await user.click(screen.getByRole("button", { name: /discover company/i })); + await waitFor(() => { + expect(screen.getByText(/here's what we found for/i)).toBeInTheDocument(); + }); + + await user.click(screen.getByRole("button", { name: /^continue$/i })); // Review -> Schedule + await user.click(screen.getByRole("button", { name: /^continue$/i })); // Schedule -> Notifications + + const email = (await screen.findByLabelText("Notification email")) as HTMLInputElement; + await waitFor(() => expect(email.value).toBe("team@example.com")); + + // Still editable - prefilling must not lock the field. + await user.clear(email); + await user.type(email, "someone-else@example.com"); + expect(email.value).toBe("someone-else@example.com"); + }); });