feat: login and register, style is not ready

This commit is contained in:
Lala, Shahd
2026-05-03 20:49:08 +00:00
parent aa70083aed
commit 361ce1e817
8 changed files with 64 additions and 64 deletions
+1 -1
View File
@@ -1,2 +1,2 @@
<app-toolbar /> <app-toolbar />
<router-outlet /> <router-outlet />
+1 -1
View File
@@ -10,7 +10,7 @@ import { ThemeService } from './services/theme.service';
styleUrl: './app.css' styleUrl: './app.css'
}) })
export class App implements OnInit { export class App implements OnInit {
constructor(private readonly themeService: ThemeService) {} constructor(private readonly themeService: ThemeService) { }
ngOnInit(): void { ngOnInit(): void {
this.themeService.initTheme(); this.themeService.initTheme();
@@ -7,18 +7,18 @@
<input id="username" type="text" class="dialog-input" formControlName="username" placeholder="Username" <input id="username" type="text" class="dialog-input" formControlName="username" placeholder="Username"
[disabled]="isLoading" /> [disabled]="isLoading" />
@if (loginForm.get('username')?.invalid && loginForm.get('username')?.touched) { @if (loginForm.get('username')?.invalid && loginForm.get('username')?.touched) {
<small class="text-danger">Username must be at least 3 characters</small> <small class="text-danger">Username must be at least 3 characters</small>
} }
<label for="password" class="sr-only">Password</label> <label for="password" class="sr-only">Password</label>
<input id="password" type="password" class="dialog-input" formControlName="password" placeholder="Password" <input id="password" type="password" class="dialog-input" formControlName="password" placeholder="Password"
[disabled]="isLoading" /> [disabled]="isLoading" />
@if (loginForm.get('password')?.invalid && loginForm.get('password')?.touched) { @if (loginForm.get('password')?.invalid && loginForm.get('password')?.touched) {
<small class="text-danger">Password must be at least 6 characters</small> <small class="text-danger">Password must be at least 6 characters</small>
} }
@if (errorMessage) { @if (errorMessage) {
<div class="error-banner">{{ errorMessage }}</div> <div class="error-banner">{{ errorMessage }}</div>
} }
<div class="dialog-actions"> <div class="dialog-actions">
@@ -26,11 +26,11 @@
<button type="button" class="dialog-btn" (click)="closeDialog()">Cancel</button> <button type="button" class="dialog-btn" (click)="closeDialog()">Cancel</button>
<button type="button" class="dialog-btn" (click)="onSubmit()" [disabled]="isLoading || loginForm.invalid"> <button type="button" class="dialog-btn" (click)="onSubmit()" [disabled]="isLoading || loginForm.invalid">
@if (isLoading) { @if (isLoading) {
<span class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span> <span class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
} }
Login Login
</button> </button>
</div> </div>
</form> </form>
</div> </div>
</div> </div>
@@ -6,26 +6,26 @@
<input id="username" type="text" class="dialog-input" formControlName="username" placeholder="Username" <input id="username" type="text" class="dialog-input" formControlName="username" placeholder="Username"
[disabled]="isLoading" /> [disabled]="isLoading" />
@if (registerForm.get('username')?.invalid && registerForm.get('username')?.touched) { @if (registerForm.get('username')?.invalid && registerForm.get('username')?.touched) {
<small class="text-danger">Username must be at least 3 characters</small> <small class="text-danger">Username must be at least 3 characters</small>
} }
<input id="email" type="email" class="dialog-input" formControlName="email" placeholder="Email" <input id="email" type="email" class="dialog-input" formControlName="email" placeholder="Email"
[disabled]="isLoading" /> [disabled]="isLoading" />
@if (registerForm.get('email')?.invalid && registerForm.get('email')?.touched) { @if (registerForm.get('email')?.invalid && registerForm.get('email')?.touched) {
<small class="text-danger">Please enter a valid email</small> <small class="text-danger">Please enter a valid email</small>
} }
<input id="password" type="password" class="dialog-input" formControlName="password" placeholder="Password" <input id="password" type="password" class="dialog-input" formControlName="password" placeholder="Password"
[disabled]="isLoading" /> [disabled]="isLoading" />
@if (registerForm.get('password')?.invalid && registerForm.get('password')?.touched) { @if (registerForm.get('password')?.invalid && registerForm.get('password')?.touched) {
<small class="text-danger">Password must be at least 6 characters</small> <small class="text-danger">Password must be at least 6 characters</small>
} }
<input id="confirmPassword" type="password" class="dialog-input" formControlName="confirmPassword" <input id="confirmPassword" type="password" class="dialog-input" formControlName="confirmPassword"
placeholder="Confirm Password" [disabled]="isLoading" /> placeholder="Confirm Password" [disabled]="isLoading" />
@if (errorMessage) { @if (errorMessage) {
<div class="error-banner">{{ errorMessage }}</div> <div class="error-banner">{{ errorMessage }}</div>
} }
<div class="dialog-actions"> <div class="dialog-actions">
@@ -33,11 +33,11 @@
<button type="button" class="dialog-btn" (click)="closeDialog()">Cancel</button> <button type="button" class="dialog-btn" (click)="closeDialog()">Cancel</button>
<button type="button" class="dialog-btn" (click)="onSubmit()" [disabled]="isLoading || registerForm.invalid"> <button type="button" class="dialog-btn" (click)="onSubmit()" [disabled]="isLoading || registerForm.invalid">
@if (isLoading) { @if (isLoading) {
<span class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span> <span class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
} }
Register Register
</button> </button>
</div> </div>
</form> </form>
</div> </div>
</div> </div>
@@ -7,17 +7,17 @@
{{ isDarkMode ? 'Light mode' : 'Dark mode' }} {{ isDarkMode ? 'Light mode' : 'Dark mode' }}
</button> </button>
@if (currentUser; as user) { @if (currentUser; as user) {
<div class="d-flex align-items-center gap-2"> <div class="d-flex align-items-center gap-2">
<span style="color:var(--bb-title);font-weight:700">{{ user.username }}</span> <span style="color:var(--bb-title);font-weight:700">{{ user.username }}</span>
<button class="dialog-btn" (click)="logout()">Logout</button> <button class="dialog-btn" (click)="logout()">Logout</button>
</div> </div>
} @else { } @else {
<button class="dialog-btn" (click)="openLoginDialog()"> <button class="dialog-btn" (click)="openLoginDialog()">
Login Login
</button> </button>
<button class="dialog-btn" (click)="openRegisterDialog()"> <button class="dialog-btn" (click)="openRegisterDialog()">
Register Register
</button> </button>
} }
</div> </div>
</div> </div>
@@ -25,9 +25,9 @@
</nav> </nav>
@if (showLoginDialog) { @if (showLoginDialog) {
<app-login-dialog (onClose)="closeLoginDialog()" (onSuccess)="onLoginSuccess()" /> <app-login-dialog (onClose)="closeLoginDialog()" (onSuccess)="onLoginSuccess()" />
} }
@if (showRegisterDialog) { @if (showRegisterDialog) {
<app-register-dialog (onClose)="closeRegisterDialog()" (onSuccess)="onRegisterSuccess()" /> <app-register-dialog (onClose)="closeRegisterDialog()" (onSuccess)="onRegisterSuccess()" />
} }
+3 -3
View File
@@ -123,7 +123,7 @@ export class WelcomeComponent implements OnInit, OnDestroy {
this.generateBackgroundBuildings(); this.generateBackgroundBuildings();
this.generateWindowsForAllBuildings(); this.generateWindowsForAllBuildings();
this.startWindowFlicker(); this.startWindowFlicker();
// Show speech bubble after 5 seconds // Show speech bubble after 5 seconds
this.speechBubbleTimeoutId = setTimeout(() => { this.speechBubbleTimeoutId = setTimeout(() => {
this.showSpeechBubble = true; this.showSpeechBubble = true;
@@ -245,7 +245,7 @@ export class WelcomeComponent implements OnInit, OnDestroy {
this.showMeatEmoji = true; this.showMeatEmoji = true;
this.showHappyBubble = false; this.showHappyBubble = false;
this.showSecondSpeechBubble = true; this.showSecondSpeechBubble = true;
// Reset meat position // Reset meat position
this.meatX = window.innerWidth / 2 - 100; this.meatX = window.innerWidth / 2 - 100;
this.meatY = window.innerHeight / 2 + 150; this.meatY = window.innerHeight / 2 + 150;
@@ -257,7 +257,7 @@ export class WelcomeComponent implements OnInit, OnDestroy {
this.showHappyBubble = false; this.showHappyBubble = false;
this.showMeatEmoji = false; this.showMeatEmoji = false;
this.bubbleMessage = 'meow'; this.bubbleMessage = 'meow';
if (this.zoomTimeoutId) { if (this.zoomTimeoutId) {
clearTimeout(this.zoomTimeoutId); clearTimeout(this.zoomTimeoutId);
} }
+11 -11
View File
@@ -5,19 +5,19 @@ export type AuthDialogState = 'login' | 'register' | null;
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class AuthDialogService { export class AuthDialogService {
private readonly dialogStateSubject = new BehaviorSubject<AuthDialogState>(null); private readonly dialogStateSubject = new BehaviorSubject<AuthDialogState>(null);
readonly dialogState$ = this.dialogStateSubject.asObservable(); readonly dialogState$ = this.dialogStateSubject.asObservable();
openLogin(): void { openLogin(): void {
this.dialogStateSubject.next('login'); this.dialogStateSubject.next('login');
} }
openRegister(): void { openRegister(): void {
this.dialogStateSubject.next('register'); this.dialogStateSubject.next('register');
} }
close(): void { close(): void {
this.dialogStateSubject.next(null); this.dialogStateSubject.next(null);
} }
} }
+24 -24
View File
@@ -3,32 +3,32 @@ import { BehaviorSubject } from 'rxjs';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class ThemeService { export class ThemeService {
private readonly darkModeSubject = new BehaviorSubject<boolean>(false); private readonly darkModeSubject = new BehaviorSubject<boolean>(false);
readonly darkMode$ = this.darkModeSubject.asObservable(); readonly darkMode$ = this.darkModeSubject.asObservable();
initTheme(): void { initTheme(): void {
const savedTheme = localStorage.getItem('theme'); const savedTheme = localStorage.getItem('theme');
this.applyDarkMode(savedTheme === 'dark'); this.applyDarkMode(savedTheme === 'dark');
}
toggleTheme(): void {
this.applyDarkMode(!this.darkModeSubject.value);
}
setDarkMode(isDarkMode: boolean): void {
this.applyDarkMode(isDarkMode);
}
private applyDarkMode(isDarkMode: boolean): void {
if (isDarkMode) {
document.documentElement.setAttribute('data-theme', 'dark');
localStorage.setItem('theme', 'dark');
} else {
document.documentElement.removeAttribute('data-theme');
localStorage.removeItem('theme');
} }
this.darkModeSubject.next(isDarkMode); toggleTheme(): void {
} this.applyDarkMode(!this.darkModeSubject.value);
}
setDarkMode(isDarkMode: boolean): void {
this.applyDarkMode(isDarkMode);
}
private applyDarkMode(isDarkMode: boolean): void {
if (isDarkMode) {
document.documentElement.setAttribute('data-theme', 'dark');
localStorage.setItem('theme', 'dark');
} else {
document.documentElement.removeAttribute('data-theme');
localStorage.removeItem('theme');
}
this.darkModeSubject.next(isDarkMode);
}
} }