From e8b2c6456432c844b86c1bc2e3032c4269c38fc4 Mon Sep 17 00:00:00 2001 From: user Date: Mon, 27 Apr 2026 14:28:09 +0900 Subject: [PATCH] ui: redesign login lobby, fix auth view transition timing --- .gitignore | Bin 18 -> 32 bytes internal/webapp/static/app.js | 25 +++++++-- internal/webapp/static/index.html | 15 +++++- internal/webapp/static/styles.css | 85 ++++++++++++++++++++++++++---- 4 files changed, 111 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index 9b10a170bd934fd9999b16a9440f3203b486fb82..e7b5d5a17275d5a7e0830070113313f439d69f31 100644 GIT binary patch literal 32 hcmdPP&#lns(ql+v$YUsD;AP-q&|(1bDu5Wo0{~?Q1p5F0 literal 18 ZcmdPP&#lns(ql+v$YUsD;AP-q001a214;k@ diff --git a/internal/webapp/static/app.js b/internal/webapp/static/app.js index de2b81a..b215fcc 100644 --- a/internal/webapp/static/app.js +++ b/internal/webapp/static/app.js @@ -364,28 +364,45 @@ function evidenceBlock(evidence = []) { return `

Evidence

`; } -window.handleCredentialResponse = async (response) => { +window._tutorGoogleCallback = async (response) => { + console.log("[auth] Google callback fired"); try { const res = await request("/api/v1/auth/google", { method: "POST", body: JSON.stringify({ id_token: response.credential }), }); + console.log("[auth] Backend login success", res.user?.email); localStorage.setItem("tutor_token", res.token); localStorage.setItem("tutor_user", JSON.stringify(res.user)); + if (els.loginError) { + els.loginError.textContent = ""; + els.loginError.classList.remove("visible"); + } renderAuth(); } catch (err) { - if (els.loginError) els.loginError.textContent = err.message; + console.error("[auth] Backend login failed", err); + if (els.loginError) { + els.loginError.textContent = err.message; + els.loginError.classList.add("visible"); + } } }; +if (window._tutorPendingGoogleResponse) { + window._tutorGoogleCallback(window._tutorPendingGoogleResponse); + window._tutorPendingGoogleResponse = null; +} + function renderAuth() { const user = JSON.parse(localStorage.getItem("tutor_user") || "null"); const token = localStorage.getItem("tutor_token"); + console.log("[auth] renderAuth", { hasUser: !!user, hasToken: !!token }); if (user && token) { els.loginView.style.display = "none"; els.workspaceView.style.display = "grid"; - els.userInfo.textContent = user.email; - setStatus(`Signed in as ${user.email}`); + els.userInfo.textContent = user.email || user.name || "User"; + setStatus(`Signed in as ${user.email || user.name}`); + if (els.loginError) els.loginError.classList.remove("visible"); } else { els.loginView.style.display = "flex"; els.workspaceView.style.display = "none"; diff --git a/internal/webapp/static/index.html b/internal/webapp/static/index.html index f95d7b9..d322860 100644 --- a/internal/webapp/static/index.html +++ b/internal/webapp/static/index.html @@ -7,6 +7,17 @@ +
@@ -14,11 +25,13 @@

Tutor Platform

Interview practice

Prove you are becoming more interview-ready after each short practice loop.

+
- + +
diff --git a/internal/webapp/static/styles.css b/internal/webapp/static/styles.css index 064a878..bae1b34 100644 --- a/internal/webapp/static/styles.css +++ b/internal/webapp/static/styles.css @@ -44,7 +44,7 @@ select { } .workspace { - display: grid; + display: none; grid-template-columns: minmax(260px, 320px) minmax(360px, 1fr) minmax(280px, 360px); gap: 1px; min-height: 100vh; @@ -464,27 +464,94 @@ button.is-loading .btn-spinner { justify-content: center; min-height: 100vh; padding: 28px; - background: var(--bg); + background: linear-gradient(160deg, #f5f7f4 0%, #e8efe5 100%); } .login-card { background: var(--surface); border: 1px solid var(--line); - border-radius: 12px; - padding: 44px 36px; - max-width: 420px; + border-radius: 18px; + padding: 52px 40px 44px; + max-width: 440px; width: 100%; text-align: center; + box-shadow: 0 12px 40px rgba(24, 32, 27, 0.07); +} + +.login-card .eyebrow { + display: inline-block; + letter-spacing: 0.14em; + font-size: 11px; + text-transform: uppercase; + color: var(--accent); + font-weight: 750; + margin-bottom: 10px; +} + +html[lang="ko"] .login-card .eyebrow { + text-transform: none; + letter-spacing: 0.04em; } .login-card h1 { - font-size: clamp(32px, 5vw, 52px); - margin: 14px 0 18px; + font-size: clamp(34px, 5.5vw, 56px); + margin: 8px 0 18px; + line-height: 1.05; } .login-card .lede { - margin: 0 auto 28px; - max-width: 34ch; + margin: 0 auto 32px; + max-width: 32ch; + color: var(--muted); + font-size: 15px; + line-height: 1.55; +} + +.login-divider { + height: 1px; + background: var(--line); + margin: 28px 0 24px; + position: relative; +} + +.login-divider::before { + content: attr(data-label); + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background: var(--surface); + padding: 0 12px; + color: var(--muted); + font-size: 11px; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.login-legal { + margin-top: 18px; + font-size: 12px; + color: var(--muted); + line-height: 1.5; +} + +.login-legal a { + color: var(--muted); + text-decoration: underline; + text-underline-offset: 2px; +} + +.login-error { + margin-top: 14px; + padding: 10px 14px; + border-radius: 8px; + background: var(--weak-bg); + color: var(--weak); + font-size: 13px; + display: none; +} +.login-error.visible { + display: block; } .user-bar {