Spaces:
Sleeping
Sleeping
| 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'; | |
| ({ | |
| 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); }, | |
| }); | |
| } | |
| } | |