Skip to content

Feature - Onboarding route to allow users to easily create their first API Key and async or batch workload#1033

Open
aschkanAH wants to merge 16 commits into
doublewordai:mainfrom
aschkanAH:feat-onboarding
Open

Feature - Onboarding route to allow users to easily create their first API Key and async or batch workload#1033
aschkanAH wants to merge 16 commits into
doublewordai:mainfrom
aschkanAH:feat-onboarding

Conversation

@aschkanAH
Copy link
Copy Markdown
Collaborator

Adds a /onboarding route shown immediately after native registration. The page renders inside the existing authenticated app shell (sidebar retained) but uses a slim header that only carries a "Skip to Dashboard" affordance.

Notes:
The invite component is visible only if a zapier webhook is set in .env variables. Zapier webhook call is fire-and-forget (opaque response under no-cors); we can't detect transport failures from the browser. Move to native APIs when possible.
The "Run Now" UX intentionally simulates a 2.5s success regardless of the underlying batch outcome. If the real upload fails we log a warning but don't surface the error in the UI — the user is about to be redirected anyway, and they'll see the actual job state on /models.
Background API key creation runs on every visit to /onboarding. Combined with the "won't be shown again" copy, that's intentional for the first-visit flow but means revisiting the page for any reason will mint a new throwaway key. If we want to avoid that, follow-up work should gate this behind the same onboarding_completed_${userId} localStorage flag the auth context already uses or in the auth0 metadata.

cursoragent and others added 5 commits April 28, 2026 02:17
Adds /onboarding route shown immediately after native registration. The page
renders inside the standard authenticated app shell (sidebar retained) but
uses a slim header that only carries a 'Skip to Dashboard' affordance.

Three interactive steps:

1. Live API key — calls the existing useCreateApiKey hook on mount so the
   user sees a real key (not a placeholder) and can copy it.
2. Workload runner — async vs batch toggle, browser vs CLI execution
   toggle, Python vs cURL language toggle. Browser mode pre-renders the
   payload and a Run Now button that uploads + creates a real batch via
   useCreateBatch / useUploadFileWithProgress while still cycling through
   idle → running → success on a 2.5s timer for predictable UX. CLI mode
   shows a 'Listening for request…' indicator that flips to success on
   click. Both flows redirect to /models 2s after success.
3. Team invite — POSTs the supplied email to the configured Zapier hook
   (no-cors, fire-and-forget) and surfaces a sent confirmation.

Also fires a background 'Hello World' batch + toast on mount so users see
data on the dashboard when they land.

The post-signup default redirect from RegisterForm is now /onboarding;
explicit ?redirect= and the server-driven onboarding_redirect_url still
take priority.

Co-authored-by: aschkanAH <aschkanAH@users.noreply.github.com>
mode: 'no-cors' silently downgrades Content-Type: application/json to
text/plain because JSON isn't CORS-safelisted. Zapier Catch Hooks only
auto-parse JSON when the request actually carries an application/json
content-type, so the previous code was firing the webhook successfully
but with all fields empty.

Switching the body to URLSearchParams produces an
application/x-www-form-urlencoded body (which IS CORS-safelisted) and
which Zapier does parse into structured fields.

Co-authored-by: aschkanAH <aschkanAH@users.noreply.github.com>
Replaces the hardcoded Zapier hook with VITE_INVITE_WEBHOOK_URL. The
invite section is hidden entirely when the variable is unset so we don't
collect emails with nowhere to send them; the submit handler also has
a defensive guard for stale builds.

Co-authored-by: aschkanAH <aschkanAH@users.noreply.github.com>
…gate race

Three fixes from review:

1. buildAsyncPayload, buildJsonlPayload, and the snippet generators were
   interpolating the model alias into JSON / Python / cURL string
   literals via raw template literals. Catalog aliases come from user-
   controlled DB metadata and may legitimately contain quotes,
   backslashes, or control characters, which would yield invalid JSON.
   handleRunNow's JSON.parse(buildAsyncPayload(...)) would throw on such
   aliases, yet the simulated 2.5s timer would still flip the UI to
   'success' and redirect — falsely telling the user a workload was
   queued.

   Payloads are now built as objects and serialized via JSON.stringify
   (JSONL/JSON cases), and code-snippet interpolations go through
   escapeForLiteral which uses JSON.stringify's inner string form to
   correctly escape both Python and shell-string literal special chars.

2. The CLI listener block exposed '(Click to simulate success)' to
   desktop users. 'Simulate' is internal language that breaks the
   illusion of the onboarding flow. Replaced with '(Click to continue)'
   and updated the aria-label to match.

3. RegisterForm called navigate('/onboarding') unconditionally after
   register(). When the server returns onboarding_redirect_url,
   AuthProvider.checkAuthStatus has already initiated a hard navigation
   via window.location.href — the new client-side push raced that and
   could briefly mount the wrong route before the hard nav resolved.
   We now read the just-populated current-user cache entry and skip
   navigate() when an onboarding_redirect_url is present, deferring
   cleanly to the server-driven redirect (matching the pre-onboarding
   behaviour for that path).

Co-authored-by: aschkanAH <aschkanAH@users.noreply.github.com>
feat(dashboard): zero-friction onboarding page
@sejori
Copy link
Copy Markdown
Contributor

