Skip to content

fix(frontend): restore login flow loading states#1852

Merged
riderx merged 7 commits into
mainfrom
codex/fix-login-loader-flow
Mar 25, 2026
Merged

fix(frontend): restore login flow loading states#1852
riderx merged 7 commits into
mainfrom
codex/fix-login-loader-flow

Conversation

@riderx
Copy link
Copy Markdown
Member

@riderx riderx commented Mar 25, 2026

Summary (AI generated)

  • restore inline loading states in the login flow instead of using the full-page loader
  • keep the email step disabled with a spinner until saved-session storage checks complete
  • disable login and SSO submit buttons until Turnstile is valid when captcha is enabled
  • update Playwright login helpers for the current two-step login flow and keep captcha-blocked submit-path tests marked as skipped/fixme

Motivation (AI generated)

The new login flow was dropping visible pending states during session bootstrap and TOTP verification, which made the page appear idle while it was still authenticating or redirecting. That caused confusing flashes of the login form for already-authenticated users and allowed submit actions before Turnstile was ready.

Business Impact (AI generated)

This reduces friction and ambiguity during sign-in, especially for returning users and accounts using 2FA or captcha. A clearer login flow lowers the risk of duplicate submissions, perceived instability, and support noise around authentication regressions.

Test Plan (AI generated)

  • bunx eslint src/pages/login.vue playwright/support/commands.ts playwright/e2e/auth.spec.ts
  • bunx playwright test playwright/e2e/auth.spec.ts playwright/e2e/sso-login.spec.ts --project=chromium
  • Verified the local UI coverage for the two-step login flow passes
  • Confirmed captcha-gated submit-path tests remain explicitly skipped until a local Turnstile bypass exists

Generated with AI

Summary by CodeRabbit

  • Bug Fixes

    • Clearer loading states during email→password/domain-check flow
    • CAPTCHA token is reset on auth failures and SSO errors
    • Improved session-check and bootstrap resilience
    • Support for multiple post-login redirect destinations
  • User Experience

    • Email “Continue” and submit buttons reflect busy/disabled states; inline spinners added
    • Forgot-password now navigates after completing the email step
    • Turnstile (captcha) availability handled more robustly
  • Tests

    • Updated end-to-end auth tests and login helper for more reliable flows
  • Chores

    • Test server startup adjusted for local runs

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 25, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a0550e20-952e-4ed2-8ce5-0890add81b27

📥 Commits

Reviewing files that changed from the base of the PR and between 15097b0 and dd1f41e.

📒 Files selected for processing (1)
  • playwright.config.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • playwright.config.ts

📝 Walkthrough

Walkthrough

Adds a typed two‑step email→continue→password flow with saved‑session checking and Turnstile CAPTCHA lifecycle/state; centralizes domain-check loading state; updates E2E helpers/tests to use the intermediate step; adjusts post‑login redirect matching; and changes Playwright frontend startup to a local env command. (50 words)

Changes

Cohort / File(s) Summary
E2E Tests
playwright/e2e/auth.spec.ts
Added typed helper continueToPasswordStep(page, email); replaced prior loading-state test with a domain-check loading-state that stubs **/private/sso/check-domain to { has_sso: false }; updated tests to use the two-step flow and relaxed post-login URL match to /apps or /dashboard.
Test Support
playwright/support/commands.ts
Updated page.login() to perform the email-step continue click and wait for the password field before entering password; adjusted post-login wait to a regex matching /apps or /dashboard.
Login Component
src/pages/login.vue
Added isCheckingSavedSession, captchaStatus, isEmailStepBusy / shouldBlockForCaptcha; Turnstile init timeout, handlers, watchers, and cleanup; changed nextLogin() to await router.replace(...); clearer error paths that reset/clear turnstileToken and widget; reworked loading UI to use :disabled/:aria-busy and inline SVGs; refactored bootstrapping and checkLogin() to try/catch/finally.
Playwright Config
playwright.config.ts
Changed frontend webServer start command to bun run serve:local and injected local environment overrides (ENV=local, SUPA_URL, SUPA_ANON, API_DOMAIN, CAPTCHA_KEY); port, timeout, reuseExistingServer and stdout behavior unchanged.

Sequence Diagram

