From a3255602b36e405982ae15f3fc5565e3c5ea5a57 Mon Sep 17 00:00:00 2001 From: shahdlala66 Date: Mon, 4 May 2026 19:48:55 +0200 Subject: [PATCH] feat: me tab added --- src/app/app.routes.ts | 2 + src/app/button-template.css | 31 +++- .../input-card/input-card.component.css | 4 +- .../login-dialog/login-dialog.component.css | 15 +- .../promotion-dialog.component.css | 20 +-- .../register-dialog.component.css | 15 +- .../components/toolbar/toolbar.component.css | 75 ++++++++- .../components/toolbar/toolbar.component.html | 6 +- .../components/toolbar/toolbar.component.ts | 6 + src/app/pages/profile/profile.component.css | 154 ++++++++++++++++++ src/app/pages/profile/profile.component.html | 66 ++++++++ src/app/pages/profile/profile.component.ts | 107 ++++++++++++ src/app/pages/welcome/welcome.component.html | 2 +- 13 files changed, 447 insertions(+), 56 deletions(-) create mode 100644 src/app/pages/profile/profile.component.css create mode 100644 src/app/pages/profile/profile.component.html create mode 100644 src/app/pages/profile/profile.component.ts diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index 215ff45..9fb0ff3 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -1,9 +1,11 @@ import { Routes } from '@angular/router'; import { GameComponent } from './pages/game/game.component'; import { WelcomeComponent } from './pages/welcome/welcome.component'; +import { ProfileComponent } from './pages/profile/profile.component'; export const routes: Routes = [ { path: '', component: WelcomeComponent }, + { path: 'profile', component: ProfileComponent }, { path: 'game/:gameId', component: GameComponent }, { path: '**', redirectTo: '' } ]; diff --git a/src/app/button-template.css b/src/app/button-template.css index 9bf4095..5b8888a 100644 --- a/src/app/button-template.css +++ b/src/app/button-template.css @@ -1,4 +1,4 @@ -/* Shared Button Template */ +/* Shared Button Template - All Button Styles Consolidated */ .app-btn { background: var(--btn-bg); @@ -38,3 +38,32 @@ .app-btn.w-100 { width: 100%; } + +/* Dialog Button Layouts */ +.dialog-actions { + display: flex; + gap: 0.6rem; + flex-wrap: wrap; +} + +.dialog-actions .app-btn { + flex: 1; + min-width: 120px; +} + +/* Promotion Dialog Button Variant */ +.promotion-choice { + flex-direction: column; + height: auto; + padding: 16px; + gap: 8px; +} + +.promotion-choice .piece-symbol { + font-size: 32px; + line-height: 1; +} + +.promotion-choice .piece-label { + font-size: 11px; +} diff --git a/src/app/components/input-card/input-card.component.css b/src/app/components/input-card/input-card.component.css index df6cc1c..65aa86b 100644 --- a/src/app/components/input-card/input-card.component.css +++ b/src/app/components/input-card/input-card.component.css @@ -1,3 +1,5 @@ +@import '../../button-template.css'; + .input-card { background: var(--color-bg-card); border: var(--border-width) solid var(--color-border); @@ -40,8 +42,6 @@ } -@import '../../button-template.css'; - .hint-text { margin: 0; color: var(--color-text-primary); diff --git a/src/app/components/login-dialog/login-dialog.component.css b/src/app/components/login-dialog/login-dialog.component.css index 6a0a6a9..acea1e8 100644 --- a/src/app/components/login-dialog/login-dialog.component.css +++ b/src/app/components/login-dialog/login-dialog.component.css @@ -1,3 +1,5 @@ +@import '../../button-template.css'; + .dialog-overlay { position: fixed; inset: 0; @@ -27,19 +29,6 @@ text-align: center; } -@import '../../button-template.css'; - -.dialog-actions { - display: flex; - gap: 0.6rem; - flex-wrap: wrap; -} - -.dialog-actions .app-btn { - flex: 1; - min-width: 120px; -} - .dialog-input { width: 100%; background: rgba(4, 4, 20, 0.62); diff --git a/src/app/components/promotion-dialog/promotion-dialog.component.css b/src/app/components/promotion-dialog/promotion-dialog.component.css index 8b77042..aedff18 100644 --- a/src/app/components/promotion-dialog/promotion-dialog.component.css +++ b/src/app/components/promotion-dialog/promotion-dialog.component.css @@ -1,3 +1,5 @@ +@import '../../button-template.css'; + .promotion-dialog-overlay { position: fixed; top: 0; @@ -73,21 +75,3 @@ grid-template-columns: 1fr 1fr; gap: 12px; } - -@import '../../button-template.css'; - -.promotion-choice { - flex-direction: column; - height: auto; - padding: 16px; - gap: 8px; -} - -.promotion-choice .piece-symbol { - font-size: 32px; - line-height: 1; -} - -.promotion-choice .piece-label { - font-size: 11px; -} diff --git a/src/app/components/register-dialog/register-dialog.component.css b/src/app/components/register-dialog/register-dialog.component.css index 6a0a6a9..acea1e8 100644 --- a/src/app/components/register-dialog/register-dialog.component.css +++ b/src/app/components/register-dialog/register-dialog.component.css @@ -1,3 +1,5 @@ +@import '../../button-template.css'; + .dialog-overlay { position: fixed; inset: 0; @@ -27,19 +29,6 @@ text-align: center; } -@import '../../button-template.css'; - -.dialog-actions { - display: flex; - gap: 0.6rem; - flex-wrap: wrap; -} - -.dialog-actions .app-btn { - flex: 1; - min-width: 120px; -} - .dialog-input { width: 100%; background: rgba(4, 4, 20, 0.62); diff --git a/src/app/components/toolbar/toolbar.component.css b/src/app/components/toolbar/toolbar.component.css index 9f52ba7..be8f759 100644 --- a/src/app/components/toolbar/toolbar.component.css +++ b/src/app/components/toolbar/toolbar.component.css @@ -1,9 +1,12 @@ +@import '../../button-template.css'; + .navbar { - background: var(--dlg-bg); - border: 1.5px solid var(--dlg-border); - box-shadow: var(--bb-glow); - border-radius: 4px; - padding: 0.25rem 0.75rem; + background: rgba(8, 6, 28, 0.85); + backdrop-filter: blur(8px); + box-shadow: 0 4px 20px rgba(0, 210, 255, 0.15); + border-bottom: 1px solid rgba(0, 210, 255, 0.2); + border-radius: 0; + padding: 0.75rem 1rem; } .navbar-brand { @@ -12,10 +15,70 @@ color: var(--bb-title) !important; font-family: 'Bebas Neue', sans-serif; letter-spacing: 1px; + cursor: pointer; } .gap-2 { gap: 0.5rem; } -@import '../../button-template.css'; +.user-section { + align-items: center; +} + +.me-btn { + background: rgba(0, 210, 255, 0.1); + color: var(--bb-title); + border: 1px solid var(--bb-border); + border-radius: 2px; + padding: 0.5rem 0.8rem; + font-family: 'Space Mono', monospace; + font-size: 11px; + font-weight: 700; + letter-spacing: 0.5px; + cursor: pointer; + transition: all 0.2s ease; + display: inline-flex; + align-items: center; + justify-content: center; + outline: none; + text-transform: uppercase; +} + +.me-btn:hover { + background: rgba(0, 210, 255, 0.2); + border-color: var(--bb-tag); + box-shadow: 0 0 10px rgba(0, 210, 255, 0.4); + transform: scale(1.05); +} + +.me-btn:active { + transform: scale(0.98); +} + +/* Sunset Mode */ +.sunset .navbar { + background: rgba(20, 5, 45, 0.85); + border-bottom-color: rgba(255, 64, 207, 0.2); + box-shadow: 0 4px 20px rgba(242, 106, 226, 0.15); +} + +.sunset .me-btn { + background: rgba(242, 106, 226, 0.1); + border-color: var(--bb-border); +} + +.sunset .me-btn:hover { + background: rgba(242, 106, 226, 0.2); + border-color: var(--bb-tag); + box-shadow: 0 0 10px rgba(242, 106, 226, 0.4); +} + +.container-fluid { + display: flex; + align-items: center; +} + +.ms-auto { + margin-left: auto; +} diff --git a/src/app/components/toolbar/toolbar.component.html b/src/app/components/toolbar/toolbar.component.html index 01789f8..cba57a3 100644 --- a/src/app/components/toolbar/toolbar.component.html +++ b/src/app/components/toolbar/toolbar.component.html @@ -7,8 +7,10 @@ {{ isDarkMode ? 'Light mode' : 'Dark mode' }} @if (currentUser; as user) { -
- {{ user.username }} +
+
} @else { diff --git a/src/app/components/toolbar/toolbar.component.ts b/src/app/components/toolbar/toolbar.component.ts index 63d7da6..bc4a815 100644 --- a/src/app/components/toolbar/toolbar.component.ts +++ b/src/app/components/toolbar/toolbar.component.ts @@ -1,5 +1,6 @@ import { Component, DestroyRef, OnInit, inject } from '@angular/core'; import { CommonModule } from '@angular/common'; +import { Router } from '@angular/router'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { AuthService } from '../../services/auth.service'; import { AuthDialogService } from '../../services/auth-dialog.service'; @@ -20,6 +21,7 @@ export class ToolbarComponent implements OnInit { private readonly authService = inject(AuthService); private readonly authDialogService = inject(AuthDialogService); private readonly themeService = inject(ThemeService); + private readonly router = inject(Router); currentUser: CurrentUser | null = null; showLoginDialog = false; @@ -71,6 +73,10 @@ export class ToolbarComponent implements OnInit { this.themeService.toggleTheme(); } + goToProfile(): void { + this.router.navigate(['/profile']); + } + onLoginSuccess(): void { this.closeLoginDialog(); } diff --git a/src/app/pages/profile/profile.component.css b/src/app/pages/profile/profile.component.css new file mode 100644 index 0000000..a50dd1c --- /dev/null +++ b/src/app/pages/profile/profile.component.css @@ -0,0 +1,154 @@ +@import '../welcome/welcome.component.css'; + +.profile-card-container { + position: absolute; + bottom: 50%; + left: 50%; + transform: translate(-50%, 50%); + width: min(500px, 90vw); + background: var(--dlg-bg); + border: 1.5px solid var(--dlg-border); + box-shadow: var(--bb-glow); + border-radius: 4px; + padding: 2rem; + z-index: 15; +} + +.profile-header { + display: flex; + flex-direction: column; + align-items: center; + gap: 1rem; + margin-bottom: 2rem; + border-bottom: 1px solid var(--dlg-border); + padding-bottom: 1.5rem; +} + +.back-btn { + align-self: flex-start; + background: transparent; + color: var(--bb-title); + border: 1px solid var(--dlg-border); + border-radius: 2px; + padding: 0.5rem 1rem; + font-family: 'Space Mono', monospace; + font-size: 10px; + font-weight: 700; + letter-spacing: 1px; + cursor: pointer; + transition: all 0.2s ease; +} + +.back-btn:hover { + background: rgba(0, 210, 255, 0.1); + border-color: var(--bb-tag); + color: var(--bb-tag); +} + +.profile-header h1 { + margin: 0; + font-family: 'Bebas Neue', sans-serif; + font-size: 28px; + letter-spacing: 2px; + color: var(--bb-title); + text-align: center; + width: 100%; +} + +.profile-content { + display: grid; + gap: 2rem; +} + +.player-avatar-section { + display: flex; + justify-content: center; +} + +.avatar-placeholder { + width: 100px; + height: 100px; + border: 2px solid var(--dlg-border); + border-radius: 8px; + display: flex; + align-items: center; + justify-content: center; + background: rgba(0, 210, 255, 0.08); + font-size: 3.5rem; +} + +.player-info-section { + display: grid; + gap: 1.2rem; +} + +.info-row { + display: grid; + grid-template-columns: 150px 1fr; + gap: 1rem; + align-items: center; + padding-bottom: 0.8rem; + border-bottom: 1px solid rgba(0, 210, 255, 0.2); +} + +.info-row:last-child { + border-bottom: none; +} + +.info-label { + color: var(--bb-tag); + font-family: 'Space Mono', monospace; + font-weight: 700; + font-size: 12px; + letter-spacing: 0.5px; + text-transform: uppercase; +} + +.info-value { + color: var(--bb-title); + font-family: 'Space Mono', monospace; + font-size: 13px; + word-break: break-all; +} + +.sunset .profile-card-container { + background: var(--dlg-bg); + border-color: var(--dlg-border); +} + +.sunset .back-btn { + border-color: var(--dlg-border); + color: var(--bb-title); +} + +.sunset .back-btn:hover { + background: rgba(242, 106, 226, 0.1); + border-color: var(--bb-tag); + color: var(--bb-tag); +} + +@media (max-width: 600px) { + .profile-card-container { + padding: 1.5rem; + width: 95vw; + } + + .info-row { + grid-template-columns: 1fr; + gap: 0.3rem; + } + + .info-label { + font-size: 10px; + } + + .info-value { + font-size: 12px; + } + + .avatar-placeholder { + width: 80px; + height: 80px; + font-size: 2.5rem; + } +} diff --git a/src/app/pages/profile/profile.component.html b/src/app/pages/profile/profile.component.html new file mode 100644 index 0000000..b058eec --- /dev/null +++ b/src/app/pages/profile/profile.component.html @@ -0,0 +1,66 @@ +
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+ +
+
+
+ +

MY PROFILE

+
+ + @if (currentUser; as user) { +
+
+
+ 👤 +
+
+ +
+
+ Player ID: + {{ user.id }} +
+ +
+ Username: + {{ user.username }} +
+ +
+ Rating: + {{ user.rating }} +
+ +
+ Member Since: + {{ user.createdAt | date: 'MMM dd, yyyy' }} +
+
+
+ } +
+
+ +
+
+
+
diff --git a/src/app/pages/profile/profile.component.ts b/src/app/pages/profile/profile.component.ts new file mode 100644 index 0000000..36d522c --- /dev/null +++ b/src/app/pages/profile/profile.component.ts @@ -0,0 +1,107 @@ +import { Component, OnInit, DestroyRef, inject } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { Router } from '@angular/router'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { AuthService } from '../../services/auth.service'; +import { ThemeService } from '../../services/theme.service'; +import { CurrentUser } from '../../models/auth.models'; + +interface Star { + style: Record; +} + +interface BackgroundBuilding { + style: Record; +} + +@Component({ + selector: 'app-profile', + standalone: true, + imports: [CommonModule], + templateUrl: './profile.component.html', + styleUrl: './profile.component.css' +}) +export class ProfileComponent implements OnInit { + private readonly destroyRef = inject(DestroyRef); + private readonly authService = inject(AuthService); + private readonly themeService = inject(ThemeService); + private readonly router = inject(Router); + + currentUser: CurrentUser | null = null; + isSunsetMode = false; + + stars: Star[] = []; + bgBuildings: BackgroundBuilding[] = []; + + ngOnInit(): void { + this.authService.currentUser$ + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe((user) => { + this.currentUser = user; + if (!user) { + this.router.navigate(['']); + } + }); + + this.themeService.darkMode$ + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe((isDarkMode) => { + this.isSunsetMode = !isDarkMode; + }); + + this.generateStars(220); + this.generateBackgroundBuildings(); + } + + goBack(): void { + this.router.navigate(['']); + } + + private generateStars(count: number): void { + this.stars = Array.from({ length: count }, () => { + const size = Math.random() * 2 + 0.5; + return { + style: { + width: `${size}px`, + height: `${size}px`, + left: `${Math.random() * 100}%`, + top: `${Math.random() * 62}%`, + '--d': `${(Math.random() * 3 + 1.5).toFixed(1)}s`, + '--dl': `${-(Math.random() * 6).toFixed(1)}s` + } + }; + }); + } + + private generateBackgroundBuildings(): void { + const specs = [ + { l: '0%', w: '7%', h: '30vh' }, + { l: '3%', w: '4%', h: '18vh' }, + { l: '7%', w: '5%', h: '22vh' }, + { l: '11%', w: '8%', h: '28vh' }, + { l: '15%', w: '6%', h: '20vh' }, + { l: '18.5%', w: '4%', h: '18vh' }, + { l: '22.5%', w: '6%', h: '26vh' }, + { l: '28%', w: '5%', h: '25vh' }, + { l: '32%', w: '4%', h: '15vh' }, + { l: '35.5%', w: '4.5%', h: '20vh' }, + { l: '42%', w: '5%', h: '28vh' }, + { l: '47%', w: '5%', h: '22vh' }, + { l: '50%', w: '7%', h: '30vh' }, + { l: '55%', w: '6%', h: '27vh' }, + { l: '60.5%', w: '5%', h: '24vh' }, + { l: '64.5%', w: '3.5%', h: '17vh' }, + { l: '70%', w: '6%', h: '23vh' }, + { l: '75%', w: '4%', h: '19vh' }, + { l: '80.5%', w: '4%', h: '21vh' }, + { l: '85.5%', w: '9%', h: '32vh' }, + { l: '88%', w: '5%', h: '20vh' }, + { l: '91%', w: '3%', h: '16vh' }, + { l: '94%', w: '6%', h: '27vh' } + ]; + + this.bgBuildings = specs.map((spec) => ({ + style: { left: spec.l, width: spec.w, height: spec.h } + })); + } +} diff --git a/src/app/pages/welcome/welcome.component.html b/src/app/pages/welcome/welcome.component.html index 0a4def3..9cfe144 100644 --- a/src/app/pages/welcome/welcome.component.html +++ b/src/app/pages/welcome/welcome.component.html @@ -94,7 +94,7 @@
WELCOME
WELCOME TO
NOWCHESS
Play your next move from the skyline.
-