sejori commented Apr 29, 2026

/build-staging

@github-actions
Copy link
Copy Markdown

Staging image feat-onboarding-rc built and pushed. A staging PR has been created in the internal repo — comment /deploy-staging there to deploy.

aschkanAH and others added 5 commits April 30, 2026 01:15
Per growth-team revision:
- 'You're ready to run at scale.' -> 'From zero to inference in seconds.'
- Subheading rewritten to lead with provisioning + browser-side run.

Co-authored-by: aschkanAH <aschkanAH@users.noreply.github.com>
The shadcn theme tokens for popover/border are stored as bare HSL
component triplets (e.g. `0 0% 100%`), not full color values. The
existing Toaster wired `--normal-bg: var(--popover)` directly, which
resolves to `background: 0 0% 100%` — invalid CSS, so sonner falls
back to its default semi-transparent surface and toasts render
illegibly when overlaying page content (most visible on the
onboarding 'Sample Batch Started' toast on top of the API key card).

Wrap the tokens in `hsl(...)` so the resolved value is a valid
opaque color.

Co-authored-by: aschkanAH <aschkanAH@users.noreply.github.com>
fix(onboarding): hero copy revision + opaque toast background
@aschkandw
Copy link
Copy Markdown

/build-staging

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 3, 2026

Staging image feat-onboarding-rc built and pushed. A staging PR has been created in the internal repo — comment /deploy-staging there to deploy.

cursoragent and others added 5 commits May 3, 2026 03:04
The background 'Hello World' batch and the Run-Now handler were both
reading from a single `modelAlias` value that fell back to a hard-coded
`medgemma-4b` while the catalog query was still loading. Because the
background effect fired immediately after auth resolved (well before
`useModels` returned), the visible payload would later re-render with
the catalog alias (e.g. 'deepseek') while the backend received
'medgemma-4b' — producing 'Model medgemma-4b has not been configured or
is not available to user' on every onboarding load in environments
without that model.

Split the alias into two values:

  - displayModelAlias: always populated (real alias or placeholder) and
    used only for rendering code samples so the UI is never blank.
  - runnableModelAlias: undefined while the catalog is loading and when
    the user has no accessible chat model. Used for outbound requests.

The background batch now waits for the catalog to resolve and bails
silently (no toast, no request) when there's no entitled chat model.
Run Now does the same in its IIFE so the simulated success timer still
fires while we skip the doomed network call.

Co-authored-by: aschkanAH <aschkanAH@users.noreply.github.com>
The previous split of displayModelAlias / runnableModelAlias still left
'medgemma-4b' visible in the rendered code samples for the brief window
between mount and the useModels query resolving. Anyone reading the
payload during that window would reasonably expect medgemma to be the
model the backend is asked to run — exactly the divergence we set out
to remove.

Replace the hard-coded fallback with an obviously-non-runnable
'<your-model-alias>' placeholder, and disable the Run Now button until
runnableModelAlias is populated. The visible payload and the outbound
payload now share a single source of truth: when there's a real alias,
both reference it; when there isn't, the placeholder is shown and the
button is disabled.

Co-authored-by: aschkanAH <aschkanAH@users.noreply.github.com>
Previously the post-success auto-redirect always landed users on
/models — fine as a generic 'where would you like to go next' but it
hides the workload they just queued. Send them instead to the route
that will surface their job:

  - /async when workloadType is 'async'
  - /batches when workloadType is 'batch'

Both routes are config-gated by batches.enabled (and async further
gated by batches.async_requests.enabled), so we consult
useAuthorization().canAccessRoute and fall back to /models when the
preferred destination isn't accessible on this deployment, to avoid
ProtectedRoute bouncing the user mid-redirect.

The 'Skip to Dashboard' header button still lands on /models per the
original spec. The CLI listener path also stays on /models — that path
is the 'click to continue' simulation, not an actual workload, so
there's no specific job for the user to view.

Inline success-strip copy now reflects the destination ('Taking you to
your batch/async request…') so the redirect isn't surprising.

Co-authored-by: aschkanAH <aschkanAH@users.noreply.github.com>
Including browserSuccessRoute in the auto-redirect effect's deps caused
a lost-redirect bug: if the value's identity changed during the 2s
window (workloadType toggle, useAuthorization refetch race, config
invalidation, …) the cleanup function ran clearTimeout, the effect
re-ran, and the idempotency ref short-circuited it before a new timer
could be scheduled. The user would see 'Taking you to your batch…'
indefinitely without ever navigating.

Move the destination into a ref that's updated on every render so the
trigger effect captures the latest value at success-time without
declaring a dep. Deps are now only the success triggers + navigate,
none of which churn between idle and success, so the cleanup-cancel
path is unreachable by construction.

Co-authored-by: aschkanAH <aschkanAH@users.noreply.github.com>
fix(onboarding): harmonise outbound model alias with rendered payload
@aschkanAH
Copy link
Copy Markdown
Collaborator Author

/build-staging

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 3, 2026

Staging image feat-onboarding-rc built and pushed. A staging PR has been created in the internal repo — comment /deploy-staging there to deploy.

@aschkandw
Copy link
Copy Markdown

/build-staging

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 6, 2026

Staging image feat-onboarding-rc built and pushed. A staging PR has been created in the internal repo — comment /deploy-staging there to deploy.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants