diff --git a/flightscore/src/pages/auth/login-page.ts b/flightscore/src/pages/auth/login-page.ts index 9e9ef01..086e8e5 100644 --- a/flightscore/src/pages/auth/login-page.ts +++ b/flightscore/src/pages/auth/login-page.ts @@ -3,55 +3,160 @@ import { customElement, state } from 'lit/decorators.js'; import { apiPost } from '../../api/api'; import '../../components/ui-link'; - @customElement('login-page') export class LoginPage extends LitElement { static styles = css` - :host { - flex: 1; - background: linear-gradient( - 135deg, - var(--color-accent), - color-mix(in srgb, var(--color-accent) 30%, black) - ); - background-size: cover; - background-position: center; - display: flex; - justify-content: center; - align-items: center; - } - - - .form { - width: 100%; - max-width: 380px; - background: var(--color-bg-nav); - border: 1px solid var(--color-border); - border-radius: 12px; - padding: 2rem; - display: flex; - flex-direction: column; - gap: 1rem; - box-shadow: 0 8px 25px rgba(0, 0, 0, 0.25); - } + :host { + flex: 1; + display: flex; + justify-content: center; + align-items: center; + background: + radial-gradient( + ellipse at 30% 20%, + color-mix(in srgb, var(--color-accent) 15%, transparent) 0%, + transparent 50% + ), + radial-gradient( + ellipse at 70% 80%, + color-mix(in srgb, var(--color-accent) 10%, transparent) 0%, + transparent 50% + ), + var(--color-bg); + padding: 1.5rem; + } - input { - border: 1px solid var(--color-border); - border-radius: 6px; - padding: 0.6rem; - font-size: 1rem; - background: var(--color-bg); - color: var(--color-text); - } + .card { + width: 100%; + max-width: 400px; + background: var(--color-bg-nav); + border: 1px solid var(--color-border); + border-radius: 1rem; + padding: 2.5rem 2rem 2rem; + display: flex; + flex-direction: column; + gap: 1.25rem; + box-shadow: + 0 1px 3px rgba(0, 0, 0, 0.08), + 0 12px 40px rgba(0, 0, 0, 0.12); + backdrop-filter: blur(12px); + } - ui-button { - align-self: flex-end; - } + .header { + display: flex; + flex-direction: column; + gap: 0.35rem; + margin-bottom: 0.25rem; + } - .error { - color: crimson; - font-size: 0.9rem; - } + .header h2 { + margin: 0; + font-size: 1.5rem; + font-weight: 700; + letter-spacing: -0.02em; + color: var(--color-text); + } + + .header p { + margin: 0; + font-size: 0.875rem; + color: color-mix(in srgb, var(--color-text) 60%, transparent); + } + + .field { + display: flex; + flex-direction: column; + gap: 0.4rem; + } + + label { + font-size: 0.8rem; + font-weight: 600; + letter-spacing: 0.02em; + color: color-mix(in srgb, var(--color-text) 70%, transparent); + text-transform: uppercase; + } + + input { + border: 1px solid var(--color-border); + border-radius: 0.5rem; + padding: 0.65rem 0.75rem; + font-size: 0.95rem; + background: var(--color-bg); + color: var(--color-text); + outline: none; + transition: + border-color 0.25s ease, + box-shadow 0.25s ease; + } + + input::placeholder { + color: color-mix(in srgb, var(--color-text) 35%, transparent); + } + + input:focus { + border-color: var(--color-accent); + box-shadow: 0 0 0 3px + color-mix(in srgb, var(--color-accent) 15%, transparent); + } + + .error { + color: #e5484d; + font-size: 0.85rem; + font-weight: 500; + padding: 0.5rem 0.75rem; + background: color-mix(in srgb, #e5484d 8%, transparent); + border: 1px solid color-mix(in srgb, #e5484d 25%, transparent); + border-radius: 0.5rem; + } + + button { + margin-top: 0.25rem; + width: 100%; + padding: 0.7rem 1rem; + font-size: 0.95rem; + font-weight: 600; + letter-spacing: 0.01em; + color: #fff; + background: var(--color-accent); + border: none; + border-radius: 0.5rem; + cursor: pointer; + transition: + background 0.25s ease, + box-shadow 0.25s ease, + transform 0.15s ease, + opacity 0.25s ease; + } + + button:hover:not(:disabled) { + background: color-mix(in srgb, var(--color-accent) 85%, black); + box-shadow: 0 4px 14px + color-mix(in srgb, var(--color-accent) 35%, transparent); + } + + button:active:not(:disabled) { + transform: scale(0.98); + } + + button:disabled { + opacity: 0.55; + cursor: not-allowed; + } + + .footer { + text-align: center; + font-size: 0.875rem; + color: color-mix(in srgb, var(--color-text) 60%, transparent); + margin: 0; + } + + .divider { + width: 100%; + height: 1px; + background: var(--color-border); + margin: 0.25rem 0; + } `; @state() email = ''; @@ -69,11 +174,13 @@ export class LoginPage extends LitElement { }); localStorage.setItem('token', res.token); window.dispatchEvent(new CustomEvent('auth-changed')); - dispatchEvent(new CustomEvent('nav', { - detail: { path: "/cc/" }, - bubbles: true, - composed: true, - })); + dispatchEvent( + new CustomEvent('nav', { + detail: { path: '/cc/' }, + bubbles: true, + composed: true, + }) + ); } catch (e: any) { this.error = e.message || 'Login failed'; } finally { @@ -81,22 +188,52 @@ export class LoginPage extends LitElement { } } + private handleKeydown(e: KeyboardEvent) { + if (e.key === 'Enter') this.handleLogin(); + } + render() { return html` -
-

Login

- (this.email = e.target.value)} - /> - (this.password = - e.target.value)} - /> - ${this.error ? html`
${this.error}
` : null} - ${this.loading ? 'Loading...' : 'Login'} -

- No account? - Register -

-
+
+
+

Welcome back

+

Sign in to your FlightScore account

+
+ +
+ + (this.email = e.target.value)} + @keydown=${this.handleKeydown} + /> +
+ +
+ + (this.password = e.target.value)} + @keydown=${this.handleKeydown} + /> +
+ + ${this.error ? html`
${this.error}
` : null} + + + +
+ + +
`; } } \ No newline at end of file