From aebd1716db4e0c57fc00f42cca9f2c53a5850948 Mon Sep 17 00:00:00 2001 From: Pablo Abella Date: Tue, 13 Oct 2026 13:16:59 +0200 Subject: [PATCH] Make the Connect step 2 its own view and polish the console After the Vercel Connect round trip, /sign-in now shows a focused "Choose your database" view: no method tabs, no preview or how-it-works sections, a connected row with Disconnect, and searchable selects ported from jev-pr-quality. ?method=key is ignored while connected. Console: gray card icons nudged onto the title's optical center, the collector and database on their own line in the consent row, a visible hover on outline buttons, and room for the cards' ring in the scrolling column. Co-Authored-By: Claude Opus 5.5 (1M context) --- app/sign-in/page.tsx | 206 +++++++++++++----------- components/console/console-app.tsx | 20 +-- components/date/button.tsx | 2 +- components/sign-in/search-select.tsx | 74 +++++++++ components/sign-in/workspace-picker.tsx | 51 +++--- 5 files changed, 217 insertions(+), 136 deletions(-) create mode 100644 components/sign-in/search-select.tsx diff --git a/app/sign-in/page.tsx b/app/sign-in/page.tsx index 194da19..064136b 100644 --- a/app/sign-in/page.tsx +++ b/app/sign-in/page.tsx @@ -1,9 +1,10 @@ // Sign-in mode only (no RAWTREE_QUERY_KEY / RAWTREE_INGEST_KEY). Layout follows // jev-pr-quality's sign-in page: connect with a RawTree account through Vercel Connect (when // RAWTREE_CONNECTOR is set) or with an API key, then a preview and how it works. `?method=key` -// picks the API key tab without client JavaScript. Env mode redirects to the dashboard. +// picks the API key tab without client JavaScript. Once connected, the page becomes step 2 of the +// Connect flow: only the workspace picker and Disconnect. Env mode redirects to the dashboard. -import { IconChartBar, IconChevronRight, IconCode, IconDatabase, IconExternalLink, IconSend, IconShieldCheck } from "@tabler/icons-react"; +import { IconChartBar, IconCheck, IconChevronRight, IconCode, IconDatabase, IconExternalLink, IconSend, IconShieldCheck } from "@tabler/icons-react"; import Image from "next/image"; import Link from "next/link"; import { cookies } from "next/headers"; @@ -108,8 +109,9 @@ export default async function SignInPage({ searchParams }: { searchParams: Promi const connector = connectorInSignInMode(); const session = (await cookies()).get(SESSION_COOKIE)?.value; const picker = await loadPicker(connector, session); - const useKey = !connector || method === "key"; const connected = Boolean(picker.workspaces); + // Once connected, the API key form is out of reach until the visitor disconnects. + const useKey = !connected && (!connector || method === "key"); return (
@@ -134,17 +136,32 @@ export default async function SignInPage({ searchParams }: { searchParams: Promi
-

Web analytics on RawTree

+

{connected ? "Choose your database" : "Web analytics on RawTree"}

- Send your own events and session recordings with one SDK, keep every row in your RawTree database, and see traffic, acquisition, - engagement, and bots without counting retries twice. + {connected + ? "Pick the RawTree organization, cluster, and database where your events are stored." + : "Send your own events and session recordings with one SDK, keep every row in your RawTree database, and see traffic, acquisition, engagement, and bots without counting retries twice."}

-

Connect to RawTree

- {connector ? ( + {connected ? ( +
+

+ + + + Connected to RawTree +

+ +
+ ) : ( +

Connect to RawTree

+ )} + {connector && !connected ? ( ) : null} -

- {useKey ? ( - <> - Enter an API key that can read and write your analytics database: the dashboard reads it, and the console sends test - events to it. - - ) : connected ? ( - <> - Connected to RawTree. Choose where your events are stored.{" "} - - - ) : ( - "Sign in with your RawTree account to see your traffic, acquisition, engagement, bots, and session replays." - )} -

+ {connected ? null : ( +

+ {useKey ? ( + <> + Enter an API key that can read and write your analytics database: the dashboard reads it, and the console sends + test events to it. + + ) : ( + "Sign in with your RawTree account to see your traffic, acquisition, engagement, bots, and session replays." + )} +

+ )} {expired ?

RawTree rejected your credentials. Sign in again.

: null} {picker.error && !useKey ?

{picker.error}

: null}
@@ -196,80 +208,86 @@ export default async function SignInPage({ searchParams }: { searchParams: Promi
)} - {/* Target of the Disconnect button, which sits inside a paragraph. */} + {/* Target of the Disconnect button, which sits outside the picker's form. */}
- + {connected ? null : ( + + )}
-
-
- What you'll see - Traffic, acquisition, engagement, bots, and session replays. Preview uses sample data. -
- Dashboard preview with headline numbers, the traffic trend, and the dashboard sections -
+ {connected ? null : ( + <> +
+
+ What you'll see + Traffic, acquisition, engagement, bots, and session replays. Preview uses sample data. +
+ Dashboard preview with headline numbers, the traffic trend, and the dashboard sections +
-
-
-

- How it works -

-

From your app to your dashboard.

-
-
    - {HOW_IT_WORKS.map(({ icon: Icon, title, body }, index) => ( -
  1. -
    - - - - 0{index + 1} -
    -
    -

    {title}

    -

    {body}

    -
    -
  2. - ))} -
-
-

- - {useKey - ? "Your site sends events with an insert-only key. Your dashboard key stays in an HTTP-only cookie." - : "Your site sends events with an insert-only key. Viewers sign in with their own RawTree account."} -

- - Technical details - -
-
+
+
+

+ How it works +

+

From your app to your dashboard.

+
+
    + {HOW_IT_WORKS.map(({ icon: Icon, title, body }, index) => ( +
  1. +
    + + + + 0{index + 1} +
    +
    +

    {title}

    +

    {body}

    +
    +
  2. + ))} +
+
+

+ + {useKey + ? "Your site sends events with an insert-only key. Your dashboard key stays in an HTTP-only cookie." + : "Your site sends events with an insert-only key. Viewers sign in with their own RawTree account."} +

+ + Technical details + +
+
+ + )}