medicodeapp / frontend /src /app /features /auth /register /register.component.ts
Denisijcu's picture
upload files
c98875e
import { Component, inject, signal } from '@angular/core';
import { FormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
import { RouterLink } from '@angular/router';
import { CommonModule } from '@angular/common';
import { AuthService } from '../../../core/services/auth.service';
@Component({
selector: 'app-register',
standalone: true,
imports: [ReactiveFormsModule, RouterLink, CommonModule],
template: `
<div class="auth-page">
<div class="auth-card">
<div class="auth-brand">
<div class="brand-icon">
<svg viewBox="0 0 40 40" fill="none"><rect width="40" height="40" rx="10" fill="#0EA5E9"/>
<path d="M20 8v24M8 20h24" stroke="white" stroke-width="3.5" stroke-linecap="round"/>
<circle cx="20" cy="20" r="5" fill="white" fill-opacity="0.3"/>
</svg>
</div>
<h1>MediCode</h1>
<p>Medical Coding Platform</p>
</div>
<form [formGroup]="form" (ngSubmit)="submit()" class="auth-form">
<h2>Create account</h2>
@if (error()) {
<div class="alert-error">{{ error() }}</div>
}
<div class="field">
<label>Full name</label>
<input type="text" formControlName="name" placeholder="Dr. Eliana Torres"
[class.invalid]="form.get('name')?.invalid && form.get('name')?.touched"/>
</div>
<div class="field">
<label>Email</label>
<input type="email" formControlName="email" placeholder="you@hospital.com"
[class.invalid]="form.get('email')?.invalid && form.get('email')?.touched"/>
</div>
<div class="field">
<label>Password</label>
<input type="password" formControlName="password" placeholder="Min. 8 characters"
[class.invalid]="form.get('password')?.invalid && form.get('password')?.touched"/>
</div>
<button type="submit" class="btn-primary" [disabled]="loading()">
@if (loading()) { <span class="spinner"></span> } @else { Create account }
</button>
<p class="auth-link">Already have an account? <a routerLink="/auth/login">Sign in</a></p>
</form>
</div>
<div class="auth-visual">
<div class="visual-content">
<h2>Join MediCode today.</h2>
<p>Streamline your medical coding workflow with AI-assisted ICD-10 and CPT lookup.</p>
</div>
</div>
</div>
`,
styles: [`
.auth-page { display:grid; grid-template-columns:1fr 1fr; min-height:100vh; background:#f8fafc; font-family:'Inter',system-ui,sans-serif; }
.auth-card { display:flex; flex-direction:column; justify-content:center; padding:3rem; max-width:480px; margin:0 auto; width:100%; }
.auth-brand { display:flex; flex-direction:column; align-items:flex-start; margin-bottom:2.5rem; }
.brand-icon svg { width:48px; height:48px; margin-bottom:0.75rem; }
.auth-brand h1 { font-size:1.75rem; font-weight:700; color:#0f172a; margin:0; }
.auth-brand p { color:#64748b; margin:0.25rem 0 0; font-size:0.9rem; }
.auth-form h2 { font-size:1.4rem; font-weight:600; color:#1e293b; margin-bottom:1.5rem; }
.alert-error { background:#fef2f2; border:1px solid #fecaca; color:#dc2626; padding:0.75rem 1rem; border-radius:8px; margin-bottom:1rem; font-size:0.875rem; }
.field { margin-bottom:1.25rem; }
.field label { display:block; font-size:0.875rem; font-weight:500; color:#374151; margin-bottom:0.5rem; }
.field input { width:100%; padding:0.75rem 1rem; border:1px solid #d1d5db; border-radius:8px; font-size:0.9375rem; background:white; box-sizing:border-box; outline:none; transition:border-color 0.15s,box-shadow 0.15s; }
.field input:focus { border-color:#0EA5E9; box-shadow:0 0 0 3px rgba(14,165,233,0.15); }
.field input.invalid { border-color:#f87171; }
.btn-primary { width:100%; padding:0.8rem; background:#0EA5E9; color:white; font-size:0.9375rem; font-weight:600; border:none; border-radius:8px; cursor:pointer; display:flex; justify-content:center; align-items:center; gap:0.5rem; }
.btn-primary:hover:not(:disabled) { background:#0284c7; }
.btn-primary:disabled { opacity:0.6; cursor:not-allowed; }
.spinner { width:18px; height:18px; border:2px solid rgba(255,255,255,0.4); border-top-color:white; border-radius:50%; animation:spin 0.6s linear infinite; }
@keyframes spin { to { transform:rotate(360deg); } }
.auth-link { margin-top:1.25rem; text-align:center; font-size:0.875rem; color:#64748b; }
.auth-link a { color:#0EA5E9; font-weight:500; text-decoration:none; }
.auth-visual { background:linear-gradient(135deg,#0c4a6e 0%,#0369a1 50%,#0EA5E9 100%); display:flex; align-items:center; justify-content:center; padding:4rem; }
.visual-content { color:white; max-width:380px; }
.visual-content h2 { font-size:2rem; font-weight:700; margin-bottom:1rem; }
.visual-content p { font-size:1.1rem; opacity:0.85; }
@media (max-width:768px) { .auth-page { grid-template-columns:1fr; } .auth-visual { display:none; } }
`],
})
export class RegisterComponent {
private fb = inject(FormBuilder);
private auth = inject(AuthService);
form = this.fb.group({
name: ['', Validators.required],
email: ['', [Validators.required, Validators.email]],
password: ['', [Validators.required, Validators.minLength(8)]],
});
loading = signal(false);
error = signal('');
submit() {
if (this.form.invalid) { this.form.markAllAsTouched(); return; }
this.loading.set(true);
this.error.set('');
const { name, email, password } = this.form.value;
this.auth.register(name!, email!, password!).subscribe({
next: () => this.loading.set(false),
error: (e) => { this.error.set(e.error?.message || 'Registration failed'); this.loading.set(false); },
});
}
}