import { LitElement, html, css } from 'lit'; import { customElement, state } from 'lit/decorators.js'; import { Router } from '@vaadin/router'; import { defineComponents, IgcButtonComponent, IgcDialogComponent, IgcIconComponent, IgcInputComponent } from 'igniteui-webcomponents'; import { Authentication } from '../services/authentication.js'; import { ExternalAuth } from '../services/externalAuth.js'; import { UserStore } from '../services/userStore.js'; import type { User } from '../models/user.js'; defineComponents(IgcButtonComponent, IgcDialogComponent, IgcIconComponent, IgcInputComponent); @customElement('auth-login-dialog') export class LoginDialogElement extends LitElement { @state() private showLogin = true; @state() private error = ''; @state() private _loginValid = false; @state() private _registerValid = false; static styles = css` igc-dialog::part(base) { max-width: 24rem; width: calc(100vw - 48px); } igc-dialog::part(title) { font-size: 1.125rem; font-weight: 600; color: #2d2d2d; border-bottom: none; } .form { display: flex; flex-flow: column; gap: 16px; padding: 8px 0 0; } .form > * { width: 100%; } igc-input { --ig-input-group-focused-secondary-color: #239ef0; --ig-input-group-focused-border-color: #239ef0; --ig-input-group-filled-text-color: #2d2d2d; } igc-input igc-icon { color: #0075d2; --ig-icon-size: 1.50rem; } .error { margin: 0; font-size: .875rem; color: #d32f2f; } .submit-btn { display: block; } .submit-btn::part(base) { width: 100%; min-height: 40px; font-weight: 600; text-transform: uppercase; } .submit-btn:not([disabled])::part(base) { background: #239ef0; color: #fff; } .submit-btn:not([disabled])::part(base):hover { background: #1a8fd8; } .submit-btn[disabled]::part(base) { background: #e0e0e0; color: #767676; } .link-btn { align-self: center; text-align: center; color: #0075d2; font-size: .875rem; cursor: pointer; text-decoration: underline; text-transform: none; } .link-btn:hover, .link-btn:focus-visible { color: #005da8; } .social-login { display: grid; gap: 8px; padding-top: 16px; border-top: 1px solid #d7d7d7; } .social-btn { display: block; } .social-btn::part(base) { width: 100%; min-height: 40px; color: #fff; font-weight: 600; text-transform: uppercase; } .google::part(base) { background: rgb(255, 19, 74); } .facebook::part(base) { background: rgb(19, 119, 213); } .microsoft::part(base) { background: rgb(27, 158, 245); } `; private dialogRef: IgcDialogComponent | null = null; public open() { this.showLogin = true; this.error = ''; this.dialogRef?.show(); } firstUpdated() { this.dialogRef = this.shadowRoot?.querySelector('igc-dialog') ?? null; } private checkLoginValidity = (e: Event) => { this._loginValid = (e.currentTarget as HTMLFormElement).checkValidity(); }; private checkRegisterValidity = (e: Event) => { this._registerValid = (e.currentTarget as HTMLFormElement).checkValidity(); }; private handleLoginSubmit = async (e: Event) => { e.preventDefault(); this.error = ''; const form = e.target as HTMLFormElement; const data = new FormData(form); const result = await Authentication.login({ email: data.get('email') as string, password: data.get('password') as string, }); if (result.user) { form.reset(); this._loginValid = false; UserStore.setUser(result.user as User); this.dialogRef?.hide(); this.dispatchEvent(new CustomEvent('auth-change', { bubbles: true, composed: true })); Router.go('/auth/profile'); } else { this.error = result.error ?? 'Login failed'; } }; private handleRegisterSubmit = async (e: Event) => { e.preventDefault(); this.error = ''; const form = e.target as HTMLFormElement; const data = new FormData(form); const result = await Authentication.register({ given_name: data.get('given_name') as string, family_name: data.get('family_name') as string, email: data.get('email') as string, password: data.get('password') as string, }); if (result.user) { form.reset(); this._registerValid = false; UserStore.setUser(result.user as User); this.dialogRef?.hide(); this.dispatchEvent(new CustomEvent('auth-change', { bubbles: true, composed: true })); Router.go('/auth/profile'); } else { this.error = result.error ?? 'Registration failed'; } }; render() { const title = this.showLogin ? 'Login' : 'Register'; const loginForm = html`
${this.error ? html`

${this.error}

` : ''} Log In { this.showLogin = false; this.error = ''; }} role="button" tabindex="0">Create new account ${ExternalAuth.hasProvider() ? html`
${ExternalAuth.hasProvider('google') ? html` ` : ''} ${ExternalAuth.hasProvider('facebook') ? html` ` : ''} ${ExternalAuth.hasProvider('microsoft') ? html` ` : ''}
` : ''}
`; const registerForm = html`
${this.error ? html`

${this.error}

` : ''} Sign Up { this.showLogin = true; this.error = ''; }} role="button" tabindex="0">Have an account?
`; return html` ${this.showLogin ? loginForm : registerForm} `; } } declare global { interface HTMLElementTagNameMap { 'auth-login-dialog': LoginDialogElement; } }