failproofai 0.0.11-beta.7 → 0.0.11-beta.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/app-path-routes-manifest.json +1 -0
- package/.next/standalone/.next/build-manifest.json +6 -6
- package/.next/standalone/.next/prerender-manifest.json +3 -3
- package/.next/standalone/.next/required-server-files.json +1 -1
- package/.next/standalone/.next/routes-manifest.json +6 -0
- package/.next/standalone/.next/server/app/_global-error/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js +2 -2
- package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +15 -15
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +10 -10
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/api/audit/invite/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/audit/invite/route/build-manifest.json +9 -0
- package/.next/standalone/.next/server/app/api/audit/invite/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/audit/invite/route.js +7 -0
- package/.next/standalone/.next/server/app/api/audit/invite/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/audit/invite/route_client-reference-manifest.js +3 -0
- package/.next/standalone/.next/server/app/api/audit/run/route.js +1 -1
- package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-request/route.js +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-verify/route.js +2 -2
- package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/logout/route.js +2 -2
- package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/reminder/route.js +2 -2
- package/.next/standalone/.next/server/app/api/auth/reminder/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/status/route.js +2 -2
- package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/audit/page.js +2 -2
- package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +15 -15
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +15 -15
- package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +10 -10
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/page.js +2 -2
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/policies/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +8 -8
- package/.next/standalone/.next/server/app/policies/page.js +2 -2
- package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page.js +3 -3
- package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +3 -3
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/projects/page/build-manifest.json +3 -3
- package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/projects/page.js +3 -3
- package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app-paths-manifest.json +1 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1_mqemn._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1r1h8v9._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1rhmvod._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1uatkiv._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1y6gxxb._.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_audit_invite_route_actions_0-2n5sy.js +3 -0
- package/.next/standalone/.next/server/chunks/node_modules_0-tu4ot._.js +3 -0
- package/.next/standalone/.next/server/chunks/node_modules_0ttxbz7._.js +3 -0
- package/.next/standalone/.next/server/chunks/node_modules_1bnh1y0._.js +3 -0
- package/.next/standalone/.next/server/chunks/node_modules_1epycqa._.js +3 -0
- package/.next/standalone/.next/server/chunks/node_modules_1wpdcgo._.js +3 -0
- package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_17k9e3w.js +8 -8
- package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_01r25oi._.js +1 -1
- package/.next/standalone/.next/server/chunks/node_modules_posthog-node_dist_entrypoints_index_node_mjs_09z9-p7._.js +1 -1
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0808sha._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1cd25c7._.js → [root-of-the-server]__0e4-6d8._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__08gyb62._.js → [root-of-the-server]__0e446gb._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ehe24g._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1-scthx._.js → [root-of-the-server]__0f62vu9._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0g253ve._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0l13qf2._.js → [root-of-the-server]__0k65l27._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0vxf0_g._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__08v7sn2._.js → [root-of-the-server]__0wprfyc._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__12mcauo._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0989_dx._.js → [root-of-the-server]__1e-x7j4._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1mt35_w._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1pcxxwg._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1uvfwgr._.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/_05whahf._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0il3fl1._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_11_p9y8._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1kje4fm._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +21 -21
- package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/node_modules_html-to-image_es_index_0y4a-0q.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/node_modules_html-to-image_es_index_1ao30b1.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/node_modules_posthog-node_dist_entrypoints_index_node_mjs_11bnuzn._.js +1 -1
- package/.next/standalone/.next/server/functions-config-manifest.json +0 -3
- package/.next/standalone/.next/server/middleware-build-manifest.js +6 -6
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +10 -10
- package/.next/standalone/.next/static/chunks/{0hmxjseydp6as.js → 07_d165p5h5ys.js} +1 -1
- package/.next/standalone/.next/static/chunks/0f7d7hnbh4djs.js +1 -0
- package/.next/standalone/.next/static/chunks/0h7auy7hzjyhw.js +1 -0
- package/.next/standalone/.next/static/chunks/168k-8z6k7e8z.css +1 -0
- package/.next/standalone/.next/static/chunks/{3n28elq6kwpy3.js → 1kvadxkgnapyj.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0fpw1jzeg66_x.js → 277oc363p56n6.js} +2 -2
- package/.next/standalone/.next/static/chunks/28mkxkl_d91-l.js +1 -0
- package/.next/standalone/.next/static/chunks/28x7jvo3kxd3u.js +41 -0
- package/.next/standalone/.next/static/chunks/29nrs5xs9c4hx.css +2 -0
- package/.next/standalone/.next/static/chunks/{41vwoxpkauj1w.js → 29tg7deqmq32l.js} +1 -1
- package/.next/standalone/.next/static/chunks/{3w8d8k_dca5rp.js → 2h0dkzyy0vocp.js} +1 -1
- package/.next/standalone/.next/static/chunks/{1eq7f419t6ln3.js → 2z42u62k-8-_q.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0avgb78edu0i-.js → 3nj6g3xu9uy78.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0xkxbaa-oo180.js → 3ty6dhcuogout.js} +1 -1
- package/.next/standalone/.next/static/chunks/3zkg2s2vzxc3d.js +1 -0
- package/.next/standalone/.next/static/chunks/{turbopack-00qy7zfa7m--m.js → turbopack-3lrm4f20fz89b.js} +1 -1
- package/.next/standalone/app/api/audit/_state.ts +40 -33
- package/.next/standalone/app/api/audit/invite/route.ts +183 -0
- package/.next/standalone/app/api/audit/run/route.ts +30 -16
- package/.next/standalone/app/api/audit/status/route.ts +1 -0
- package/.next/standalone/app/audit/_components/audit-dashboard.tsx +33 -65
- package/.next/standalone/app/audit/_components/audit-poster.tsx +322 -0
- package/.next/standalone/app/audit/_components/audit-progress-strip.tsx +1 -1
- package/.next/standalone/app/audit/_components/auth-dialog.tsx +21 -49
- package/.next/standalone/app/audit/_components/come-back-better-section.tsx +316 -0
- package/.next/standalone/app/audit/_components/empty-state.tsx +8 -2
- package/.next/standalone/app/audit/_components/how-to-improve-section.tsx +187 -0
- package/.next/standalone/app/audit/_components/invite-dialog.tsx +227 -0
- package/.next/standalone/app/audit/_components/quirks-section.tsx +75 -0
- package/.next/standalone/app/audit/_components/rerun-button.tsx +43 -14
- package/.next/standalone/app/audit/_components/run-progress.tsx +2 -2
- package/.next/standalone/app/audit/_components/share-templates.ts +23 -22
- package/.next/standalone/app/audit/_components/sigil.tsx +9 -66
- package/.next/standalone/app/audit/_components/strengths-section.tsx +20 -32
- package/.next/standalone/app/audit/audit-styles.css +781 -1784
- package/.next/standalone/app/components/sessions-list.tsx +77 -80
- package/.next/standalone/app/globals.css +214 -32
- package/.next/standalone/app/layout.tsx +1 -10
- package/.next/standalone/app/policies/hooks-client.tsx +12 -4
- package/.next/standalone/app/project/[name]/page.tsx +23 -79
- package/.next/standalone/app/projects/page.tsx +14 -23
- package/.next/standalone/assets/audit/poster-styles.css +1 -1
- package/.next/standalone/assets/audit/styles.css +11 -11
- package/.next/standalone/components/navbar.tsx +2 -37
- package/.next/standalone/lib/auth/api-server-client.ts +25 -0
- package/.next/standalone/node_modules/@next/env/package.json +2 -2
- package/.next/standalone/node_modules/next/dist/build/swc/index.js +1 -1
- package/.next/standalone/node_modules/next/dist/compiled/next-server/pages-turbo.runtime.prod.js +1 -1
- package/.next/standalone/node_modules/next/dist/lib/patch-incorrect-lockfile.js +3 -3
- package/.next/standalone/node_modules/next/dist/server/config.js +1 -1
- package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-turbopack.js +2 -2
- package/.next/standalone/node_modules/next/dist/server/dev/hot-reloader-webpack.js +1 -1
- package/.next/standalone/node_modules/next/dist/server/lib/app-info-log.js +1 -1
- package/.next/standalone/node_modules/next/dist/server/lib/start-server.js +1 -1
- package/.next/standalone/node_modules/next/dist/shared/lib/errors/canary-only-config-error.js +1 -1
- package/.next/standalone/node_modules/next/dist/telemetry/anonymous-meta.js +1 -1
- package/.next/standalone/node_modules/next/dist/telemetry/events/swc-load-failure.js +1 -1
- package/.next/standalone/node_modules/next/dist/telemetry/events/version.js +2 -2
- package/.next/standalone/node_modules/next/package.json +15 -15
- package/.next/standalone/package.json +18 -15
- package/.next/standalone/server.js +1 -1
- package/dist/cli.mjs +2 -2
- package/lib/auth/api-server-client.ts +25 -0
- package/package.json +18 -15
- package/src/audit/dashboard-cache.ts +13 -6
- package/src/audit/social-proof.ts +34 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__07tgnzi._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0oeun7z._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__12pit4m._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__13ra2jq._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1b9z5-i._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1ixjiy8._.js +0 -3
- package/.next/standalone/.next/server/chunks/_1-1804f._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__00jkjmt._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__013du6r._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0e85wxv._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0gfxvb1._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1hlrq6y._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ihxdo5._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1vvfde2._.js +0 -4
- package/.next/standalone/.next/server/chunks/ssr/node_modules_html2canvas_dist_html2canvas_esm_0q4m0_h.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/node_modules_html2canvas_dist_html2canvas_esm_1n-0xws.js +0 -3
- package/.next/standalone/.next/static/chunks/09ueq8s1as8xs.css +0 -2
- package/.next/standalone/.next/static/chunks/0qassxjx1ef04.js +0 -1
- package/.next/standalone/.next/static/chunks/2452blhu4bcdr.js +0 -41
- package/.next/standalone/.next/static/chunks/2so39wg7mjbi7.js +0 -1
- package/.next/standalone/.next/static/chunks/3gti1qdk5epqn.js +0 -1
- package/.next/standalone/.next/static/chunks/3osmt-t2hsrng.js +0 -1
- package/.next/standalone/.next/static/chunks/3wycox197ouus.css +0 -1
- package/.next/standalone/app/audit/_components/findings-section.tsx +0 -135
- package/.next/standalone/app/audit/_components/identity-section.tsx +0 -126
- package/.next/standalone/app/audit/_components/policies-section.tsx +0 -194
- package/.next/standalone/app/audit/_components/return-section.tsx +0 -416
- package/.next/standalone/app/audit/_components/score-section.tsx +0 -179
- package/.next/standalone/app/audit/_components/share-dock.tsx +0 -265
- package/.next/standalone/app/audit/_components/show-off-cta.tsx +0 -135
- /package/.next/standalone/.next/static/{90NFQus9VOnEzD6sP0EyP → NYPiJP6Rv_exQdSFVS8HP}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{90NFQus9VOnEzD6sP0EyP → NYPiJP6Rv_exQdSFVS8HP}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{90NFQus9VOnEzD6sP0EyP → NYPiJP6Rv_exQdSFVS8HP}/_ssgManifest.js +0 -0
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Auth dialog — modal overlay shown when an unauthenticated user clicks
|
|
5
|
-
*
|
|
5
|
+
* a cadence button to set a reminder. Two-step flow:
|
|
6
6
|
*
|
|
7
7
|
* 1. Email entry → POST /api/auth/login-request
|
|
8
8
|
* 2. OTP entry → POST /api/auth/login-verify
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* the dashboard's API route writes it to
|
|
10
|
+
* Calm chrome to match the rest of /audit: 1px borders, plain
|
|
11
|
+
* lowercase copy, no corner crosshair frame. The dialog never sees
|
|
12
|
+
* the refresh token — the dashboard's API route writes it to
|
|
13
|
+
* ~/.failproofai/auth.json.
|
|
13
14
|
*/
|
|
14
15
|
|
|
15
16
|
import React, { useCallback, useEffect, useRef, useState } from "react";
|
|
@@ -23,15 +24,12 @@ export interface AuthedUser {
|
|
|
23
24
|
|
|
24
25
|
interface Props {
|
|
25
26
|
open: boolean;
|
|
26
|
-
/**
|
|
27
|
+
/** Optional title. Defaults to "set a reminder". */
|
|
27
28
|
headline?: string;
|
|
28
|
-
/** Copy under the title explaining why we need auth right now. */
|
|
29
|
-
reason?: string;
|
|
30
29
|
onClose: () => void;
|
|
31
30
|
/** Fired after successful verify. Caller decides what to do next. */
|
|
32
31
|
onAuthed: (user: AuthedUser) => void;
|
|
33
|
-
/** Telemetry tag identifying which CTA opened the dialog.
|
|
34
|
-
* "unknown" so existing call sites continue to compile. */
|
|
32
|
+
/** Telemetry tag identifying which CTA opened the dialog. */
|
|
35
33
|
source?: string;
|
|
36
34
|
}
|
|
37
35
|
|
|
@@ -52,8 +50,7 @@ function describeFetchError(err: unknown): string {
|
|
|
52
50
|
|
|
53
51
|
export function AuthDialog({
|
|
54
52
|
open,
|
|
55
|
-
headline = "
|
|
56
|
-
reason = "verify yourself to continue.",
|
|
53
|
+
headline = "where to route the reminder?",
|
|
57
54
|
onClose,
|
|
58
55
|
onAuthed,
|
|
59
56
|
source = "unknown",
|
|
@@ -65,8 +62,6 @@ export function AuthDialog({
|
|
|
65
62
|
const emailInputRef = useRef<HTMLInputElement | null>(null);
|
|
66
63
|
const codeInputRef = useRef<HTMLInputElement | null>(null);
|
|
67
64
|
|
|
68
|
-
// Reset internal state every time the dialog opens. Also fire the
|
|
69
|
-
// funnel-opened event so we can measure dismissal vs verification rates.
|
|
70
65
|
useEffect(() => {
|
|
71
66
|
if (open) {
|
|
72
67
|
setStep({ kind: "email", error: null });
|
|
@@ -76,9 +71,6 @@ export function AuthDialog({
|
|
|
76
71
|
}
|
|
77
72
|
}, [capture, open, source]);
|
|
78
73
|
|
|
79
|
-
// Fire dismissed when the dialog closes WITHOUT a successful verify.
|
|
80
|
-
// We piggyback on `open` flipping false instead of intercepting every
|
|
81
|
-
// close path so resend / step transitions don't double-count.
|
|
82
74
|
const wasOpenRef = useRef(false);
|
|
83
75
|
useEffect(() => {
|
|
84
76
|
if (open) {
|
|
@@ -94,7 +86,6 @@ export function AuthDialog({
|
|
|
94
86
|
wasOpenRef.current = false;
|
|
95
87
|
}, [capture, open, source, step.kind]);
|
|
96
88
|
|
|
97
|
-
// Autofocus the right input as the step changes.
|
|
98
89
|
useEffect(() => {
|
|
99
90
|
if (!open) return;
|
|
100
91
|
const t = setTimeout(() => {
|
|
@@ -104,7 +95,6 @@ export function AuthDialog({
|
|
|
104
95
|
return () => clearTimeout(t);
|
|
105
96
|
}, [open, step.kind]);
|
|
106
97
|
|
|
107
|
-
// ESC to close.
|
|
108
98
|
useEffect(() => {
|
|
109
99
|
if (!open) return;
|
|
110
100
|
const onKey = (e: KeyboardEvent): void => {
|
|
@@ -114,7 +104,6 @@ export function AuthDialog({
|
|
|
114
104
|
return () => window.removeEventListener("keydown", onKey);
|
|
115
105
|
}, [open, busy, onClose]);
|
|
116
106
|
|
|
117
|
-
// Resend countdown ticker.
|
|
118
107
|
const resendActive = step.kind === "code" && step.resendIn > 0;
|
|
119
108
|
useEffect(() => {
|
|
120
109
|
if (!resendActive) return;
|
|
@@ -129,9 +118,6 @@ export function AuthDialog({
|
|
|
129
118
|
const requestCode = useCallback(
|
|
130
119
|
async (email: string, opts: { isResend?: boolean } = {}): Promise<void> => {
|
|
131
120
|
const { isResend = false } = opts;
|
|
132
|
-
// Show resend errors inline on the OTP step — the previously sent
|
|
133
|
-
// code is still usable. Only the first-send error path bounces back
|
|
134
|
-
// to the email step.
|
|
135
121
|
const setError = (msg: string) => {
|
|
136
122
|
if (isResend) {
|
|
137
123
|
setStep((s) => (s.kind === "code" ? { ...s, error: msg } : s));
|
|
@@ -268,11 +254,6 @@ export function AuthDialog({
|
|
|
268
254
|
}}
|
|
269
255
|
>
|
|
270
256
|
<div className="auth-dialog">
|
|
271
|
-
<span className="corner tl">┌</span>
|
|
272
|
-
<span className="corner tr">┐</span>
|
|
273
|
-
<span className="corner bl">└</span>
|
|
274
|
-
<span className="corner br">┘</span>
|
|
275
|
-
|
|
276
257
|
<button
|
|
277
258
|
type="button"
|
|
278
259
|
className="auth-close"
|
|
@@ -280,21 +261,19 @@ export function AuthDialog({
|
|
|
280
261
|
disabled={busy}
|
|
281
262
|
aria-label="close"
|
|
282
263
|
>
|
|
283
|
-
|
|
264
|
+
×
|
|
284
265
|
</button>
|
|
285
266
|
|
|
286
|
-
<div className="auth-label">━━ identity check</div>
|
|
287
267
|
<h2 id="auth-dialog-title" className="auth-headline">
|
|
288
268
|
{headline}
|
|
289
269
|
</h2>
|
|
290
270
|
|
|
291
271
|
{step.kind === "email" && (
|
|
292
272
|
<>
|
|
293
|
-
<p className="auth-sub">
|
|
273
|
+
<p className="auth-sub">
|
|
274
|
+
we'll send a one-time code to confirm.
|
|
275
|
+
</p>
|
|
294
276
|
<form onSubmit={onEmailSubmit} className="auth-form">
|
|
295
|
-
<label className="auth-field-label" htmlFor="auth-dialog-email">
|
|
296
|
-
email
|
|
297
|
-
</label>
|
|
298
277
|
<input
|
|
299
278
|
ref={emailInputRef}
|
|
300
279
|
id="auth-dialog-email"
|
|
@@ -311,7 +290,7 @@ export function AuthDialog({
|
|
|
311
290
|
{step.error && <div className="auth-error">{step.error}</div>}
|
|
312
291
|
<div className="auth-actions">
|
|
313
292
|
<button type="submit" className="auth-btn primary" disabled={busy}>
|
|
314
|
-
{busy ? "
|
|
293
|
+
{busy ? "sending…" : "send code"}
|
|
315
294
|
</button>
|
|
316
295
|
<button
|
|
317
296
|
type="button"
|
|
@@ -319,7 +298,7 @@ export function AuthDialog({
|
|
|
319
298
|
onClick={onClose}
|
|
320
299
|
disabled={busy}
|
|
321
300
|
>
|
|
322
|
-
|
|
301
|
+
cancel
|
|
323
302
|
</button>
|
|
324
303
|
</div>
|
|
325
304
|
</form>
|
|
@@ -329,14 +308,10 @@ export function AuthDialog({
|
|
|
329
308
|
{step.kind === "code" && (
|
|
330
309
|
<>
|
|
331
310
|
<p className="auth-sub">
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
check your inbox — it expires in {Math.ceil(step.expiresIn / 60)} min.
|
|
311
|
+
code sent to <span className="auth-email">{step.email}</span>.
|
|
312
|
+
expires in {Math.ceil(step.expiresIn / 60)} min.
|
|
335
313
|
</p>
|
|
336
314
|
<form onSubmit={onCodeSubmit} className="auth-form">
|
|
337
|
-
<label className="auth-field-label" htmlFor="auth-dialog-code">
|
|
338
|
-
one-time code
|
|
339
|
-
</label>
|
|
340
315
|
<input
|
|
341
316
|
ref={codeInputRef}
|
|
342
317
|
id="auth-dialog-code"
|
|
@@ -354,7 +329,7 @@ export function AuthDialog({
|
|
|
354
329
|
{step.error && <div className="auth-error">{step.error}</div>}
|
|
355
330
|
<div className="auth-actions">
|
|
356
331
|
<button type="submit" className="auth-btn primary" disabled={busy}>
|
|
357
|
-
{busy ? "
|
|
332
|
+
{busy ? "verifying…" : "verify"}
|
|
358
333
|
</button>
|
|
359
334
|
<button
|
|
360
335
|
type="button"
|
|
@@ -363,8 +338,8 @@ export function AuthDialog({
|
|
|
363
338
|
disabled={busy || step.resendIn > 0}
|
|
364
339
|
>
|
|
365
340
|
{step.resendIn > 0
|
|
366
|
-
? `
|
|
367
|
-
: "
|
|
341
|
+
? `resend in ${step.resendIn}s`
|
|
342
|
+
: "resend code"}
|
|
368
343
|
</button>
|
|
369
344
|
</div>
|
|
370
345
|
<button
|
|
@@ -382,15 +357,12 @@ export function AuthDialog({
|
|
|
382
357
|
{step.kind === "done" && (
|
|
383
358
|
<>
|
|
384
359
|
<p className="auth-sub">
|
|
385
|
-
<span className="auth-ok">✓</span>
|
|
360
|
+
<span className="auth-ok">✓</span> signed in as{" "}
|
|
386
361
|
<span className="auth-email">{step.user.email}</span>.
|
|
387
362
|
</p>
|
|
388
|
-
<p className="auth-sub" style={{ marginTop: 8 }}>
|
|
389
|
-
session saved locally.
|
|
390
|
-
</p>
|
|
391
363
|
<div className="auth-actions">
|
|
392
364
|
<button type="button" className="auth-btn primary" onClick={onClose}>
|
|
393
|
-
|
|
365
|
+
continue
|
|
394
366
|
</button>
|
|
395
367
|
</div>
|
|
396
368
|
</>
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Section 05 — COME BACK BETTER. "build the habit."
|
|
5
|
+
*
|
|
6
|
+
* Two side-by-side cards:
|
|
7
|
+
*
|
|
8
|
+
* • Reminder — set a reminder cadence (3d / 7d / 14d / 30d). The cadence
|
|
9
|
+
* selection persists through /api/auth/reminder. Anon users get the
|
|
10
|
+
* AuthDialog first; authed-with-existing-reminder users see the next
|
|
11
|
+
* audit date and can reset.
|
|
12
|
+
*
|
|
13
|
+
* • Unlock perks — share with N friends to unlock pro features for a
|
|
14
|
+
* month. UI only — invite tracking + entitlement is a follow-up; the
|
|
15
|
+
* button opens the same X share intent the poster uses.
|
|
16
|
+
*
|
|
17
|
+
* Re-audit moves out of this section: a small inline "or re-audit now"
|
|
18
|
+
* link sits under the reminder card so the affordance survives without
|
|
19
|
+
* dominating the layout.
|
|
20
|
+
*/
|
|
21
|
+
import React, { useCallback, useEffect, useRef, useState } from "react";
|
|
22
|
+
import { usePostHog } from "@/contexts/PostHogContext";
|
|
23
|
+
import { isAbortError } from "@/lib/fetch-with-timeout";
|
|
24
|
+
import { AuthDialog, type AuthedUser } from "./auth-dialog";
|
|
25
|
+
import { InviteDialog } from "./invite-dialog";
|
|
26
|
+
|
|
27
|
+
interface Props {
|
|
28
|
+
isRunning: boolean;
|
|
29
|
+
onRerun: () => void;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const DEFAULT_REMINDER_DAYS = 7;
|
|
33
|
+
const REMINDER_OPTIONS = [3, 7, 14, 30] as const;
|
|
34
|
+
type Cadence = typeof REMINDER_OPTIONS[number];
|
|
35
|
+
|
|
36
|
+
const PERKS_PERK = "share with 3 friends → unlock pro features for a month.";
|
|
37
|
+
|
|
38
|
+
type AuthStatus =
|
|
39
|
+
| { kind: "unknown" }
|
|
40
|
+
| { kind: "anon" }
|
|
41
|
+
| { kind: "authed"; user: { id: string; email: string } };
|
|
42
|
+
|
|
43
|
+
interface Reminder {
|
|
44
|
+
next_audit_at: number;
|
|
45
|
+
user_email: string;
|
|
46
|
+
set_at: number;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function daysUntil(unixSecs: number): number {
|
|
50
|
+
const nowSecs = Math.floor(Date.now() / 1000);
|
|
51
|
+
return Math.max(0, Math.ceil((unixSecs - nowSecs) / 86400));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function formatNextAudit(unixSecs: number): string {
|
|
55
|
+
const d = new Date(unixSecs * 1000);
|
|
56
|
+
return d.toLocaleDateString(undefined, {
|
|
57
|
+
weekday: "short",
|
|
58
|
+
month: "short",
|
|
59
|
+
day: "numeric",
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function ComeBackBetterSection({ isRunning, onRerun }: Props) {
|
|
64
|
+
const { capture } = usePostHog();
|
|
65
|
+
const [authStatus, setAuthStatus] = useState<AuthStatus>({ kind: "unknown" });
|
|
66
|
+
const [reminder, setReminder] = useState<Reminder | null>(null);
|
|
67
|
+
const [cadence, setCadence] = useState<Cadence>(DEFAULT_REMINDER_DAYS);
|
|
68
|
+
const [dialogOpen, setDialogOpen] = useState(false);
|
|
69
|
+
const [inviteDialogOpen, setInviteDialogOpen] = useState(false);
|
|
70
|
+
const [reminderBusy, setReminderBusy] = useState(false);
|
|
71
|
+
const ctaShownRef = useRef(false);
|
|
72
|
+
const lastRefreshAtRef = useRef(0);
|
|
73
|
+
|
|
74
|
+
const refreshStatus = useCallback(async () => {
|
|
75
|
+
lastRefreshAtRef.current = Date.now();
|
|
76
|
+
// Preserve current UI state on transient failures (5xx, network blips).
|
|
77
|
+
// Downgrading to anon on every error would clear a valid reminder mid-
|
|
78
|
+
// session on a single failed poll, forcing an unnecessary auth prompt.
|
|
79
|
+
// Only fall through to anon on the very first probe (still "unknown")
|
|
80
|
+
// so the cadence buttons unlock even if the server is unreachable.
|
|
81
|
+
const fallbackToAnonOnError = () => {
|
|
82
|
+
setAuthStatus((prev) => (prev.kind === "unknown" ? { kind: "anon" } : prev));
|
|
83
|
+
};
|
|
84
|
+
try {
|
|
85
|
+
const res = await fetch("/api/auth/status", { cache: "no-store" });
|
|
86
|
+
if (!res.ok) {
|
|
87
|
+
fallbackToAnonOnError();
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const body = (await res.json()) as {
|
|
91
|
+
authenticated?: boolean;
|
|
92
|
+
user?: { id: string; email: string };
|
|
93
|
+
reminder?: Reminder | null;
|
|
94
|
+
};
|
|
95
|
+
if (body.authenticated && body.user) {
|
|
96
|
+
setAuthStatus({ kind: "authed", user: body.user });
|
|
97
|
+
setReminder(body.reminder ?? null);
|
|
98
|
+
} else {
|
|
99
|
+
setAuthStatus({ kind: "anon" });
|
|
100
|
+
setReminder(null);
|
|
101
|
+
}
|
|
102
|
+
} catch {
|
|
103
|
+
fallbackToAnonOnError();
|
|
104
|
+
}
|
|
105
|
+
}, []);
|
|
106
|
+
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
void refreshStatus();
|
|
109
|
+
const REFRESH_MIN_INTERVAL_MS = 5_000;
|
|
110
|
+
const maybeRefresh = () => {
|
|
111
|
+
if (Date.now() - lastRefreshAtRef.current < REFRESH_MIN_INTERVAL_MS) return;
|
|
112
|
+
void refreshStatus();
|
|
113
|
+
};
|
|
114
|
+
const onFocus = () => maybeRefresh();
|
|
115
|
+
const onVisibility = () => {
|
|
116
|
+
if (document.visibilityState === "visible") maybeRefresh();
|
|
117
|
+
};
|
|
118
|
+
window.addEventListener("focus", onFocus);
|
|
119
|
+
document.addEventListener("visibilitychange", onVisibility);
|
|
120
|
+
return () => {
|
|
121
|
+
window.removeEventListener("focus", onFocus);
|
|
122
|
+
document.removeEventListener("visibilitychange", onVisibility);
|
|
123
|
+
};
|
|
124
|
+
}, [refreshStatus]);
|
|
125
|
+
|
|
126
|
+
useEffect(() => {
|
|
127
|
+
if (ctaShownRef.current) return;
|
|
128
|
+
if (authStatus.kind === "unknown") return;
|
|
129
|
+
ctaShownRef.current = true;
|
|
130
|
+
capture("audit_reminder_cta_shown", {
|
|
131
|
+
auth_state: authStatus.kind,
|
|
132
|
+
has_existing_reminder: reminder !== null,
|
|
133
|
+
source: "come_back_better_section",
|
|
134
|
+
});
|
|
135
|
+
}, [authStatus, capture, reminder]);
|
|
136
|
+
|
|
137
|
+
const persistReminder = useCallback(
|
|
138
|
+
async (inDays: number): Promise<Reminder | null> => {
|
|
139
|
+
const controller = new AbortController();
|
|
140
|
+
const timer = setTimeout(() => controller.abort(), 10_000);
|
|
141
|
+
try {
|
|
142
|
+
setReminderBusy(true);
|
|
143
|
+
const res = await fetch("/api/auth/reminder", {
|
|
144
|
+
method: "POST",
|
|
145
|
+
headers: { "content-type": "application/json" },
|
|
146
|
+
body: JSON.stringify({ in_days: inDays }),
|
|
147
|
+
signal: controller.signal,
|
|
148
|
+
});
|
|
149
|
+
if (!res.ok) {
|
|
150
|
+
if (res.status === 401) {
|
|
151
|
+
setAuthStatus({ kind: "anon" });
|
|
152
|
+
setReminder(null);
|
|
153
|
+
}
|
|
154
|
+
capture("audit_reminder_saved", {
|
|
155
|
+
status: `http_${res.status}`,
|
|
156
|
+
source: "come_back_better_section",
|
|
157
|
+
cadence_days: inDays,
|
|
158
|
+
});
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
const body = (await res.json()) as { reminder?: Reminder };
|
|
162
|
+
capture("audit_reminder_saved", {
|
|
163
|
+
status: body.reminder ? "success" : "empty",
|
|
164
|
+
source: "come_back_better_section",
|
|
165
|
+
cadence_days: inDays,
|
|
166
|
+
});
|
|
167
|
+
return body.reminder ?? null;
|
|
168
|
+
} catch (err) {
|
|
169
|
+
const kind = isAbortError(err) ? "timeout" : "error";
|
|
170
|
+
capture("audit_reminder_saved", {
|
|
171
|
+
status: kind,
|
|
172
|
+
source: "come_back_better_section",
|
|
173
|
+
cadence_days: inDays,
|
|
174
|
+
});
|
|
175
|
+
return null;
|
|
176
|
+
} finally {
|
|
177
|
+
clearTimeout(timer);
|
|
178
|
+
setReminderBusy(false);
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
[capture],
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
const handleCadenceClick = useCallback(
|
|
185
|
+
async (next: Cadence) => {
|
|
186
|
+
setCadence(next);
|
|
187
|
+
capture("audit_reminder_cta_clicked", {
|
|
188
|
+
auth_state: authStatus.kind,
|
|
189
|
+
has_existing_reminder: reminder !== null,
|
|
190
|
+
cadence_days: next,
|
|
191
|
+
source: "come_back_better_section",
|
|
192
|
+
});
|
|
193
|
+
if (authStatus.kind === "authed") {
|
|
194
|
+
const saved = await persistReminder(next);
|
|
195
|
+
if (saved) setReminder(saved);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
if (authStatus.kind === "anon") {
|
|
199
|
+
setDialogOpen(true);
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
[authStatus, capture, persistReminder, reminder],
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
const handleAuthed = useCallback(
|
|
206
|
+
async (user: AuthedUser) => {
|
|
207
|
+
setAuthStatus({ kind: "authed", user });
|
|
208
|
+
capture("audit_auth_completed", {
|
|
209
|
+
source: "come_back_better_section",
|
|
210
|
+
});
|
|
211
|
+
const saved = await persistReminder(cadence);
|
|
212
|
+
if (saved) setReminder(saved);
|
|
213
|
+
},
|
|
214
|
+
[cadence, capture, persistReminder],
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
const handleInvite = useCallback(() => {
|
|
218
|
+
capture("audit_perks_invite_clicked", {
|
|
219
|
+
source: "come_back_better_section",
|
|
220
|
+
auth_state: authStatus.kind,
|
|
221
|
+
});
|
|
222
|
+
// Unauthed users go through the AuthDialog first so we have a sender
|
|
223
|
+
// identity to Cc on the invite email.
|
|
224
|
+
if (authStatus.kind !== "authed") {
|
|
225
|
+
setDialogOpen(true);
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
setInviteDialogOpen(true);
|
|
229
|
+
}, [authStatus.kind, capture]);
|
|
230
|
+
|
|
231
|
+
const handleRerunInline = useCallback(() => {
|
|
232
|
+
if (isRunning) return;
|
|
233
|
+
onRerun();
|
|
234
|
+
}, [isRunning, onRerun]);
|
|
235
|
+
|
|
236
|
+
const days = reminder ? daysUntil(reminder.next_audit_at) : 0;
|
|
237
|
+
|
|
238
|
+
return (
|
|
239
|
+
<section className="audit-sec" data-screen-label="05 Come back better">
|
|
240
|
+
<div className="audit-sec-head">
|
|
241
|
+
<span className="audit-sec-eyebrow">
|
|
242
|
+
<span className="ix">05</span>{"// come back better"}
|
|
243
|
+
</span>
|
|
244
|
+
</div>
|
|
245
|
+
<h2 className="audit-sec-title">build the habit</h2>
|
|
246
|
+
|
|
247
|
+
<div className="cbb-grid">
|
|
248
|
+
{/* Reminder card */}
|
|
249
|
+
<div className="cbb-card">
|
|
250
|
+
<div className="cbb-card-title">set a reminder</div>
|
|
251
|
+
<div className="cbb-card-sub">
|
|
252
|
+
{reminder
|
|
253
|
+
? `next audit set for ${formatNextAudit(reminder.next_audit_at)} · in ${days} day${days === 1 ? "" : "s"}.`
|
|
254
|
+
: "we'll nudge you when your next audit is due. pick the cadence:"}
|
|
255
|
+
</div>
|
|
256
|
+
<div className="cadence-row">
|
|
257
|
+
{REMINDER_OPTIONS.map((d) => (
|
|
258
|
+
<button
|
|
259
|
+
key={d}
|
|
260
|
+
type="button"
|
|
261
|
+
className={`cadence-btn${cadence === d ? " on" : ""}`}
|
|
262
|
+
disabled={reminderBusy || authStatus.kind === "unknown"}
|
|
263
|
+
onClick={() => void handleCadenceClick(d)}
|
|
264
|
+
>
|
|
265
|
+
{d}d
|
|
266
|
+
</button>
|
|
267
|
+
))}
|
|
268
|
+
</div>
|
|
269
|
+
<button
|
|
270
|
+
type="button"
|
|
271
|
+
className="cbb-link"
|
|
272
|
+
disabled={isRunning}
|
|
273
|
+
onClick={handleRerunInline}
|
|
274
|
+
>
|
|
275
|
+
{isRunning ? "scanning…" : "or re-audit now →"}
|
|
276
|
+
</button>
|
|
277
|
+
</div>
|
|
278
|
+
|
|
279
|
+
{/* Perks card */}
|
|
280
|
+
<div className="cbb-card">
|
|
281
|
+
<div className="cbb-card-title">unlock failproof perks</div>
|
|
282
|
+
<div className="cbb-card-sub">{PERKS_PERK}</div>
|
|
283
|
+
<button type="button" className="invite-btn" onClick={handleInvite}>
|
|
284
|
+
invite a friend
|
|
285
|
+
</button>
|
|
286
|
+
<div className="cbb-foot">
|
|
287
|
+
{"// invites are sent from failproof.ai, Cc'd to you, with a link to run their own audit."}
|
|
288
|
+
</div>
|
|
289
|
+
</div>
|
|
290
|
+
</div>
|
|
291
|
+
|
|
292
|
+
<InviteDialog
|
|
293
|
+
open={inviteDialogOpen}
|
|
294
|
+
source="come_back_better_section"
|
|
295
|
+
onClose={() => setInviteDialogOpen(false)}
|
|
296
|
+
onUnauthorized={() => {
|
|
297
|
+
// Session expired between probe and submit — flip back to anon
|
|
298
|
+
// and bounce through the AuthDialog so the user re-auths.
|
|
299
|
+
setAuthStatus({ kind: "anon" });
|
|
300
|
+
setReminder(null);
|
|
301
|
+
setDialogOpen(true);
|
|
302
|
+
}}
|
|
303
|
+
/>
|
|
304
|
+
|
|
305
|
+
<AuthDialog
|
|
306
|
+
open={dialogOpen}
|
|
307
|
+
source="return_section"
|
|
308
|
+
onClose={() => setDialogOpen(false)}
|
|
309
|
+
onAuthed={(u) => {
|
|
310
|
+
setDialogOpen(false);
|
|
311
|
+
void handleAuthed(u);
|
|
312
|
+
}}
|
|
313
|
+
/>
|
|
314
|
+
</section>
|
|
315
|
+
);
|
|
316
|
+
}
|
|
@@ -32,7 +32,13 @@ export function EmptyState({ mode, running, onStarted, onCompleted }: Props) {
|
|
|
32
32
|
});
|
|
33
33
|
onStarted();
|
|
34
34
|
try {
|
|
35
|
-
|
|
35
|
+
// since:"all" — scan the user's entire session history, not a window.
|
|
36
|
+
await triggerRun({ cli: [], since: "all" });
|
|
37
|
+
} catch (err) {
|
|
38
|
+
// A failed first run falls back to the empty state via onCompleted's
|
|
39
|
+
// refetch (no cache was written). Swallow here so it doesn't surface as
|
|
40
|
+
// an unhandled promise rejection on the click handler.
|
|
41
|
+
console.error("audit first run failed:", err);
|
|
36
42
|
} finally {
|
|
37
43
|
await onCompleted();
|
|
38
44
|
}
|
|
@@ -82,7 +88,7 @@ export function EmptyState({ mode, running, onStarted, onCompleted }: Props) {
|
|
|
82
88
|
{running ? "[ scanning… ]" : "[ run audit ]"}
|
|
83
89
|
</button>
|
|
84
90
|
<span className="empty-meta">
|
|
85
|
-
scans
|
|
91
|
+
scans all sessions · all installed CLIs · may take a while
|
|
86
92
|
</span>
|
|
87
93
|
</div>
|
|
88
94
|
</div>
|