Compare commits
8 Commits
7de4f3784b
...
8457bdd4ca
| Author | SHA1 | Date | |
|---|---|---|---|
| 8457bdd4ca | |||
| fd8ac63dc3 | |||
| c4575fd219 | |||
| 8eb27ba8b9 | |||
| 3c1f5c76e0 | |||
| 36d72fd6cd | |||
| bd7ec581e3 | |||
| ff75c8ce2f |
@@ -0,0 +1,20 @@
|
||||
## 0.0.0 (2026-05-12)
|
||||
|
||||
### Features
|
||||
|
||||
* added bot, light and dark mode ([2de003e](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/commit/2de003e497baee72f998d0d805ca1e58aababe48))
|
||||
* added web view 1v1 ([1828fa3](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/commit/1828fa3275ddb8ce6bb90a9f6a970ec428ebce3a))
|
||||
* NCS-63 User account implementation ([#2](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/issues/2)) ([ff75c8c](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/commit/ff75c8ce2fad54137f04a14c15bc1d4a38fa9bb8))
|
||||
* NCS-75 Frontend Deployment Dockerfile ([#4](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/issues/4)) ([bd7ec58](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/commit/bd7ec581e38b5d8e775741bf16e19b4dc67b979e))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* build issues ([36d72fd](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/commit/36d72fd6cda41be51d28f8ac307dcdbcd31afa91))
|
||||
* cleaner components seperation ([8b090e4](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/commit/8b090e4d96c64c0adb253e3aefad7930108ccfb9))
|
||||
* gitignore ([4da882f](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/commit/4da882f481ba7a008aac868fb37de7cb2bafea5d))
|
||||
* GITIGNORE ([8df2d05](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/commit/8df2d0550ab17c9afb2d19c414eac700a75add02))
|
||||
* npm ([c11c1d4](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/commit/c11c1d4dce9de4bd5b463e891eebf961b37feb04))
|
||||
* removed .vs ([2833ead](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/commit/2833ead7be3b47ee5c188d2d21b7326cb3cb3f26))
|
||||
* removed cache files ([7ee59c4](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/commit/7ee59c434bf137a08fd560bbc02ceefbcfd90f04))
|
||||
* size of pieces and removed text view of the game state ([c60d00f](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/commit/c60d00f9d25247504845654615065fbccd7fe448))
|
||||
* structure ([3e8c7c4](https://git.janis-eccarius.de/NowChess/NowChess-Frontend/commit/3e8c7c4057e55aeec7cee8c24f6751ff24912c93))
|
||||
+12
-6
@@ -1,19 +1,25 @@
|
||||
FROM node:20-alpine AS build
|
||||
FROM --platform=$BUILDPLATFORM node:lts-alpine3.23 AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package*.json ./
|
||||
RUN npm ci --silent
|
||||
|
||||
RUN npm install
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
RUN npm run build -- --configuration production
|
||||
FROM --platform=$TARGETPLATFORM nginx:stable-alpine AS production
|
||||
|
||||
FROM nginx:stable-alpine
|
||||
RUN apk add --no-cache gettext
|
||||
|
||||
RUN rm -rf /usr/share/nginx/html/*
|
||||
COPY --from=builder /app/dist/nowchess-frontend/browser /usr/share/nginx/html
|
||||
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
COPY --from=build /app/dist/nowchess-frontend/browser /usr/share/nginx/html
|
||||
COPY public/env.template.js /usr/share/nginx/html/env.template.js
|
||||
COPY docker-entrypoint.sh /docker-entrypoint.sh
|
||||
RUN chmod +x /docker-entrypoint.sh
|
||||
|
||||
EXPOSE 80
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||
|
||||
+15
-4
@@ -47,17 +47,25 @@
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "500kB",
|
||||
"maximumError": "4MB"
|
||||
"maximumWarning": "1MB",
|
||||
"maximumError": "5MB"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "1.5MB",
|
||||
"maximumError": "2MB"
|
||||
"maximumWarning": "1MB",
|
||||
"maximumError": "5MB"
|
||||
}
|
||||
],
|
||||
"outputHashing": "all"
|
||||
},
|
||||
"staging": {
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.staging.ts"
|
||||
}
|
||||
]
|
||||
},
|
||||
"development": {
|
||||
"fileReplacements": [
|
||||
{
|
||||
@@ -83,6 +91,9 @@
|
||||
"production": {
|
||||
"buildTarget": "nowchess-frontend:build:production"
|
||||
},
|
||||
"staging": {
|
||||
"buildTarget": "nowchess-frontend:build:staging"
|
||||
},
|
||||
"development": {
|
||||
"buildTarget": "nowchess-frontend:build:development"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
# Replace placeholders in env.template.js with environment variables and write env.js
|
||||
TEMPLATE_PATH="/usr/share/nginx/html/env.template.js"
|
||||
TARGET_PATH="/usr/share/nginx/html/env.js"
|
||||
|
||||
if [ -f "$TEMPLATE_PATH" ]; then
|
||||
echo "Rendering runtime config from $TEMPLATE_PATH"
|
||||
echo "Using environment variables:"
|
||||
printenv
|
||||
echo "----"
|
||||
envsubst < "$TEMPLATE_PATH" > "$TARGET_PATH"
|
||||
else
|
||||
echo "No runtime template found at $TEMPLATE_PATH, skipping"
|
||||
fi
|
||||
|
||||
exec nginx -g 'daemon off;'
|
||||
+18
-12
@@ -1,16 +1,22 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
location / {
|
||||
root /usr/share/nginx/html;
|
||||
index index.html index.htm;
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
location ~* \.(?:js|css|png|jpg|jpeg|gif|ico|svg|woff2?|ttf|eot)$ {
|
||||
try_files $uri =404;
|
||||
expires 1y;
|
||||
add_header Cache-Control "public";
|
||||
}
|
||||
location /env.js {
|
||||
root /usr/share/nginx/html;
|
||||
default_type application/javascript;
|
||||
expires -1;
|
||||
add_header 'Cache-Control' 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
|
||||
}
|
||||
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -4,7 +4,8 @@
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
"build": "ng build",
|
||||
"build": "ng build --configuration production",
|
||||
"build:staging": "ng build --configuration staging",
|
||||
"watch": "ng build --watch --configuration development",
|
||||
"test": "ng test"
|
||||
},
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
window.__RUNTIME_CONFIG__ = {
|
||||
API_URL: "${API_URL}",
|
||||
WEBSOCKET_URL: "${WEBSOCKET_URL}"
|
||||
};
|
||||
@@ -2,12 +2,10 @@ 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';
|
||||
import { ChallengesComponent } from './pages/challenges/challenges.component';
|
||||
|
||||
export const routes: Routes = [
|
||||
{ path: '', component: WelcomeComponent },
|
||||
{ path: 'profile', component: ProfileComponent },
|
||||
{ path: 'challenges', component: ChallengesComponent },
|
||||
{ path: 'game/:gameId', component: GameComponent },
|
||||
{ path: '**', redirectTo: '' }
|
||||
];
|
||||
|
||||
+1
-6
@@ -2,7 +2,6 @@ import { Component, OnInit } from '@angular/core';
|
||||
import { RouterOutlet } from '@angular/router';
|
||||
import { ToolbarComponent } from './components/toolbar/toolbar.component';
|
||||
import { ThemeService } from './services/theme.service';
|
||||
import { ChallengeWebSocketService } from './services/challenge-websocket.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
@@ -11,13 +10,9 @@ import { ChallengeWebSocketService } from './services/challenge-websocket.servic
|
||||
styleUrl: './app.css'
|
||||
})
|
||||
export class App implements OnInit {
|
||||
constructor(
|
||||
private readonly themeService: ThemeService,
|
||||
private readonly challengeWs: ChallengeWebSocketService
|
||||
) { }
|
||||
constructor(private readonly themeService: ThemeService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.themeService.initTheme();
|
||||
this.challengeWs.connect();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,148 +82,3 @@
|
||||
.ms-auto {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.notification-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.notification-badge {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
padding: 0;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
&.has-notifications {
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
.badge {
|
||||
position: absolute;
|
||||
top: -8px;
|
||||
right: -8px;
|
||||
background-color: #ff6b6b;
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
box-shadow: 0 0 10px rgba(255, 107, 107, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
filter: drop-shadow(0 0 3px rgba(0, 213, 255, 0.3));
|
||||
}
|
||||
50% {
|
||||
filter: drop-shadow(0 0 8px rgba(0, 213, 255, 0.6));
|
||||
}
|
||||
100% {
|
||||
filter: drop-shadow(0 0 3px rgba(0, 213, 255, 0.3));
|
||||
}
|
||||
}
|
||||
|
||||
.notification-menu {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
right: 0;
|
||||
background: linear-gradient(135deg, #0a0e27 0%, #1a1a3e 100%);
|
||||
border: 1px solid rgba(0, 213, 255, 0.3);
|
||||
border-radius: 8px;
|
||||
min-width: 250px;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
z-index: 1001;
|
||||
box-shadow: 0 0 20px rgba(0, 213, 255, 0.3);
|
||||
margin-top: 10px;
|
||||
|
||||
.notification-menu-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
border-bottom: 1px solid rgba(0, 213, 255, 0.2);
|
||||
color: #00d5ff;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
|
||||
.close-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: #00d5ff;
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.notification-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.notification-item {
|
||||
padding: 10px 12px;
|
||||
background-color: rgba(0, 213, 255, 0.05);
|
||||
border: 1px solid rgba(0, 213, 255, 0.1);
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
color: #b0b0d0;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(0, 213, 255, 0.1);
|
||||
border-color: rgba(0, 213, 255, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.notification-menu-footer {
|
||||
padding: 8px 8px 0;
|
||||
border-top: 1px solid rgba(0, 213, 255, 0.2);
|
||||
}
|
||||
|
||||
.view-all-btn {
|
||||
width: 100%;
|
||||
padding: 8px 12px;
|
||||
background-color: rgba(0, 213, 255, 0.1);
|
||||
border: 1px solid rgba(0, 213, 255, 0.3);
|
||||
border-radius: 4px;
|
||||
color: #00d5ff;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(0, 213, 255, 0.2);
|
||||
border-color: #00d5ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,37 +3,6 @@
|
||||
<span class="navbar-brand">NowChess</span>
|
||||
<div class="ms-auto">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<!-- Challenge Notification Badge -->
|
||||
<div class="notification-container">
|
||||
<button type="button" class="notification-badge" (click)="toggleNotificationMenu()"
|
||||
[class.has-notifications]="incomingChallenges.length > 0" title="View challenges">
|
||||
🔔
|
||||
<span *ngIf="incomingChallenges.length > 0" class="badge">
|
||||
{{ incomingChallenges.length }}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<!-- Notification Menu Dropdown -->
|
||||
@if (showNotificationMenu && incomingChallenges.length > 0) {
|
||||
<div class="notification-menu" (click)="$event.stopPropagation()">
|
||||
<div class="notification-menu-header">
|
||||
Challenges
|
||||
<button type="button" class="close-btn" (click)="closeNotificationMenu()">×</button>
|
||||
</div>
|
||||
<div class="notification-list">
|
||||
<div *ngFor="let challenge of incomingChallenges" class="notification-item">
|
||||
{{ challenge.challenger.name }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="notification-menu-footer">
|
||||
<button type="button" class="view-all-btn" (click)="goToChallenges()">
|
||||
View All Challenges →
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<button type="button" class="app-btn" (click)="toggleTheme()">
|
||||
{{ isDarkMode ? 'Light mode' : 'Dark mode' }}
|
||||
</button>
|
||||
@@ -42,9 +11,6 @@
|
||||
<button type="button" class="me-btn" (click)="goToProfile()">
|
||||
👤 {{ user.username }}
|
||||
</button>
|
||||
<button type="button" class="app-btn" (click)="goToChallenges()">
|
||||
Challenges
|
||||
</button>
|
||||
<button type="button" class="app-btn" (click)="logout()">Logout</button>
|
||||
</div>
|
||||
} @else {
|
||||
@@ -60,12 +26,6 @@
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Challenge Notification Popup -->
|
||||
@if (displayedChallenge) {
|
||||
<app-challenge-notification [challenge]="displayedChallenge" (accept)="onChallengeAccepted($event)"
|
||||
(decline)="onChallengeDeclined($event)" (close)="onNotificationClose()" />
|
||||
}
|
||||
|
||||
@if (showLoginDialog) {
|
||||
<app-login-dialog (onClose)="closeLoginDialog()" (onSuccess)="onLoginSuccess()" />
|
||||
}
|
||||
|
||||
@@ -8,14 +8,11 @@ import { CurrentUser } from '../../models/auth.models';
|
||||
import { LoginDialogComponent } from '../login-dialog/login-dialog.component';
|
||||
import { RegisterDialogComponent } from '../register-dialog/register-dialog.component';
|
||||
import { ThemeService } from '../../services/theme.service';
|
||||
import { ChallengeEventService } from '../../services/challenge-event.service';
|
||||
import { ChallengeNotificationComponent } from '../challenge-notification/challenge-notification.component';
|
||||
import { Challenge } from '../../models/challenge.models';
|
||||
|
||||
@Component({
|
||||
selector: 'app-toolbar',
|
||||
standalone: true,
|
||||
imports: [CommonModule, LoginDialogComponent, RegisterDialogComponent, ChallengeNotificationComponent],
|
||||
imports: [CommonModule, LoginDialogComponent, RegisterDialogComponent],
|
||||
templateUrl: './toolbar.component.html',
|
||||
styleUrl: './toolbar.component.css'
|
||||
})
|
||||
@@ -24,16 +21,12 @@ export class ToolbarComponent implements OnInit {
|
||||
private readonly authService = inject(AuthService);
|
||||
private readonly authDialogService = inject(AuthDialogService);
|
||||
private readonly themeService = inject(ThemeService);
|
||||
private readonly challengeEventService = inject(ChallengeEventService);
|
||||
private readonly router = inject(Router);
|
||||
|
||||
currentUser: CurrentUser | null = null;
|
||||
showLoginDialog = false;
|
||||
showRegisterDialog = false;
|
||||
isDarkMode = false;
|
||||
incomingChallenges: Challenge[] = [];
|
||||
showNotificationMenu = false;
|
||||
displayedChallenge: Challenge | null = null;
|
||||
|
||||
ngOnInit(): void {
|
||||
this.authService.currentUser$
|
||||
@@ -54,22 +47,6 @@ export class ToolbarComponent implements OnInit {
|
||||
.subscribe((isDarkMode) => {
|
||||
this.isDarkMode = isDarkMode;
|
||||
});
|
||||
|
||||
this.challengeEventService.getIncomingChallenges$()
|
||||
.pipe(takeUntilDestroyed(this.destroyRef))
|
||||
.subscribe((challenges) => {
|
||||
this.incomingChallenges = challenges;
|
||||
// Show the most recent challenge as notification
|
||||
if (challenges.length > 0) {
|
||||
this.displayedChallenge = challenges[0];
|
||||
}
|
||||
});
|
||||
|
||||
this.challengeEventService.getChallengeReceived$()
|
||||
.pipe(takeUntilDestroyed(this.destroyRef))
|
||||
.subscribe((challenge) => {
|
||||
this.displayedChallenge = challenge;
|
||||
});
|
||||
}
|
||||
|
||||
openLoginDialog(): void {
|
||||
@@ -107,34 +84,4 @@ export class ToolbarComponent implements OnInit {
|
||||
onRegisterSuccess(): void {
|
||||
this.closeRegisterDialog();
|
||||
}
|
||||
|
||||
toggleNotificationMenu(): void {
|
||||
this.showNotificationMenu = !this.showNotificationMenu;
|
||||
}
|
||||
|
||||
closeNotificationMenu(): void {
|
||||
this.showNotificationMenu = false;
|
||||
}
|
||||
|
||||
onChallengeAccepted(challenge: Challenge): void {
|
||||
this.challengeEventService.onChallengeAccepted(challenge);
|
||||
this.displayedChallenge = null;
|
||||
this.closeNotificationMenu();
|
||||
// Navigate to the game (once game creation is handled by backend)
|
||||
}
|
||||
|
||||
onChallengeDeclined(challenge: Challenge): void {
|
||||
this.challengeEventService.removeChallenge(challenge.id);
|
||||
this.displayedChallenge = null;
|
||||
this.closeNotificationMenu();
|
||||
}
|
||||
|
||||
onNotificationClose(): void {
|
||||
this.displayedChallenge = null;
|
||||
}
|
||||
|
||||
goToChallenges(): void {
|
||||
this.closeNotificationMenu();
|
||||
void this.router.navigate(['/challenges']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Load runtime configuration from window.__RUNTIME_CONFIG__
|
||||
* This is injected by docker-entrypoint.sh at container startup
|
||||
*/
|
||||
export function loadRuntimeConfig() {
|
||||
const config = (window as any).__RUNTIME_CONFIG__ || {};
|
||||
return {
|
||||
apiUrl: config.API_URL || '',
|
||||
wsUrl: config.WEBSOCKET_URL || 'ws://localhost:8080'
|
||||
};
|
||||
}
|
||||
@@ -46,6 +46,13 @@
|
||||
<div class="w" *ngFor="let win of windows['wA3']" [ngStyle]="win.style"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Draggable Meat Emoji -->
|
||||
@if (showMeatEmoji) {
|
||||
<div class="meat-emoji" [style.left.px]="meatX" [style.top.px]="meatY" (mousedown)="onMeatMouseDown($event)">
|
||||
🍖
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="bwrap" style="left:21%;width:15%;">
|
||||
@@ -94,8 +101,8 @@
|
||||
<div class="bb-tag">WELCOME</div>
|
||||
<div class="bb-title" style="font-size:clamp(16px,1.8vw,26px);">WELCOME TO<br />NOWCHESS</div>
|
||||
<div class="bb-subtitle">Play your next move from the skyline.</div>
|
||||
<button type="button" class="app-btn" (click)="openChallengeDialog()" [disabled]="creating">
|
||||
{{ creating ? 'CREATING...' : 'CREATE GAME →' }}
|
||||
<button type="button" class="app-btn" (click)="startOneVsOne()" [disabled]="creating">
|
||||
{{ creating ? 'CREATING...' : 'START NOW →' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -204,7 +211,6 @@
|
||||
<div class="dialog-card" (click)="$event.stopPropagation()">
|
||||
<div class="dialog-title">MORE OPTIONS</div>
|
||||
<div class="dialog-actions">
|
||||
<button type="button" class="app-btn" (click)="openChallengeDialog()">START GAME</button>
|
||||
<button type="button" class="app-btn" (click)="openImportDialog()">IMPORT GAME</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -257,10 +263,6 @@
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (showChallengeDialog) {
|
||||
<app-challenge-create-dialog (closeChallengeDialog)="closeChallengeDialog()"></app-challenge-create-dialog>
|
||||
}
|
||||
|
||||
@if (errorMessage) {
|
||||
<p class="error-banner">{{ errorMessage }}</p>
|
||||
}
|
||||
|
||||
@@ -30,6 +30,21 @@ interface WindowCell {
|
||||
style: Record<string, string>;
|
||||
}
|
||||
|
||||
interface Star {
|
||||
style: Record<string, string>;
|
||||
}
|
||||
|
||||
interface BackgroundBuilding {
|
||||
style: Record<string, string>;
|
||||
}
|
||||
|
||||
interface WindowCell {
|
||||
state: 'off' | 'on';
|
||||
color?: string;
|
||||
glowColor?: string;
|
||||
style: Record<string, string>;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-welcome',
|
||||
standalone: true,
|
||||
@@ -46,7 +61,6 @@ export class WelcomeComponent implements OnInit, OnDestroy {
|
||||
creating = false;
|
||||
joiningGame = false;
|
||||
importing = false;
|
||||
showChallengeDialog = false;
|
||||
errorMessage = '';
|
||||
|
||||
showDifficultyDialog = false;
|
||||
@@ -156,20 +170,6 @@ export class WelcomeComponent implements OnInit, OnDestroy {
|
||||
this.errorMessage = '';
|
||||
}
|
||||
|
||||
openChallengeDialog(): void {
|
||||
if (!this.requireAuth(() => this.showChallengeDialog = true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.closeAllDialogs();
|
||||
this.showChallengeDialog = true;
|
||||
}
|
||||
|
||||
closeChallengeDialog(): void {
|
||||
this.showChallengeDialog = false;
|
||||
this.errorMessage = '';
|
||||
}
|
||||
|
||||
openOptionsDialog(): void {
|
||||
this.closeAllDialogs();
|
||||
this.showOptionsDialog = true;
|
||||
@@ -455,7 +455,6 @@ export class WelcomeComponent implements OnInit, OnDestroy {
|
||||
this.showOptionsDialog = false;
|
||||
this.showJoinDialog = false;
|
||||
this.showImportDialog = false;
|
||||
this.showChallengeDialog = false;
|
||||
this.errorMessage = '';
|
||||
}
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@ import { HttpInterceptorFn } from '@angular/common/http';
|
||||
export const authInterceptor: HttpInterceptorFn = (req, next) => {
|
||||
const token = localStorage.getItem('token');
|
||||
|
||||
// Add token to protected endpoints only (not registration or login)
|
||||
const isProtectedEndpoint =
|
||||
req.url.includes('/api/account/me') ||
|
||||
req.url.includes('/api/account/bots') ||
|
||||
req.url.includes('/api/account/official-bots') ||
|
||||
req.url.includes('/api/board/game') ||
|
||||
req.url.includes('/api/challenge');
|
||||
|
||||
if (token && isProtectedEndpoint) {
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { loadRuntimeConfig } from '../app/core/config.loader';
|
||||
|
||||
const runtimeConfig = loadRuntimeConfig();
|
||||
|
||||
export const environment = {
|
||||
production: true,
|
||||
apiBaseUrl: runtimeConfig.apiUrl || 'https://st.nowchess.janis-eccarius.de',
|
||||
accountServiceUrl: runtimeConfig.apiUrl || 'https://st.nowchess.janis-eccarius.de',
|
||||
wsBaseUrl: runtimeConfig.wsUrl || 'wss://st.nowchess.janis-eccarius.de',
|
||||
apiPath: '/api/board/game'
|
||||
};
|
||||
@@ -1,7 +1,11 @@
|
||||
import { loadRuntimeConfig } from '../app/core/config.loader';
|
||||
|
||||
const runtimeConfig = loadRuntimeConfig();
|
||||
|
||||
export const environment = {
|
||||
production: true,
|
||||
apiBaseUrl: '',
|
||||
accountServiceUrl: '',
|
||||
wsBaseUrl: 'ws://localhost:8080',
|
||||
production: false,
|
||||
apiBaseUrl: runtimeConfig.apiUrl || '',
|
||||
accountServiceUrl: runtimeConfig.apiUrl || '',
|
||||
wsBaseUrl: runtimeConfig.wsUrl,
|
||||
apiPath: '/api/board/game'
|
||||
};
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
<base href="/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||
<script src="/env.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
MAJOR=0
|
||||
MINOR=1
|
||||
PATCH=0
|
||||
Reference in New Issue
Block a user