diff --git a/e2e/smoke/public-reporting.spec.ts b/e2e/smoke/public-reporting.spec.ts index c2066583..974ebbe6 100644 --- a/e2e/smoke/public-reporting.spec.ts +++ b/e2e/smoke/public-reporting.spec.ts @@ -79,6 +79,75 @@ test.describe("Public Issue Reporting", () => { ).toBeVisible(); }); + test("should pre-fill name on signup when provided without email", async ({ + page, + }) => { + await page.goto("/report"); + await page.getByTestId("machine-select").selectOption({ index: 1 }); + // Wait for URL refresh (router.push) to prevent race conditions on Mobile Safari + await expect(page).toHaveURL(/machine=/); + + await page + .getByLabel("Issue Title *") + .fill(`${PUBLIC_PREFIX} with Name Only`); + await selectOption(page, "severity-select", "minor"); + + // Provide name but no email + await page.getByLabel("First Name").fill("Jane"); + await page.getByLabel("Last Name").fill("Reporter"); + + await page.getByRole("button", { name: "Submit Issue Report" }).click(); + + // Should redirect to success page with new_pending flag + await expect(page).toHaveURL(/\/report\/success/); + await expect(page.getByText("Want to track your reports?")).toBeVisible(); + + // Click the signup link + await page.getByRole("link", { name: "Join PinPoint" }).click(); + + // Should redirect to signup page with name pre-filled + await expect(page).toHaveURL(/\/signup\?/); + await expect(page.getByLabel(/First Name/i)).toHaveValue("Jane"); + await expect(page.getByLabel(/Last Name/i)).toHaveValue("Reporter"); + }); + + test("should pre-fill name and email on signup when both provided", async ({ + page, + }) => { + const timestamp = Date.now(); + const email = `reporter-${timestamp}@example.com`; + + await page.goto("/report"); + await page.getByTestId("machine-select").selectOption({ index: 1 }); + // Wait for URL refresh (router.push) to prevent race conditions on Mobile Safari + await expect(page).toHaveURL(/machine=/); + + await page + .getByLabel("Issue Title *") + .fill(`${PUBLIC_PREFIX} with Name and Email`); + await selectOption(page, "severity-select", "minor"); + + // Provide name and email + await page.getByLabel("First Name").fill("John"); + await page.getByLabel("Last Name").fill("Smith"); + await page.getByLabel("Email Address").fill(email); + + await page.getByRole("button", { name: "Submit Issue Report" }).click(); + + // Should redirect to success page with new_pending flag + await expect(page).toHaveURL(/\/report\/success/); + await expect(page.getByText("Want to track your reports?")).toBeVisible(); + + // Click the signup link + await page.getByRole("link", { name: "Join PinPoint" }).click(); + + // Should redirect to signup page with name and email pre-filled + await expect(page).toHaveURL(/\/signup\?/); + await expect(page.getByLabel(/First Name/i)).toHaveValue("John"); + await expect(page.getByLabel(/Last Name/i)).toHaveValue("Smith"); + await expect(page.getByLabel(/Email/i)).toHaveValue(email); + }); + test("should allow reporting another issue from success page", async ({ page, }) => { diff --git a/src/app/(auth)/signup/page.tsx b/src/app/(auth)/signup/page.tsx index 6326790a..4223923c 100644 --- a/src/app/(auth)/signup/page.tsx +++ b/src/app/(auth)/signup/page.tsx @@ -19,7 +19,11 @@ import { eq } from "drizzle-orm"; export default async function SignupPage({ searchParams, }: { - searchParams: Promise<{ email?: string }>; + searchParams: Promise<{ + email?: string; + firstName?: string; + lastName?: string; + }>; }): Promise { // Check if already logged in const supabase = await createClient(); @@ -31,7 +35,7 @@ export default async function SignupPage({ redirect("/dashboard"); } - const { email } = await searchParams; + const { email, firstName, lastName } = await searchParams; let initialData = undefined; if (email) { @@ -47,8 +51,19 @@ export default async function SignupPage({ }; } else { // If email is provided but no invited user found, we still pre-fill the email - initialData = { email }; + // Also include firstName and lastName from query params if provided + initialData = { + email, + ...(firstName && { firstName }), + ...(lastName && { lastName }), + }; } + } else if (firstName || lastName) { + // No email but name provided (e.g., from report success page) + initialData = { + ...(firstName && { firstName }), + ...(lastName && { lastName }), + }; } return ( diff --git a/src/app/report/actions.ts b/src/app/report/actions.ts index 151a9f5e..eeeefe39 100644 --- a/src/app/report/actions.ts +++ b/src/app/report/actions.ts @@ -195,6 +195,16 @@ export async function submitPublicIssueAction( // We can use reporterEmail or reporterName to decide if they provided info if ((reporterEmail || reporterName) && !reportedBy) { successParams.set("new_pending", "true"); + // Pass name and email information to success page for signup pre-fill + if (firstName) { + successParams.set("firstName", firstName); + } + if (lastName) { + successParams.set("lastName", lastName); + } + if (reporterEmail) { + successParams.set("email", reporterEmail); + } } const successUrl = successParams.toString() diff --git a/src/app/report/success/page.tsx b/src/app/report/success/page.tsx index 3b660f42..8b9fd32e 100644 --- a/src/app/report/success/page.tsx +++ b/src/app/report/success/page.tsx @@ -7,11 +7,32 @@ import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card"; export default async function ReportSuccessPage({ searchParams, }: { - searchParams: Promise<{ new_pending?: string }>; + searchParams: Promise<{ + new_pending?: string; + firstName?: string; + lastName?: string; + email?: string; + }>; }): Promise { const params = await searchParams; const isNewPending = params.new_pending === "true"; + // Build signup URL with pre-filled name and email data + const signupParams = new URLSearchParams(); + if (params.firstName) { + signupParams.set("firstName", params.firstName); + } + if (params.lastName) { + signupParams.set("lastName", params.lastName); + } + if (params.email) { + signupParams.set("email", params.email); + } + const signupUrl = + signupParams.toString() !== "" + ? `/signup?${signupParams.toString()}` + : "/signup"; + return (
@@ -50,7 +71,7 @@ export default async function ReportSuccessPage({ size="sm" className="w-full bg-primary text-on-primary" > - Join PinPoint + Join PinPoint
)}