Start the email flow directly from the Overview CTA

This commit is contained in:
Jonathan Singer
2026-07-20 13:46:35 -04:00
parent cfe1073650
commit ea0c107559
4 changed files with 58 additions and 26 deletions
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="color-scheme" content="dark" />
<title>Strix Results</title>
<script type="module" crossorigin src="./assets/index-BDEw1w2r.js"></script>
<script type="module" crossorigin src="./assets/index-CCsW0NU2.js"></script>
<link rel="stylesheet" crossorigin href="./assets/index-Cldwq1Im.css">
</head>
<body>
+11 -2
View File
@@ -76,6 +76,7 @@ export default function App() {
const [auth, setAuth] = useState<AuthStatus | null>(null);
const [runs, setRuns] = useState<RunsPayload | null>(null);
const [emailPurpose, setEmailPurpose] = useState<"report" | "verify">("report");
const [emailSkipDisclosure, setEmailSkipDisclosure] = useState(false);
const refreshAuth = useCallback(async () => {
try {
@@ -182,15 +183,22 @@ export default function App() {
setView("overview");
}, []);
const openEmail = useCallback(() => {
const goEmail = useCallback((skipDisclosure: boolean) => {
trackCta("email_report");
setEmailPurpose("report");
setEmailSkipDisclosure(skipDisclosure);
setView("email");
}, []);
// Sidebar entry keeps the disclosure (first place those users see it);
const openEmail = useCallback(() => goEmail(false), [goEmail]);
// the Overview CTA already states the tradeoff, so it starts the flow directly.
const openEmailFromOverview = useCallback(() => goEmail(true), [goEmail]);
const openVerify = useCallback(() => {
trackCta("history_unlock");
setEmailPurpose("verify");
setEmailSkipDisclosure(false);
setView("email");
}, []);
@@ -292,6 +300,7 @@ export default function App() {
activeRun={activeRun}
auth={auth}
purpose={emailPurpose}
skipDisclosure={emailSkipDisclosure}
onAuthChanged={() => {
void refreshAuth();
void refreshRuns();
@@ -343,7 +352,7 @@ export default function App() {
counts={counts}
total={run.vulnerabilities.length}
reportMarkdown={run.reportMarkdown}
onOpenEmail={openEmail}
onOpenEmail={openEmailFromOverview}
/>
) : view === "agents" && agentCount > 0 ? (
<AgentsTab run={run} />
@@ -1,4 +1,4 @@
import { useState } from "react";
import { useEffect, useRef, useState } from "react";
import { Mail, ShieldCheck, Lock, Copy, Check, Loader2, AlertCircle, ArrowLeft } from "lucide-react";
import {
otpStart,
@@ -21,6 +21,12 @@ interface EmailReportViewProps {
activeRun: string | null;
auth: AuthStatus | null;
purpose: "report" | "verify";
/**
* Skip the report disclosure and start the flow directly (used by the
* Overview CTA, which already states the tradeoff). Unverified users land on
* the email step; already-verified users send immediately.
*/
skipDisclosure?: boolean;
/** Refresh auth + runs after a successful verify (lifts state to App). */
onAuthChanged: () => void;
/** Leave this page (report "Done" -> overview; verify success -> history). */
@@ -52,13 +58,19 @@ export default function EmailReportView({
activeRun,
auth,
purpose,
skipDisclosure = false,
onAuthChanged,
onExit,
}: EmailReportViewProps) {
const verified = auth?.verified === true;
const verifyOnly = purpose === "verify";
// Verify mode skips the report disclosure and goes straight to the email step.
const [step, setStep] = useState<Step>(verifyOnly ? "email" : "disclosure");
// Verify mode (and the Overview CTA, which skips the disclosure) start on the
// email step; a verified user who skips the disclosure sends immediately.
const [step, setStep] = useState<Step>(() => {
if (verifyOnly) return "email";
if (skipDisclosure) return verified ? "sending" : "email";
return "disclosure";
});
const [email, setEmail] = useState(auth?.email ?? "");
const [code, setCode] = useState("");
const [busy, setBusy] = useState(false);
@@ -68,6 +80,7 @@ export default function EmailReportView({
const [filename, setFilename] = useState("");
const [copied, setCopied] = useState(false);
const [sentTo, setSentTo] = useState("");
const autoSentRef = useRef(false);
const doSend = async () => {
setStep("sending");
@@ -95,6 +108,16 @@ export default function EmailReportView({
else setStep("email");
};
// A verified user who skipped the disclosure (Overview CTA) sends on arrival.
useEffect(() => {
if (!verifyOnly && skipDisclosure && verified && !autoSentRef.current) {
autoSentRef.current = true;
void doSend();
}
// Run once on mount; the page remounts fresh each time it is opened.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const submitEmail = async () => {
const value = email.trim();
if (!value) {