sequenceDiagram
    actor User
    participant LoginPage as Login Component
    participant DomainAPI as Domain Check API
    participant AuthAPI as Auth API
    participant Turnstile as CAPTCHA Widget
    participant Router as Router

    User->>LoginPage: Enter email
    LoginPage->>DomainAPI: Check domain (has_sso?)
    DomainAPI-->>LoginPage: Respond has_sso
    LoginPage->>LoginPage: Update isEmailStepBusy, enable/disable Continue
    User->>LoginPage: Click Continue
    LoginPage->>LoginPage: Reveal password field
    User->>LoginPage: Enter password
    User->>Turnstile: Complete CAPTCHA
    Turnstile-->>LoginPage: Return token
    LoginPage->>AuthAPI: Submit credentials + token
    alt Auth success
        AuthAPI-->>LoginPage: Success
        LoginPage->>Router: await router.replace('/apps' or '/dashboard')
        Router-->>User: Redirect
    else Auth failure
        AuthAPI-->>LoginPage: Error
        LoginPage->>Turnstile: Reset widget / clear token
        LoginPage->>LoginPage: Update flags / show errors
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I hopped in with an email bright,
Clicked continue — the password in sight,
Turnstile whispered a tiny token,
When errors roared, I reset and woken,
Off I bounded — login fixed and light!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix(frontend): restore login flow loading states' clearly summarizes the main change—restoring inline loading states in the login flow.
Description check ✅ Passed The description covers the summary, motivation, and test plan sections with concrete details; however, the Screenshots and Checklist sections required by the template are missing.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/fix-login-loader-flow

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8e7364923b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/pages/login.vue Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/pages/login.vue (1)

505-514: Extract repeated spinner markup into a reusable component/helper.

The same SVG block is duplicated across multiple buttons. Centralizing it will reduce drift and simplify future style/test updates.

Also applies to: 545-554, 601-610, 681-690, 755-764

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/login.vue` around lines 505 - 514, Create a reusable spinner
component (e.g., LoadingSpinner) that encapsulates the SVG and accepts
class/name props (or simply forwards attributes) and the data-test value; then
replace each duplicated SVG block in src/pages/login.vue (the repeated spinner
used with v-if="isLoading") with the new component (e.g., <LoadingSpinner
v-if="isLoading" />), import/register the component in the Login component, and
ensure the component forwards classes/attributes so existing styling and
data-test="loading" continue to work.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/pages/login.vue`:
- Around line 347-350: The branch that handles a falsy result from
autoAuth(route) (where logSession is falsy) returns early without calling
hideLoader(), which can leave the global loader visible; update the conditional
in the login flow so that before returning when !logSession you call
hideLoader() (and ensure isLoading.value is set appropriately), or wrap the
autoAuth(route) call and subsequent logic in a try/finally that calls
hideLoader() to guarantee the loader is hidden; look for symbols autoAuth,
logSession, isLoading.value and hideLoader to apply the fix.
- Around line 373-427: The try/finally in checkLogin() resets
isCheckingSavedSession but doesn't catch errors causing loader/UI
inconsistencies; wrap the existing try body with a catch that logs the error (or
shows a toast), sets isLoading.value = false, calls hideLoader(), and ensures
any other UI flags (e.g., hasQuerySession.value) are left consistent before
rethrowing or swallowing as appropriate; specifically modify the function
checkLogin() to add a catch block that handles errors from
supabase.auth.getClaims(), getSession(), exchangeCodeForSession(),
checkAuthUser(), and checkMagicLink() and performs the cleanup (isLoading.value
= false, hideLoader(), optionally toast.error or console.error) before the
finally block runs to reset isCheckingSavedSession.value.

---

Nitpick comments:
In `@src/pages/login.vue`:
- Around line 505-514: Create a reusable spinner component (e.g.,
LoadingSpinner) that encapsulates the SVG and accepts class/name props (or
simply forwards attributes) and the data-test value; then replace each
duplicated SVG block in src/pages/login.vue (the repeated spinner used with
v-if="isLoading") with the new component (e.g., <LoadingSpinner v-if="isLoading"
/>), import/register the component in the Login component, and ensure the
component forwards classes/attributes so existing styling and
data-test="loading" continue to work.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 11230ed6-4152-410e-b324-6501635382bb

📥 Commits

Reviewing files that changed from the base of the PR and between 55662cb and 8e73649.

📒 Files selected for processing (3)
  • playwright/e2e/auth.spec.ts
  • playwright/support/commands.ts
  • src/pages/login.vue

Comment thread src/pages/login.vue
Comment thread src/pages/login.vue
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b4b24ea70c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/pages/login.vue Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
src/pages/login.vue (3)

582-591: Consider extracting the spinner SVG into a reusable component.

The same inline spinner SVG is repeated five times across the template. Extracting it would reduce duplication and simplify future styling changes.

Example extraction

