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 @@
+
+
+
+
+
+
+
+
+
+
+
+ @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.
-