Added a Login and Register-Page
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
import { LitElement, html, css } from 'lit';
|
||||
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);
|
||||
}
|
||||
|
||||
input {
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 6px;
|
||||
padding: 0.6rem;
|
||||
font-size: 1rem;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
ui-button {
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: crimson;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
`;
|
||||
|
||||
@state() email = '';
|
||||
@state() password = '';
|
||||
@state() error: string | null = null;
|
||||
@state() loading = false;
|
||||
|
||||
async handleLogin() {
|
||||
this.error = null;
|
||||
this.loading = true;
|
||||
try {
|
||||
const res = await apiPost<{ token: string }>('/api/auth/login', {
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
});
|
||||
localStorage.setItem('token', res.token);
|
||||
window.dispatchEvent(new CustomEvent('auth-changed')); // for later reactive flows
|
||||
} catch (e: any) {
|
||||
this.error = e.message || 'Login failed';
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div class="form">
|
||||
<h2>Login</h2>
|
||||
<input type="email" placeholder="Email" .value=${this.email} @input=${(e: any)=> (this.email = e.target.value)}
|
||||
/>
|
||||
<input type="password" placeholder="Password" .value=${this.password} @input=${(e: any)=> (this.password =
|
||||
e.target.value)}
|
||||
/>
|
||||
${this.error ? html`<div class="error">${this.error}</div>` : null}
|
||||
<ui-button ?disabled=${this.loading} @click=${this.handleLogin}>${this.loading ? 'Loading...' : 'Login'}</ui-button>
|
||||
<p>
|
||||
No account?
|
||||
<ui-link href="/register">Register</ui-link>
|
||||
</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
import { LitElement, html, css } from 'lit';
|
||||
import { customElement, state } from 'lit/decorators.js';
|
||||
import { apiPost } from '../../api/api';
|
||||
|
||||
@customElement('register-page')
|
||||
export class RegisterPage 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);
|
||||
}
|
||||
|
||||
input {
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 6px;
|
||||
padding: 0.6rem;
|
||||
font-size: 1rem;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.error {
|
||||
color: crimson;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
`;
|
||||
|
||||
@state() name = '';
|
||||
@state() email = '';
|
||||
@state() password = '';
|
||||
@state() error: string | null = null;
|
||||
@state() loading = false;
|
||||
|
||||
async handleRegister() {
|
||||
this.error = null;
|
||||
this.loading = true;
|
||||
try {
|
||||
const res = await apiPost<{ id: number; name: string; email: string }>(
|
||||
'/api/auth/register',
|
||||
{
|
||||
name: this.name,
|
||||
email: this.email,
|
||||
password: this.password,
|
||||
}
|
||||
);
|
||||
console.log('Registered user:', res);
|
||||
window.dispatchEvent(
|
||||
new CustomEvent('nav', {
|
||||
detail: { path: '/login' },
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
})
|
||||
);
|
||||
} catch (e: any) {
|
||||
this.error = e.message || 'Registration failed';
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div class="form">
|
||||
<h2>Register</h2>
|
||||
<input
|
||||
placeholder="Full name"
|
||||
.value=${this.name}
|
||||
@input=${(e: any) => (this.name = e.target.value)}
|
||||
/>
|
||||
<input
|
||||
type="email"
|
||||
placeholder="Email"
|
||||
.value=${this.email}
|
||||
@input=${(e: any) => (this.email = e.target.value)}
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
.value=${this.password}
|
||||
@input=${(e: any) => (this.password = e.target.value)}
|
||||
/>
|
||||
|
||||
${this.error ? html`<div class="error">${this.error}</div>` : null}
|
||||
|
||||
<ui-button
|
||||
?disabled=${this.loading}
|
||||
@click=${this.handleRegister}
|
||||
>${this.loading ? 'Loading...' : 'Register'}</ui-button
|
||||
>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { LitElement, html } from 'lit';
|
||||
import { customElement } from 'lit/decorators.js';
|
||||
|
||||
@customElement('competition-page')
|
||||
export class CompetitionPage extends LitElement {
|
||||
render() {
|
||||
return html`
|
||||
<h1>Welcome to the Competitions</h1>
|
||||
`;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user