Create a small component or use a shared template fragment:

<!-- components/LoadingSpinner.vue -->
<template>
  <svg
    class="inline-block mr-3 -ml-1 w-5 h-5 text-white align-middle animate-spin"
    xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
  >
    <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" />
    <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" />
  </svg>
</template>

Then replace inline SVGs with <LoadingSpinner v-if="isLoading" />.

Also applies to: 622-631, 685-694, 772-781, 846-855

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/login.vue` around lines 582 - 591, Extract the repeated inline
spinner SVG into a reusable Vue component (e.g., LoadingSpinner) and replace
each inline SVG instance that uses v-if="isLoading" (the SVG block present in
src/pages/login.vue and repeated at the other locations) with <LoadingSpinner
v-if="isLoading" />; ensure the new component preserves the original classes
(inline-block mr-3 -ml-1 w-5 h-5 text-white align-middle animate-spin), viewBox,
and child elements so styling/behavior is identical, then import/register
LoadingSpinner in the parent component(s).

507-523: nextLogin() is not awaited, unlike other call sites.

At line 522, nextLogin() is called without await, while all other invocations (lines 163, 345, 436, 482) await it. This inconsistency could cause the function to return before navigation completes.

Proposed fix
   hasQuerySession.value = false
   querySessionAccessToken.value = ''
   querySessionRefreshToken.value = ''
-  nextLogin()
+  await nextLogin()
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/login.vue` around lines 507 - 523, The acceptQuerySession function
calls nextLogin() without awaiting it, causing inconsistent behavior compared to
other call sites; update acceptQuerySession to await nextLogin() after
successful session set (ensure isLoading.value is handled appropriately around
the await) so navigation completes before the function returns—locate the
acceptQuerySession function and change the final call to await nextLogin() to
match the other usages where nextLogin is awaited.

64-70: Consider typing window.turnstile to avoid implicit any.

window.turnstile is accessed without a type declaration. If strict TypeScript is enabled, this may produce a warning.

Optional fix

Add a type declaration in a .d.ts file or inline:

declare global {
  interface Window {
    turnstile?: unknown
  }
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/login.vue` around lines 64 - 70, The code reads window.turnstile
without a type, causing implicit any under strict TS; add a type declaration for
turnstile (e.g., augment the global Window interface with turnstile?: unknown or
the appropriate type) in a .d.ts or module file so references in the captcha
init logic (captchaInitTimeout, clearCaptchaInitTimeout, and the check
`window.turnstile`) are properly typed; alternatively, narrow or cast `window`
access at the use site to the declared type to satisfy the compiler.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/pages/login.vue`:
- Around line 582-591: Extract the repeated inline spinner SVG into a reusable
Vue component (e.g., LoadingSpinner) and replace each inline SVG instance that
uses v-if="isLoading" (the SVG block present in src/pages/login.vue and repeated
at the other locations) with <LoadingSpinner v-if="isLoading" />; ensure the new
component preserves the original classes (inline-block mr-3 -ml-1 w-5 h-5
text-white align-middle animate-spin), viewBox, and child elements so
styling/behavior is identical, then import/register LoadingSpinner in the parent
component(s).
- Around line 507-523: The acceptQuerySession function calls nextLogin() without
awaiting it, causing inconsistent behavior compared to other call sites; update
acceptQuerySession to await nextLogin() after successful session set (ensure
isLoading.value is handled appropriately around the await) so navigation
completes before the function returns—locate the acceptQuerySession function and
change the final call to await nextLogin() to match the other usages where
nextLogin is awaited.
- Around line 64-70: The code reads window.turnstile without a type, causing
implicit any under strict TS; add a type declaration for turnstile (e.g.,
augment the global Window interface with turnstile?: unknown or the appropriate
type) in a .d.ts or module file so references in the captcha init logic
(captchaInitTimeout, clearCaptchaInitTimeout, and the check `window.turnstile`)
are properly typed; alternatively, narrow or cast `window` access at the use
site to the declared type to satisfy the compiler.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 048faea1-906f-46e3-a752-8edd162d7c21

📥 Commits

Reviewing files that changed from the base of the PR and between b4b24ea and 15097b0.

📒 Files selected for processing (1)
  • src/pages/login.vue

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ba8f9a2074

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread playwright.config.ts Outdated
@riderx riderx merged commit 2afc7d2 into main Mar 25, 2026
13 checks passed
@riderx riderx deleted the codex/fix-login-loader-flow branch March 25, 2026 02:57
@sonarqubecloud
Copy link
Copy Markdown

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.

1 participant