Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions e2e/smoke/public-reporting.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}) => {
Expand Down
21 changes: 18 additions & 3 deletions src/app/(auth)/signup/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<React.JSX.Element> {
// Check if already logged in
const supabase = await createClient();
Expand All @@ -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) {
Expand All @@ -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 (
Expand Down
10 changes: 10 additions & 0 deletions src/app/report/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
25 changes: 23 additions & 2 deletions src/app/report/success/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<React.JSX.Element> {
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 (
<main className="min-h-screen bg-surface py-12">
<div className="container mx-auto max-w-xl px-4">
Expand Down Expand Up @@ -50,7 +71,7 @@ export default async function ReportSuccessPage({
size="sm"
className="w-full bg-primary text-on-primary"
>
<Link href="/signup">Join PinPoint</Link>
<Link href={signupUrl}>Join PinPoint</Link>
</Button>
</div>
)}
Expand Down