fix(review): loop-2 findings — login fallback, proof-band confidence, contrast, content hedges
Code review: login no longer strands visitors with zero sign-in methods when
the providers fetch fails (falls back to SMS + error notice); skeleton while
providers load instead of a blank card.
Design re-audit: proof band flipped from apology to flex ('Don't take our
word for it / Every claim links to its proof') and promoted to the primary
heading tier; unverifiable '60 seconds'/'in minutes' speed claims dropped;
fg-subtle body text bumped to fg-muted (AA contrast); marketplace section
differentiated via border-y + elevated bg (adjacent hairlines removed);
preview frame traffic lights on-palette; header h-12 -> h-14 (hero svh calc
adjusted); emerald-400 -> --color-success token.
Content QA: rest-api article description drift between page and registry
resolved; ChatGPT plan-gating table and Atlassian Rovo SSE-cutoff claims now
carry dated hedges.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SXUwmPVRTD8AKQtio6gCN5
This commit is contained in:
@@ -208,6 +208,7 @@ export default function LoginPage() {
|
||||
// product should never see a phone-number field as the front door. It only
|
||||
// renders expanded when SMS is the sole configured provider.
|
||||
const [phoneOpen, setPhoneOpen] = useState(false);
|
||||
const [providersState, setProvidersState] = useState<'loading' | 'ready' | 'failed'>('loading');
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
// Email magic-link
|
||||
@@ -228,11 +229,21 @@ export default function LoginPage() {
|
||||
)
|
||||
.then((p) => {
|
||||
setProviders(p);
|
||||
setProvidersState('ready');
|
||||
// SMS as the only provider → show the phone form directly (no point
|
||||
// hiding the sole sign-in method behind a toggle).
|
||||
if (p.sms && !p.email && !p.google && !p.github) setPhoneOpen(true);
|
||||
})
|
||||
.catch(() => undefined);
|
||||
.catch(() => {
|
||||
// If the providers lookup fails, the page must not strand the visitor
|
||||
// with zero sign-in methods — fall back to SMS + phone form expanded
|
||||
// (the one flow that has no provider-specific client config) and say
|
||||
// why the other options are missing.
|
||||
setProviders({ google: false, github: false, sms: true, email: false });
|
||||
setPhoneOpen(true);
|
||||
setProvidersState('failed');
|
||||
setError('Some sign-in options could not be loaded. Reload the page to try again.');
|
||||
});
|
||||
const err = new URLSearchParams(window.location.search).get('error');
|
||||
if (err) setError(ERROR_COPY[err] ?? 'Sign-in failed. Please try again.');
|
||||
}, []);
|
||||
@@ -296,6 +307,15 @@ export default function LoginPage() {
|
||||
Passwordless — pick whichever is easiest.
|
||||
</p>
|
||||
|
||||
{/* Providers still loading → neutral skeleton instead of a blank card
|
||||
(previously nothing rendered until the fetch resolved). */}
|
||||
{providersState === 'loading' && (
|
||||
<div aria-hidden className="mt-7 space-y-2">
|
||||
<div className="h-10 w-full animate-pulse rounded-md bg-[--color-bg-elevated]" />
|
||||
<div className="h-10 w-full animate-pulse rounded-md bg-[--color-bg-elevated]" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{hasOAuth && (
|
||||
<div className="mt-7 space-y-2">
|
||||
{providers.google && (
|
||||
|
||||
Reference in New Issue
Block a user