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"); + }); });