From 3391a8533dba442774d80768a20609a0934eca27 Mon Sep 17 00:00:00 2001 From: Marcin Bator Date: Wed, 9 Oct 2024 10:37:25 +0200 Subject: [PATCH 01/59] Dockerfile --- Dockerfile | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 1aaffa1..8129bef 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,7 @@ WORKDIR /project RUN npm install -g @angular/cli@18 -COPY "rag-2-frontend/package.json" "rag-2-frontend/package-lock.json" ./ +COPY "package.json" "package-lock.json" ./ RUN npm ci COPY . . @@ -24,6 +24,4 @@ groupadd docker usermod -aG docker vscode EOF -COPY "rag-2-frontend/" . - CMD ["ng", "serve", "--host", "0.0.0.0"] From 93632d666641f703803d7f59825ae681757128e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Buczek?= Date: Tue, 15 Oct 2024 14:38:54 +0200 Subject: [PATCH 02/59] feat: #63 console refactor in progress --- .../console-fieldset.component.ts | 40 +++++++++++++++---- .../components/console/console.component.ts | 2 +- src/app/game/game.page.component.ts | 3 +- 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/src/app/game/components/console/console-fieldset/console-fieldset.component.ts b/src/app/game/components/console/console-fieldset/console-fieldset.component.ts index 7db0469..a8b286f 100644 --- a/src/app/game/components/console/console-fieldset/console-fieldset.component.ts +++ b/src/app/game/components/console/console-fieldset/console-fieldset.component.ts @@ -8,17 +8,41 @@ import { TExchangeData } from '@gameModels/exchange-data.type'; standalone: true, imports: [KeyValuePipe, ExchangeDataPipe], template: ` + @for (data of logData | keyvalue; track data.key) { -
- {{ data.key }}: +
+ + {{ data.key }}: + + @if (isTLogData(data.value)) { - + } @else { - {{ - data.value - }} + + {{ data.value }} + }
} diff --git a/src/app/game/components/console/console.component.ts b/src/app/game/components/console/console.component.ts index 25d5578..b754bed 100644 --- a/src/app/game/components/console/console.component.ts +++ b/src/app/game/components/console/console.component.ts @@ -8,7 +8,7 @@ import { ConsoleFieldsetComponent } from './console-fieldset/console-fieldset.co standalone: true, imports: [ExchangeDataPipe, ConsoleFieldsetComponent], template: ` -
+
diff --git a/src/app/game/game.page.component.ts b/src/app/game/game.page.component.ts index a48ebc1..c222656 100644 --- a/src/app/game/game.page.component.ts +++ b/src/app/game/game.page.component.ts @@ -49,6 +49,7 @@ import { GameMenuComponent } from './components/game-menu/game-menu.component'; (pauseEmitter)="gamePauseSubject.next($event)" (restartEmitter)="gameRestartSubject.next()" />
@@ -67,8 +68,8 @@ import { GameMenuComponent } from './components/game-menu/game-menu.component'; }
} +
- `, }) export class GamePageComponent implements OnInit, OnDestroy { From fb75ca056cc5d2f6247ba0560f47d70149760b31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Buczek?= Date: Tue, 15 Oct 2024 23:23:22 +0200 Subject: [PATCH 03/59] refactor: #63 changed display in console fieldset --- .../console-fieldset.component.ts | 28 ++----------------- 1 file changed, 3 insertions(+), 25 deletions(-) diff --git a/src/app/game/components/console/console-fieldset/console-fieldset.component.ts b/src/app/game/components/console/console-fieldset/console-fieldset.component.ts index a8b286f..c9dad35 100644 --- a/src/app/game/components/console/console-fieldset/console-fieldset.component.ts +++ b/src/app/game/components/console/console-fieldset/console-fieldset.component.ts @@ -8,31 +8,9 @@ import { TExchangeData } from '@gameModels/exchange-data.type'; standalone: true, imports: [KeyValuePipe, ExchangeDataPipe], template: ` - @for (data of logData | keyvalue; track data.key) { -
- +
+ {{ data.key }}: @@ -40,7 +18,7 @@ import { TExchangeData } from '@gameModels/exchange-data.type'; } @else { - + {{ data.value }} } From 488ab60eadd83216f9d94bb1768ecc1f9908b749 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Buczek?= Date: Tue, 15 Oct 2024 23:40:54 +0200 Subject: [PATCH 04/59] feat: #63 added handling of too low resolution for game's display --- .../cant-display-game.component.spec.ts | 72 ++++++++++++++ .../cant-display-game.component.ts | 36 +++++++ src/app/game/game.page.component.ts | 94 +++++++++++-------- 3 files changed, 165 insertions(+), 37 deletions(-) create mode 100644 src/app/game/components/cant-display-game/cant-display-game.component.spec.ts create mode 100644 src/app/game/components/cant-display-game/cant-display-game.component.ts diff --git a/src/app/game/components/cant-display-game/cant-display-game.component.spec.ts b/src/app/game/components/cant-display-game/cant-display-game.component.spec.ts new file mode 100644 index 0000000..3f5d9e4 --- /dev/null +++ b/src/app/game/components/cant-display-game/cant-display-game.component.spec.ts @@ -0,0 +1,72 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { CantDisplayGameComponent } from './cant-display-game.component'; +import { NgOptimizedImage } from '@angular/common'; +import { By } from '@angular/platform-browser'; + +describe('Error404PageComponent', () => { + let component: CantDisplayGameComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [CantDisplayGameComponent, NgOptimizedImage], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(CantDisplayGameComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create the component', () => { + expect(component).toBeTruthy(); + }); + + it('should display the correct title text', () => { + const titleElement = fixture.debugElement.query(By.css('h1')); + expect(titleElement.nativeElement.textContent).toContain( + 'THE GAME CANNOT BE DISPLAYED' + ); + }); + + it('should display the correct subtitle text', () => { + const subtitleElement = fixture.debugElement.query(By.css('h2')); + expect(subtitleElement.nativeElement.textContent).toContain( + 'Unfortunately...' + ); + }); + + it('should display the correct body text', () => { + const bodyTextElement = fixture.debugElement.query(By.css('span')); + expect(bodyTextElement.nativeElement.textContent).toContain( + 'The screen resolution you are trying to view the game on is too low. It must be at least 1280 px.' + ); + }); + + it('should display the image with the correct attributes', () => { + const imgElement = fixture.debugElement.query(By.css('img')); + expect(imgElement.nativeElement.src).toContain('images/rag-2.png'); + expect(imgElement.nativeElement.alt).toBe('Logo'); + }); + + it('should have the correct classes applied to the main div', () => { + const mainDivElement = fixture.debugElement.query( + By.css('div.flex.flex-col.min-h-all') + ); + expect(mainDivElement.nativeElement.classList).toContain('bg-gray-400'); + expect(mainDivElement.nativeElement.classList).toContain('text-mainOrange'); + }); + + it('should have the correct classes applied to the container div', () => { + const containerDivElement = fixture.debugElement.query( + By.css('div.flex.flex-col.md\\:flex-row') + ); + expect(containerDivElement.nativeElement.classList).toContain( + 'border-mainOrange' + ); + expect(containerDivElement.nativeElement.classList).toContain( + 'text-mainCreme' + ); + }); +}); diff --git a/src/app/game/components/cant-display-game/cant-display-game.component.ts b/src/app/game/components/cant-display-game/cant-display-game.component.ts new file mode 100644 index 0000000..f868820 --- /dev/null +++ b/src/app/game/components/cant-display-game/cant-display-game.component.ts @@ -0,0 +1,36 @@ +import { NgOptimizedImage } from '@angular/common'; +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-cant-display-game', + standalone: true, + imports: [NgOptimizedImage], + template: ` +
+

+ THE GAME CANNOT BE DISPLAYED +

+
+
+ Logo +
+
+

Unfortunately...

+ The screen resolution you are trying to view the game on is too + low. It must be at least 1280 px. + +
+
+
+ `, +}) +export class CantDisplayGameComponent {} diff --git a/src/app/game/game.page.component.ts b/src/app/game/game.page.component.ts index c222656..5414c2b 100644 --- a/src/app/game/game.page.component.ts +++ b/src/app/game/game.page.component.ts @@ -13,6 +13,8 @@ import { PlayerSourceType } from 'app/shared/models/player-source-type.enum'; import { games } from './data/games'; import { ActivatedRoute, Router, NavigationStart } from '@angular/router'; import { GameMenuComponent } from './components/game-menu/game-menu.component'; +import { BreakpointObserver, BreakpointState } from '@angular/cdk/layout'; +import { CantDisplayGameComponent } from './components/cant-display-game/cant-display-game.component'; @Component({ selector: 'app-game', @@ -25,50 +27,55 @@ import { GameMenuComponent } from './components/game-menu/game-menu.component'; ConsoleComponent, AuthRequiredDirective, GameMenuComponent, + CantDisplayGameComponent, ], template: `
- @if (game) { -
-
- - @if (filterPlayersByActiveAndSocket(playersSelected).length > 0) { - +
+ + @if (filterPlayersByActiveAndSocket(playersSelected).length > 0) { + + } +
+
+ + - } -
-
- - + [setDataPossibleToPersist]="gameStateData" /> +
-
-
- @switch (game.name) { - @case ('pong') { - +
+ @switch (game.name) { + @case ('pong') { + + } } - } -
+
+ } + + } @else { + } -
`, }) @@ -76,6 +83,7 @@ export class GamePageComponent implements OnInit, OnDestroy { private _route = inject(ActivatedRoute); private _router = inject(Router); private _routerSubscription: Subscription | null = null; + private _breakpointSubscription: Subscription | null = null; private _previousUrl = ''; public gameName = ''; @@ -87,6 +95,15 @@ export class GamePageComponent implements OnInit, OnDestroy { public gameStateData: TExchangeData = {}; public gameRestartSubject = new Subject(); public gamePauseSubject = new Subject(); + public isMinWidthXl = false; + + public constructor(private _breakpointObserver: BreakpointObserver) { + this._breakpointSubscription = this._breakpointObserver + .observe(['(min-width: 1280px)']) + .subscribe((state: BreakpointState) => { + this.isMinWidthXl = state.matches; + }); + } public ngOnInit(): void { this._route.paramMap.subscribe(params => { @@ -112,6 +129,9 @@ export class GamePageComponent implements OnInit, OnDestroy { if (this._routerSubscription) { this._routerSubscription.unsubscribe(); } + if (this._breakpointSubscription) { + this._breakpointSubscription.unsubscribe(); + } } public updatePlayers(players: Player[]): void { From 9861e50520fd7d15ca395556e5decfc9c6aef9b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Buczek?= Date: Wed, 16 Oct 2024 16:23:03 +0200 Subject: [PATCH 05/59] feat: #40 initial shortcut created with handle of auth --- .../game-list}/game-list.component.spec.ts | 0 .../game-list}/game-list.component.ts | 0 .../user-shortcut.component.spec.ts | 23 ++++++++ .../user-shortcut/user-shortcut.component.ts | 55 +++++++++++++++++++ .../navbar/navbar.component.spec.ts | 2 +- .../components/navbar/navbar.component.ts | 16 ++++-- tailwind.config.js | 1 + 7 files changed, 91 insertions(+), 6 deletions(-) rename src/app/shared/components/navbar/{ => components/game-list}/game-list.component.spec.ts (100%) rename src/app/shared/components/navbar/{ => components/game-list}/game-list.component.ts (100%) create mode 100644 src/app/shared/components/navbar/components/user-shortcut/user-shortcut.component.spec.ts create mode 100644 src/app/shared/components/navbar/components/user-shortcut/user-shortcut.component.ts diff --git a/src/app/shared/components/navbar/game-list.component.spec.ts b/src/app/shared/components/navbar/components/game-list/game-list.component.spec.ts similarity index 100% rename from src/app/shared/components/navbar/game-list.component.spec.ts rename to src/app/shared/components/navbar/components/game-list/game-list.component.spec.ts diff --git a/src/app/shared/components/navbar/game-list.component.ts b/src/app/shared/components/navbar/components/game-list/game-list.component.ts similarity index 100% rename from src/app/shared/components/navbar/game-list.component.ts rename to src/app/shared/components/navbar/components/game-list/game-list.component.ts diff --git a/src/app/shared/components/navbar/components/user-shortcut/user-shortcut.component.spec.ts b/src/app/shared/components/navbar/components/user-shortcut/user-shortcut.component.spec.ts new file mode 100644 index 0000000..580b18b --- /dev/null +++ b/src/app/shared/components/navbar/components/user-shortcut/user-shortcut.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { UserShortcutComponent } from './user-shortcut.component'; + +describe('UserShortcutComponent', () => { + let component: UserShortcutComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [UserShortcutComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(UserShortcutComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/components/navbar/components/user-shortcut/user-shortcut.component.ts b/src/app/shared/components/navbar/components/user-shortcut/user-shortcut.component.ts new file mode 100644 index 0000000..e2a5e2b --- /dev/null +++ b/src/app/shared/components/navbar/components/user-shortcut/user-shortcut.component.ts @@ -0,0 +1,55 @@ +import { Component, inject, OnDestroy, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; +import { AuthenticationService } from 'app/shared/services/authentication.service'; +import { Subscription } from 'rxjs'; + +@Component({ + selector: 'app-user-shortcut', + standalone: true, + imports: [], + template: ` + + @if (isUserInfoVisible) { +
+ ADMIN +
+ DASHBOARD + LOGOUT +
+ } + `, +}) +export class UserShortcutComponent implements OnInit, OnDestroy { + private _authService = inject(AuthenticationService); + private _router = inject(Router); + + private _authSubscription: Subscription | null = null; + + public isLoggedIn = false; + public isUserInfoVisible = false; + + public ngOnInit(): void { + this._authSubscription = this._authService.authStatus$.subscribe( + isAuthenticated => { + this.isLoggedIn = isAuthenticated; + } + ); + } + + public handleButtonClick(): void { + if (this.isLoggedIn) { + this.isUserInfoVisible = !this.isUserInfoVisible; + } else { + this._router.navigate(['/login']); + } + } + + public ngOnDestroy(): void { + if (this._authSubscription) { + this._authSubscription.unsubscribe(); + } + } +} diff --git a/src/app/shared/components/navbar/navbar.component.spec.ts b/src/app/shared/components/navbar/navbar.component.spec.ts index 17cdb5b..49ec248 100644 --- a/src/app/shared/components/navbar/navbar.component.spec.ts +++ b/src/app/shared/components/navbar/navbar.component.spec.ts @@ -1,7 +1,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; import { NavbarComponent } from './navbar.component'; -import { GameListComponent } from './game-list.component'; +import { GameListComponent } from './components/game-list/game-list.component'; import * as feather from 'feather-icons'; describe('NavbarComponent', () => { diff --git a/src/app/shared/components/navbar/navbar.component.ts b/src/app/shared/components/navbar/navbar.component.ts index bde3c8c..c1fa50e 100644 --- a/src/app/shared/components/navbar/navbar.component.ts +++ b/src/app/shared/components/navbar/navbar.component.ts @@ -7,15 +7,23 @@ import { HostListener, } from '@angular/core'; import { Router, NavigationStart, RouterModule } from '@angular/router'; -import { GameListComponent } from './game-list.component'; +import { GameListComponent } from './components/game-list/game-list.component'; import { BreakpointObserver, BreakpointState } from '@angular/cdk/layout'; import * as feather from 'feather-icons'; import { Subscription } from 'rxjs'; +import { AuthRequiredDirective } from '@utils/directives/auth-required.directive'; +import { UserShortcutComponent } from './components/user-shortcut/user-shortcut.component'; @Component({ selector: 'app-navbar', standalone: true, - imports: [RouterModule, NgOptimizedImage, GameListComponent], + imports: [ + RouterModule, + NgOptimizedImage, + GameListComponent, + AuthRequiredDirective, + UserShortcutComponent, + ], template: `
- +
`, diff --git a/tailwind.config.js b/tailwind.config.js index f88f9ea..10ae48c 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -37,6 +37,7 @@ module.exports = { }, boxShadow: { navbarShadow: '0px 1px 7px 2px rgba(255, 96, 0, 1)', + userInfoShadow: '-1px 1px 7px 2px rgba(255, 96, 0, 1)', footerShadow: 'inset 0px 4px 25px -8px rgba(93, 93, 93, 1)', sideMenuShadow: '-5px 7px 25px -7px rgba(93, 93, 93, 1)', }, From 3c45dde574959f4d341804c0cb746d6b15f0fc87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Buczek?= Date: Wed, 16 Oct 2024 21:39:58 +0200 Subject: [PATCH 06/59] feat: #40 showing current user role in shortcut --- .../user-shortcut/user-shortcut.component.ts | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/app/shared/components/navbar/components/user-shortcut/user-shortcut.component.ts b/src/app/shared/components/navbar/components/user-shortcut/user-shortcut.component.ts index e2a5e2b..c6f93f7 100644 --- a/src/app/shared/components/navbar/components/user-shortcut/user-shortcut.component.ts +++ b/src/app/shared/components/navbar/components/user-shortcut/user-shortcut.component.ts @@ -1,5 +1,6 @@ import { Component, inject, OnDestroy, OnInit } from '@angular/core'; import { Router } from '@angular/router'; +import { TRole } from 'app/shared/models/role.enum'; import { AuthenticationService } from 'app/shared/services/authentication.service'; import { Subscription } from 'rxjs'; @@ -13,10 +14,13 @@ import { Subscription } from 'rxjs'; @if (isUserInfoVisible) {
- ADMIN -
+ class="flex flex-col space-y-1 text-base w-56 absolute -z-10 top-[82px] right-0 bg-mainGray shadow-userInfoShadow p-3 rounded-bl-lg"> + Your role: {{ currentUserRole }} +
DASHBOARD +
LOGOUT
} @@ -27,8 +31,10 @@ export class UserShortcutComponent implements OnInit, OnDestroy { private _router = inject(Router); private _authSubscription: Subscription | null = null; + private _roleSubscription: Subscription | null = null; public isLoggedIn = false; + public currentUserRole: TRole | null = null; public isUserInfoVisible = false; public ngOnInit(): void { @@ -37,6 +43,10 @@ export class UserShortcutComponent implements OnInit, OnDestroy { this.isLoggedIn = isAuthenticated; } ); + + this._roleSubscription = this._authService.currentRole$.subscribe(role => { + this.currentUserRole = role; + }); } public handleButtonClick(): void { @@ -51,5 +61,8 @@ export class UserShortcutComponent implements OnInit, OnDestroy { if (this._authSubscription) { this._authSubscription.unsubscribe(); } + if (this._roleSubscription) { + this._roleSubscription.unsubscribe(); + } } } From 68879d103fc645cf379281dcc2a5e5a161ec09e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Buczek?= Date: Wed, 16 Oct 2024 22:09:04 +0200 Subject: [PATCH 07/59] feat: #40 handle logout button + styles in menu --- .../user-shortcut/user-shortcut.component.ts | 63 +++++++++++++++---- 1 file changed, 50 insertions(+), 13 deletions(-) diff --git a/src/app/shared/components/navbar/components/user-shortcut/user-shortcut.component.ts b/src/app/shared/components/navbar/components/user-shortcut/user-shortcut.component.ts index c6f93f7..83dda82 100644 --- a/src/app/shared/components/navbar/components/user-shortcut/user-shortcut.component.ts +++ b/src/app/shared/components/navbar/components/user-shortcut/user-shortcut.component.ts @@ -1,7 +1,9 @@ import { Component, inject, OnDestroy, OnInit } from '@angular/core'; import { Router } from '@angular/router'; +import { UserEndpointsService } from '@endpoints/user-endpoints.service'; import { TRole } from 'app/shared/models/role.enum'; import { AuthenticationService } from 'app/shared/services/authentication.service'; +import { NotificationService } from 'app/shared/services/notification.service'; import { Subscription } from 'rxjs'; @Component({ @@ -10,28 +12,42 @@ import { Subscription } from 'rxjs'; imports: [], template: ` - @if (isUserInfoVisible) { -
- Your role: {{ currentUserRole }} -
- DASHBOARD -
- LOGOUT -
- } +
+ Your role: {{ currentUserRole }} +
+ +
+ +
`, }) export class UserShortcutComponent implements OnInit, OnDestroy { private _authService = inject(AuthenticationService); private _router = inject(Router); + private _userEndpointsService = inject(UserEndpointsService); + private _notificationService = inject(NotificationService); private _authSubscription: Subscription | null = null; private _roleSubscription: Subscription | null = null; + private _logoutSubscription: Subscription | null = null; public isLoggedIn = false; public currentUserRole: TRole | null = null; @@ -57,6 +73,24 @@ export class UserShortcutComponent implements OnInit, OnDestroy { } } + public logoutButtonClick(): void { + if (this.isLoggedIn && this.currentUserRole !== null) { + this._logoutSubscription = this._userEndpointsService.logout().subscribe({ + next: () => { + this._router.navigate(['/']); + this._notificationService.addNotification( + "You've been logged out successfully!", + 3000 + ); + this.isLoggedIn = false; + this.currentUserRole = null; + this.isUserInfoVisible = false; + this._authService.setAuthStatus(false); + }, + }); + } + } + public ngOnDestroy(): void { if (this._authSubscription) { this._authSubscription.unsubscribe(); @@ -64,5 +98,8 @@ export class UserShortcutComponent implements OnInit, OnDestroy { if (this._roleSubscription) { this._roleSubscription.unsubscribe(); } + if (this._logoutSubscription) { + this._logoutSubscription.unsubscribe(); + } } } From 983642f2e4e225252010cd508279b2390880421c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Buczek?= Date: Wed, 16 Oct 2024 22:24:33 +0200 Subject: [PATCH 08/59] feat: #40 added rwd to menu and link to dashboard --- .../user-shortcut/user-shortcut.component.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/app/shared/components/navbar/components/user-shortcut/user-shortcut.component.ts b/src/app/shared/components/navbar/components/user-shortcut/user-shortcut.component.ts index 83dda82..2ba7de1 100644 --- a/src/app/shared/components/navbar/components/user-shortcut/user-shortcut.component.ts +++ b/src/app/shared/components/navbar/components/user-shortcut/user-shortcut.component.ts @@ -1,5 +1,5 @@ import { Component, inject, OnDestroy, OnInit } from '@angular/core'; -import { Router } from '@angular/router'; +import { Router, RouterModule } from '@angular/router'; import { UserEndpointsService } from '@endpoints/user-endpoints.service'; import { TRole } from 'app/shared/models/role.enum'; import { AuthenticationService } from 'app/shared/services/authentication.service'; @@ -9,7 +9,7 @@ import { Subscription } from 'rxjs'; @Component({ selector: 'app-user-shortcut', standalone: true, - imports: [], + imports: [RouterModule], template: `
+ }} top-[75px] 2xs:top-[83px] bg-mainGray shadow-userInfoShadow p-3 rounded-bl-lg"> Your role: {{ currentUserRole }}
- + +
`, From fec4ef3a02fbe16edd3a3e76e0ae0d2de1f824a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Buczek?= Date: Wed, 16 Oct 2024 22:53:04 +0200 Subject: [PATCH 09/59] feat: #40 added auth and guest guards --- src/app/app.routes.ts | 11 +++++++++ .../dashboard.page.component.spec.ts | 23 +++++++++++++++++++ src/app/dashboard/dashboard.page.component.ts | 9 ++++++++ src/utils/helpers/auth.guard.ts | 19 +++++++++++++++ src/utils/helpers/guest.guard.ts | 19 +++++++++++++++ 5 files changed, 81 insertions(+) create mode 100644 src/app/dashboard/dashboard.page.component.spec.ts create mode 100644 src/app/dashboard/dashboard.page.component.ts create mode 100644 src/utils/helpers/auth.guard.ts create mode 100644 src/utils/helpers/guest.guard.ts diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index fb88494..5936db5 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -7,6 +7,9 @@ import { Error404PageComponent } from './shared/components/error-pages/error404. import { Error500PageComponent } from './shared/components/error-pages/error500.page.component'; import { RegisterConfirmComponent } from './user-workflow/register/components/register-confirm.component'; import { ResetPasswordComponent } from './user-workflow/login/components/reset-password.component'; +import { DashboardPageComponent } from './dashboard/dashboard.page.component'; +import { authGuard } from '@utils/helpers/auth.guard'; +import { guestGuard } from '@utils/helpers/guest.guard'; export const routes: Routes = [ { @@ -19,6 +22,12 @@ export const routes: Routes = [ redirectTo: '', pathMatch: 'full', }, + { + path: 'dashboard', + component: DashboardPageComponent, + title: 'Dashboard Page', + canActivate: [authGuard], + }, { path: 'game/:gameName', component: GamePageComponent, @@ -26,6 +35,7 @@ export const routes: Routes = [ }, { path: 'login', + canActivate: [guestGuard], children: [ { path: '', @@ -41,6 +51,7 @@ export const routes: Routes = [ }, { path: 'register', + canActivate: [guestGuard], children: [ { path: '', diff --git a/src/app/dashboard/dashboard.page.component.spec.ts b/src/app/dashboard/dashboard.page.component.spec.ts new file mode 100644 index 0000000..939efbc --- /dev/null +++ b/src/app/dashboard/dashboard.page.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DashboardPageComponent } from './dashboard.page.component'; + +describe('DashboardPageComponent', () => { + let component: DashboardPageComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [DashboardPageComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(DashboardPageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/dashboard/dashboard.page.component.ts b/src/app/dashboard/dashboard.page.component.ts new file mode 100644 index 0000000..c8b5be5 --- /dev/null +++ b/src/app/dashboard/dashboard.page.component.ts @@ -0,0 +1,9 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-dashboard-page', + standalone: true, + imports: [], + template: `

dashboard-page works!

`, +}) +export class DashboardPageComponent {} diff --git a/src/utils/helpers/auth.guard.ts b/src/utils/helpers/auth.guard.ts new file mode 100644 index 0000000..0dd98b1 --- /dev/null +++ b/src/utils/helpers/auth.guard.ts @@ -0,0 +1,19 @@ +import { inject } from '@angular/core'; +import { CanActivateFn, Router } from '@angular/router'; +import { AuthenticationService } from 'app/shared/services/authentication.service'; +import { map, take } from 'rxjs/operators'; + +export const authGuard: CanActivateFn = () => { + const authService = inject(AuthenticationService); + const router = inject(Router); + + return authService.authStatus$.pipe( + take(1), // funkcja bierze tylko 1 wartość i się kończy + map(isAuthenticated => { + if (isAuthenticated) { + return true; + } + return router.parseUrl('/'); + }) + ); +}; diff --git a/src/utils/helpers/guest.guard.ts b/src/utils/helpers/guest.guard.ts new file mode 100644 index 0000000..7b5454d --- /dev/null +++ b/src/utils/helpers/guest.guard.ts @@ -0,0 +1,19 @@ +import { inject } from '@angular/core'; +import { CanActivateFn, Router } from '@angular/router'; +import { AuthenticationService } from 'app/shared/services/authentication.service'; +import { map, take } from 'rxjs/operators'; + +export const guestGuard: CanActivateFn = () => { + const authService = inject(AuthenticationService); + const router = inject(Router); + + return authService.authStatus$.pipe( + take(1), // funkcja bierze tylko 1 wartość i się kończy + map(isAuthenticated => { + if (!isAuthenticated) { + return true; + } + return router.parseUrl('/'); + }) + ); +}; From f15ad529101cc591571b4e30e1dee3e18334e752 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Buczek?= Date: Wed, 16 Oct 2024 23:34:17 +0200 Subject: [PATCH 10/59] feat: #40 initial dashboard page --- src/app/dashboard/dashboard.page.component.ts | 73 ++++++++++++++++++- .../user-shortcut/user-shortcut.component.ts | 5 +- 2 files changed, 72 insertions(+), 6 deletions(-) diff --git a/src/app/dashboard/dashboard.page.component.ts b/src/app/dashboard/dashboard.page.component.ts index c8b5be5..44bbe18 100644 --- a/src/app/dashboard/dashboard.page.component.ts +++ b/src/app/dashboard/dashboard.page.component.ts @@ -1,9 +1,74 @@ -import { Component } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { Component, inject, OnDestroy, OnInit } from '@angular/core'; +import { UserEndpointsService } from '@endpoints/user-endpoints.service'; +import { IUserResponse } from 'app/shared/models/user.models'; +import { Subscription } from 'rxjs'; @Component({ selector: 'app-dashboard-page', standalone: true, - imports: [], - template: `

dashboard-page works!

`, + imports: [CommonModule], + template: `
+
+

+ Hello, {{ aboutMeUserInfo?.name }}! +

+
+
+

+ Your email address: + + {{ aboutMeUserInfo?.email }} + +

+

+ Logged in as: + + {{ aboutMeUserInfo?.role | uppercase }} + +

+

+ Your study cycle years: + + {{ aboutMeUserInfo?.studyCycleYearA }} / + {{ aboutMeUserInfo?.studyCycleYearB }} + +

+
+
+ +
+

User account settings

+
+
+ zmien haslo + usun konto +
+
+
`, }) -export class DashboardPageComponent {} +export class DashboardPageComponent implements OnInit, OnDestroy { + private _userEndpointsService = inject(UserEndpointsService); + + private _getMeSubscription: Subscription | null = null; + + public aboutMeUserInfo: IUserResponse | null = null; + + public ngOnInit(): void { + this._getMeSubscription = this._userEndpointsService.getMe().subscribe({ + next: (response: IUserResponse) => { + this.aboutMeUserInfo = response; + }, + error: () => { + this.aboutMeUserInfo = null; + }, + }); + } + + public ngOnDestroy(): void { + if (this._getMeSubscription) { + this._getMeSubscription.unsubscribe(); + } + } +} diff --git a/src/app/shared/components/navbar/components/user-shortcut/user-shortcut.component.ts b/src/app/shared/components/navbar/components/user-shortcut/user-shortcut.component.ts index 2ba7de1..aacc32b 100644 --- a/src/app/shared/components/navbar/components/user-shortcut/user-shortcut.component.ts +++ b/src/app/shared/components/navbar/components/user-shortcut/user-shortcut.component.ts @@ -1,3 +1,4 @@ +import { CommonModule } from '@angular/common'; import { Component, inject, OnDestroy, OnInit } from '@angular/core'; import { Router, RouterModule } from '@angular/router'; import { UserEndpointsService } from '@endpoints/user-endpoints.service'; @@ -9,7 +10,7 @@ import { Subscription } from 'rxjs'; @Component({ selector: 'app-user-shortcut', standalone: true, - imports: [RouterModule], + imports: [RouterModule, CommonModule], template: ` + `, }) -export class DashboardPageComponent implements OnInit, OnDestroy { +export class DashboardPageComponent + implements OnInit, OnDestroy, AfterViewInit +{ private _userEndpointsService = inject(UserEndpointsService); private _statsEndpointsService = inject(StatsEndpointsService); @@ -81,6 +76,10 @@ export class DashboardPageComponent implements OnInit, OnDestroy { }); } + public ngAfterViewInit(): void { + feather.replace(); + } + public getUserStats(userId: number): void { this._getUserStats = this._statsEndpointsService .getUserStats(userId) diff --git a/src/app/shared/components/navbar/navbar.component.ts b/src/app/shared/components/navbar/navbar.component.ts index c1fa50e..445af65 100644 --- a/src/app/shared/components/navbar/navbar.component.ts +++ b/src/app/shared/components/navbar/navbar.component.ts @@ -102,6 +102,7 @@ export class NavbarComponent implements AfterViewInit, OnInit, OnDestroy { public ngAfterViewInit(): void { feather.replace(); } + public toggleGameList(): void { this.isGameListActive = !this.isGameListActive; } From 1a5f6f7ecf20618893cf54517071ad9debe43a03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Buczek?= Date: Sat, 19 Oct 2024 21:53:23 +0200 Subject: [PATCH 17/59] feat: #40 created initial modal for buttons --- .../components/modal.component.spec.ts | 23 ++++++++++ .../dashboard/components/modal.component.ts | 19 ++++++++ src/app/dashboard/dashboard.page.component.ts | 44 ++++++++++++++++++- 3 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 src/app/dashboard/components/modal.component.spec.ts create mode 100644 src/app/dashboard/components/modal.component.ts diff --git a/src/app/dashboard/components/modal.component.spec.ts b/src/app/dashboard/components/modal.component.spec.ts new file mode 100644 index 0000000..f9bab0f --- /dev/null +++ b/src/app/dashboard/components/modal.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ModalComponent } from './modal.component'; + +describe('ModalComponent', () => { + let component: ModalComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ModalComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ModalComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/dashboard/components/modal.component.ts b/src/app/dashboard/components/modal.component.ts new file mode 100644 index 0000000..4699bfd --- /dev/null +++ b/src/app/dashboard/components/modal.component.ts @@ -0,0 +1,19 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-modal', + standalone: true, + imports: [], + template: ` +
+ +
+ `, +}) +export class ModalComponent {} diff --git a/src/app/dashboard/dashboard.page.component.ts b/src/app/dashboard/dashboard.page.component.ts index 07c8a00..9af498a 100644 --- a/src/app/dashboard/dashboard.page.component.ts +++ b/src/app/dashboard/dashboard.page.component.ts @@ -16,11 +16,17 @@ import { Subscription } from 'rxjs'; import { ProgressCircleBarComponent } from './components/progress-circle-bar.component'; import { StatsEndpointsService } from '@endpoints/stats-endpoints.service'; import { UserInfoComponent } from './components/user-info.component'; +import { ModalComponent } from './components/modal.component'; @Component({ selector: 'app-dashboard-page', standalone: true, - imports: [CommonModule, ProgressCircleBarComponent, UserInfoComponent], + imports: [ + CommonModule, + ProgressCircleBarComponent, + UserInfoComponent, + ModalComponent, + ], template: `
+ +
+ + +
+

Game handling options

+
+
+ + +
`, From a81d54f9f9d23cef658df70f608a6931f47efa59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Buczek?= Date: Sat, 19 Oct 2024 23:45:20 +0200 Subject: [PATCH 18/59] feat: #40 added handling of changing password and deleting account + other sections buttons --- .../dashboard/components/modal.component.ts | 27 ++- .../user-account-settings.component.spec.ts | 23 ++ .../user-account-settings.component.ts | 221 ++++++++++++++++++ .../user-info.component.spec.ts | 0 .../{ => sections}/user-info.component.ts | 2 +- src/app/dashboard/dashboard.page.component.ts | 51 ++-- 6 files changed, 295 insertions(+), 29 deletions(-) create mode 100644 src/app/dashboard/components/sections/user-account-settings.component.spec.ts create mode 100644 src/app/dashboard/components/sections/user-account-settings.component.ts rename src/app/dashboard/components/{ => sections}/user-info.component.spec.ts (100%) rename src/app/dashboard/components/{ => sections}/user-info.component.ts (97%) diff --git a/src/app/dashboard/components/modal.component.ts b/src/app/dashboard/components/modal.component.ts index 4699bfd..a809881 100644 --- a/src/app/dashboard/components/modal.component.ts +++ b/src/app/dashboard/components/modal.component.ts @@ -1,19 +1,26 @@ -import { Component } from '@angular/core'; +import { Component, Output, EventEmitter } from '@angular/core'; @Component({ selector: 'app-modal', standalone: true, - imports: [], template: ` -
-
+
-
+ + `, }) -export class ModalComponent {} +export class ModalComponent { + @Output() public closeModal = new EventEmitter(); + + public onBackdropClick(): void { + this.closeModal.emit(); + } +} diff --git a/src/app/dashboard/components/sections/user-account-settings.component.spec.ts b/src/app/dashboard/components/sections/user-account-settings.component.spec.ts new file mode 100644 index 0000000..fd8efd4 --- /dev/null +++ b/src/app/dashboard/components/sections/user-account-settings.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { UserAccountSettingsComponent } from './user-account-settings.component'; + +describe('UserAccountSettingsComponent', () => { + let component: UserAccountSettingsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [UserAccountSettingsComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(UserAccountSettingsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/dashboard/components/sections/user-account-settings.component.ts b/src/app/dashboard/components/sections/user-account-settings.component.ts new file mode 100644 index 0000000..07a7294 --- /dev/null +++ b/src/app/dashboard/components/sections/user-account-settings.component.ts @@ -0,0 +1,221 @@ +/* eslint-disable max-lines */ +import { Component, inject, OnDestroy } from '@angular/core'; +import { ModalComponent } from '../modal.component'; +import { + NonNullableFormBuilder, + ReactiveFormsModule, + Validators, +} from '@angular/forms'; +import { FormValidationService } from 'app/shared/services/form-validation.service'; +import { Subscription } from 'rxjs'; +import { UserEndpointsService } from '@endpoints/user-endpoints.service'; +import { NotificationService } from 'app/shared/services/notification.service'; + +@Component({ + selector: 'app-user-account-settings', + standalone: true, + imports: [ModalComponent, ReactiveFormsModule], + template: ` +

User account settings

+
+
+ + +
+ @if (modalVisibility !== null) { + +
+

+ {{ modalTitle }} +

+ @if (modalVisibility === 'changePassword') { +
+
+ + +
+
+ + +
+
+ } @else if (modalVisibility === 'deleteAccount') { +

+ You will lose all your data, progress, and saved games and will + not be able to undo it. +

+

+ Are you sure about this action? It can't be undone later! +

+ } + + + @if ( + (changePasswordForm.invalid && + (changePasswordForm.dirty || changePasswordForm.touched)) || + errorMessage !== null + ) { +
+ @for (error of getFormErrors(); track error) { + @if (modalVisibility === 'changePassword') { +

{{ error }}

+ } + } + @if (errorMessage !== null) { +

{{ errorMessage }}

+ } +
+ } +
+
+ } + `, +}) +export class UserAccountSettingsComponent implements OnDestroy { + private _formBuilder = inject(NonNullableFormBuilder); + private _formValidationService = inject(FormValidationService); + private _userEndpointsService = inject(UserEndpointsService); + private _notificationService = inject(NotificationService); + + private _changePasswordSubscribtion: Subscription | null = null; + private _deleteAccountSubscribtion: Subscription | null = null; + + public changePasswordForm = this._formBuilder.group({ + oldPassword: ['', [Validators.required, Validators.minLength(8)]], + newPassword: ['', [Validators.required, Validators.minLength(8)]], + }); + + public errorMessage: string | null = null; + + public modalVisibility: 'changePassword' | 'deleteAccount' | null = null; + public modalTitle = ''; + public modalButtonText = ''; + public modalButtonFunction!: () => void; + + public shouldShowError(controlName: string): boolean | undefined { + return this._formValidationService.shouldShowError( + this.changePasswordForm, + controlName + ); + } + + public getFormErrors(): string[] { + return this._formValidationService.getFormErrors(this.changePasswordForm); + } + + public changePasswordModal(): void { + this.modalVisibility = 'changePassword'; + this.modalTitle = 'Changing your password'; + this.modalButtonText = 'Change password'; + this.modalButtonFunction = this.changePasswordFunction; + this.errorMessage = null; + } + + public deleteAccountModal(): void { + this.modalVisibility = 'deleteAccount'; + this.modalTitle = 'Deleting your account and all data'; + this.modalButtonText = 'Delete account and all data'; + this.modalButtonFunction = this.deleteAccountFunction; + this.errorMessage = null; + } + + public hideModal(): void { + this.modalVisibility = null; + } + + public changePasswordFunction(): void { + this.errorMessage = null; + if (this.changePasswordForm.valid) { + const formValues = this.changePasswordForm.value; + if (formValues.oldPassword && formValues.newPassword) { + this._changePasswordSubscribtion = this._userEndpointsService + .changePassword(formValues.oldPassword, formValues.newPassword) + .subscribe({ + next: () => { + this._notificationService.addNotification( + 'Your password has been changed!' + ); + this.errorMessage = null; + this.modalVisibility = null; + }, + error: (error: string) => { + this.errorMessage = error; + }, + }); + } + } + } + + public deleteAccountFunction(): void { + this._deleteAccountSubscribtion = this._userEndpointsService + .deleteAccount() + .subscribe({ + next: () => { + this._notificationService.addNotification( + 'Your account has been permamently deleted!' + ); + this.errorMessage = null; + this.modalVisibility = null; + }, + error: (error: string) => { + this.errorMessage = error; + }, + }); + } + + public ngOnDestroy(): void { + if (this._changePasswordSubscribtion) { + this._changePasswordSubscribtion.unsubscribe(); + } + + if (this._deleteAccountSubscribtion) { + this._deleteAccountSubscribtion.unsubscribe(); + } + } +} diff --git a/src/app/dashboard/components/user-info.component.spec.ts b/src/app/dashboard/components/sections/user-info.component.spec.ts similarity index 100% rename from src/app/dashboard/components/user-info.component.spec.ts rename to src/app/dashboard/components/sections/user-info.component.spec.ts diff --git a/src/app/dashboard/components/user-info.component.ts b/src/app/dashboard/components/sections/user-info.component.ts similarity index 97% rename from src/app/dashboard/components/user-info.component.ts rename to src/app/dashboard/components/sections/user-info.component.ts index c6ba848..3f9fd3a 100644 --- a/src/app/dashboard/components/user-info.component.ts +++ b/src/app/dashboard/components/sections/user-info.component.ts @@ -4,7 +4,7 @@ import { IUserResponse, IUserStatsResponse, } from 'app/shared/models/user.models'; -import { ProgressCircleBarComponent } from './progress-circle-bar.component'; +import { ProgressCircleBarComponent } from '../progress-circle-bar.component'; @Component({ selector: 'app-user-info', diff --git a/src/app/dashboard/dashboard.page.component.ts b/src/app/dashboard/dashboard.page.component.ts index 9af498a..484206a 100644 --- a/src/app/dashboard/dashboard.page.component.ts +++ b/src/app/dashboard/dashboard.page.component.ts @@ -15,8 +15,8 @@ import { import { Subscription } from 'rxjs'; import { ProgressCircleBarComponent } from './components/progress-circle-bar.component'; import { StatsEndpointsService } from '@endpoints/stats-endpoints.service'; -import { UserInfoComponent } from './components/user-info.component'; -import { ModalComponent } from './components/modal.component'; +import { UserInfoComponent } from './components/sections/user-info.component'; +import { UserAccountSettingsComponent } from './components/sections/user-account-settings.component'; @Component({ selector: 'app-dashboard-page', @@ -25,23 +25,33 @@ import { ModalComponent } from './components/modal.component'; CommonModule, ProgressCircleBarComponent, UserInfoComponent, - ModalComponent, + UserAccountSettingsComponent, ], template: `
+ class="flex flex-col space-y-16 font-mono w-full bg-mainGray pt-6 pb-12 xl:pt-14"> + +
-

User account settings

+

Game handling options


+ -
-

Game handling options

+

+ Administration settings +


+
From 7120239d135212e4ff6714bb344e0613a044b37c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Buczek?= Date: Sun, 20 Oct 2024 18:11:57 +0200 Subject: [PATCH 19/59] feat: #40 added new games and editing existing --- .../game-handling-options.component.spec.ts | 23 ++ .../game-handling-options.component.ts | 265 ++++++++++++++++++ .../user-account-settings.component.ts | 2 +- .../sections/user-info.component.ts | 2 +- src/app/dashboard/dashboard.page.component.ts | 38 +-- 5 files changed, 294 insertions(+), 36 deletions(-) create mode 100644 src/app/dashboard/components/sections/game-handling-options.component.spec.ts create mode 100644 src/app/dashboard/components/sections/game-handling-options.component.ts diff --git a/src/app/dashboard/components/sections/game-handling-options.component.spec.ts b/src/app/dashboard/components/sections/game-handling-options.component.spec.ts new file mode 100644 index 0000000..5fbc88f --- /dev/null +++ b/src/app/dashboard/components/sections/game-handling-options.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { GameHandlingOptionsComponent } from './game-handling-options.component'; + +describe('GameHandlingOptionsComponent', () => { + let component: GameHandlingOptionsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [GameHandlingOptionsComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(GameHandlingOptionsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/dashboard/components/sections/game-handling-options.component.ts b/src/app/dashboard/components/sections/game-handling-options.component.ts new file mode 100644 index 0000000..b6354b7 --- /dev/null +++ b/src/app/dashboard/components/sections/game-handling-options.component.ts @@ -0,0 +1,265 @@ +/* eslint-disable max-lines */ +import { Component, inject, OnDestroy, OnInit } from '@angular/core'; +import { ModalComponent } from '../modal.component'; +import { GameEndpointsService } from '@endpoints/game-endpoints.service'; +import { NotificationService } from 'app/shared/services/notification.service'; +import { Subscription } from 'rxjs'; +import { IGameRequest, IGameResponse } from 'app/shared/models/game.models'; +import { + NonNullableFormBuilder, + ReactiveFormsModule, + Validators, +} from '@angular/forms'; + +@Component({ + selector: 'app-game-handling-options', + standalone: true, + imports: [ModalComponent, ReactiveFormsModule], + template: ` +

Game handling options

+
+
+ + + +
+ @if (modalVisibility !== null) { + +
+

+ {{ modalTitle }} +

+
+ @if (modalVisibility === 'addNewGame') { +
+ + +
+ } @else if ( + (modalVisibility === 'editGame' || + modalVisibility === 'removeGame') && + gameList !== null + ) { + + } + @if (modalVisibility === 'editGame') { +
+ + +
+ } +
+ + +
+ @if (errorMessage !== null) { +

{{ errorMessage }}

+ } +
+
+
+ } + `, +}) +export class GameHandlingOptionsComponent implements OnDestroy { + private _gameEndpointsService = inject(GameEndpointsService); + private _notificationService = inject(NotificationService); + private _formBuilder = inject(NonNullableFormBuilder); + + private _getGamesSubscription: Subscription | null = null; + private _addGameSubscription: Subscription | null = null; + private _editGameSubscription: Subscription | null = null; + private _removeGameSubscription: Subscription | null = null; + + public selectedGameId = 0; + + public gameHandlingForm = this._formBuilder.group({ + newGameName: ['', [Validators.required]], + editedGameName: ['', [Validators.required]], + }); + + public gameList: IGameResponse[] | null = null; + + public errorMessage: string | null = null; + + public modalVisibility: 'addNewGame' | 'editGame' | 'removeGame' | null = + null; + public modalTitle = ''; + public modalButtonText = ''; + public modalButtonFunction!: () => void; + + public setSelectedGameId(event: Event): void { + const target = event.target as HTMLSelectElement; + const selectedId = target?.value; + this.selectedGameId = parseInt(selectedId, 10); + } + + public getGameList(): void { + this._getGamesSubscription = this._gameEndpointsService + .getGames() + .subscribe({ + next: (response: IGameResponse[]) => { + this.gameList = response; + }, + }); + } + + public addNewGameModal(): void { + this.modalVisibility = 'addNewGame'; + this.modalTitle = 'Adding new game'; + this.modalButtonText = 'Add new game'; + this.modalButtonFunction = this.addNewGameFunction; + this.errorMessage = null; + } + + public editGameModal(): void { + this.modalVisibility = 'editGame'; + this.modalTitle = 'Editing existing game'; + this.modalButtonText = 'Edit game'; + this.modalButtonFunction = this.editGameFunction; + this.errorMessage = null; + this.getGameList(); + } + + public removeGameModal(): void { + this.modalVisibility = 'removeGame'; + this.modalTitle = 'Removing existing game'; + this.modalButtonText = 'Remove game'; + this.modalButtonFunction = this.removeGameFunction; + this.errorMessage = null; + this.getGameList(); + } + + public hideModal(): void { + this.modalVisibility = null; + } + + public addNewGameFunction(): void { + this.errorMessage = null; + if (this.gameHandlingForm.value.newGameName) { + const formValues = this.gameHandlingForm.value; + if (formValues.newGameName) { + const gameData: IGameRequest = { name: formValues.newGameName }; + this._addGameSubscription = this._gameEndpointsService + .addGame(gameData) + .subscribe({ + next: () => { + this._notificationService.addNotification( + 'New game has been added!', + 3000 + ); + this.errorMessage = null; + this.modalVisibility = null; + }, + error: (error: string) => { + this.errorMessage = error; + }, + }); + } + } + } + + public editGameFunction(): void { + this.errorMessage = null; + if ( + this.gameHandlingForm.value.editedGameName && + this.selectedGameId !== 0 + ) { + const formValues = this.gameHandlingForm.value; + if (formValues.editedGameName) { + const gameData: IGameRequest = { name: formValues.editedGameName }; + this._editGameSubscription = this._gameEndpointsService + .updateGame(this.selectedGameId, gameData) + .subscribe({ + next: () => { + this._notificationService.addNotification( + 'Existing game name has been changed!', + 3000 + ); + this.errorMessage = null; + this.modalVisibility = null; + }, + error: (error: string) => { + this.errorMessage = error; + }, + }); + } + } + } + + public removeGameFunction(): void { + // + } + + public ngOnDestroy(): void { + if (this._getGamesSubscription) { + this._getGamesSubscription.unsubscribe(); + } + if (this._addGameSubscription) { + this._addGameSubscription.unsubscribe(); + } + if (this._editGameSubscription) { + this._editGameSubscription.unsubscribe(); + } + if (this._removeGameSubscription) { + this._removeGameSubscription.unsubscribe(); + } + } +} diff --git a/src/app/dashboard/components/sections/user-account-settings.component.ts b/src/app/dashboard/components/sections/user-account-settings.component.ts index 07a7294..3e7d8b8 100644 --- a/src/app/dashboard/components/sections/user-account-settings.component.ts +++ b/src/app/dashboard/components/sections/user-account-settings.component.ts @@ -17,7 +17,7 @@ import { NotificationService } from 'app/shared/services/notification.service'; imports: [ModalComponent, ReactiveFormsModule], template: `

User account settings

-
+
- - -
-
- +

Administration settings

-
+
+ + + +
+ @if (modalVisibility !== null) { + +
+

+ {{ modalTitle }} +

+ + + +
+ @if (errorMessage !== null) { +

{{ errorMessage }}

+ } +
+
+
+ } + `, +}) +export class AdminSettingsComponent { + public errorMessage: string | null = null; + + public modalVisibility: + | 'banUser' + | 'changeUserRole' + | 'getUserDetails' + | 'getStudentsList' + | null = null; + public modalTitle = ''; + public modalButtonText = ''; + public modalButtonFunction!: () => void; + + public hideModal(): void { + this.modalVisibility = null; + } +} diff --git a/src/app/dashboard/dashboard.page.component.ts b/src/app/dashboard/dashboard.page.component.ts index 4925f9b..c26c8a5 100644 --- a/src/app/dashboard/dashboard.page.component.ts +++ b/src/app/dashboard/dashboard.page.component.ts @@ -18,6 +18,7 @@ import { StatsEndpointsService } from '@endpoints/stats-endpoints.service'; import { UserInfoComponent } from './components/sections/user-info.component'; import { UserAccountSettingsComponent } from './components/sections/user-account-settings.component'; import { GameHandlingOptionsComponent } from './components/sections/game-handling-options.component'; +import { AdminSettingsComponent } from './components/sections/admin-settings.component'; @Component({ selector: 'app-dashboard-page', @@ -28,6 +29,7 @@ import { GameHandlingOptionsComponent } from './components/sections/game-handlin UserInfoComponent, UserAccountSettingsComponent, GameHandlingOptionsComponent, + AdminSettingsComponent, ], template: `
@@ -37,46 +39,7 @@ import { GameHandlingOptionsComponent } from './components/sections/game-handlin class="flex flex-row justify-stretch w-full" /> -
-

- Administration settings -

-
-
- - - - -
-
+
`, }) export class DashboardPageComponent From 51b15cd496209adecaa2a4139a4a25e9a670f4b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Buczek?= Date: Mon, 21 Oct 2024 20:59:11 +0200 Subject: [PATCH 22/59] refactor: #40 reorganized styles in wrapping sections --- .../components/sections/admin-settings.component.ts | 2 +- .../sections/game-handling-options.component.ts | 2 +- .../sections/user-account-settings.component.ts | 2 +- src/app/dashboard/dashboard.page.component.ts | 8 +++++--- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/app/dashboard/components/sections/admin-settings.component.ts b/src/app/dashboard/components/sections/admin-settings.component.ts index 81ced4b..8b60311 100644 --- a/src/app/dashboard/components/sections/admin-settings.component.ts +++ b/src/app/dashboard/components/sections/admin-settings.component.ts @@ -7,7 +7,7 @@ import { ModalComponent } from '../modal.component'; imports: [ModalComponent], template: `

Administration settings

-
+
`, }) export class DashboardPageComponent From e5fbbbc80c24c207eb00e83d8b73340861c93d9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Buczek?= Date: Mon, 21 Oct 2024 21:32:28 +0200 Subject: [PATCH 23/59] feat: #40 changed date format + added recorded game component --- .../sections/recorded-games.component.spec.ts | 23 +++++++++++++++++++ .../sections/recorded-games.component.ts | 15 ++++++++++++ .../sections/user-info.component.ts | 9 ++++---- src/app/dashboard/dashboard.page.component.ts | 3 +++ 4 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 src/app/dashboard/components/sections/recorded-games.component.spec.ts create mode 100644 src/app/dashboard/components/sections/recorded-games.component.ts diff --git a/src/app/dashboard/components/sections/recorded-games.component.spec.ts b/src/app/dashboard/components/sections/recorded-games.component.spec.ts new file mode 100644 index 0000000..33781ee --- /dev/null +++ b/src/app/dashboard/components/sections/recorded-games.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { RecordedGamesComponent } from './recorded-games.component'; + +describe('RecordedGamesComponent', () => { + let component: RecordedGamesComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [RecordedGamesComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(RecordedGamesComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/dashboard/components/sections/recorded-games.component.ts b/src/app/dashboard/components/sections/recorded-games.component.ts new file mode 100644 index 0000000..47ee71a --- /dev/null +++ b/src/app/dashboard/components/sections/recorded-games.component.ts @@ -0,0 +1,15 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-recorded-games', + standalone: true, + imports: [], + template: ` +

User recorded games

+
+
+ tabelka z rekordami z gier +
+ `, +}) +export class RecordedGamesComponent {} diff --git a/src/app/dashboard/components/sections/user-info.component.ts b/src/app/dashboard/components/sections/user-info.component.ts index 6f47bad..77fc523 100644 --- a/src/app/dashboard/components/sections/user-info.component.ts +++ b/src/app/dashboard/components/sections/user-info.component.ts @@ -33,8 +33,9 @@ import { ProgressCircleBarComponent } from '../progress-circle-bar.component';

Your study cycle years: - {{ aboutMeUserInfo?.studyCycleYearA }} / - {{ aboutMeUserInfo?.studyCycleYearB }} + {{ aboutMeUserInfo?.studyCycleYearA }}/{{ + aboutMeUserInfo?.studyCycleYearB + }}

@@ -52,13 +53,13 @@ import { ProgressCircleBarComponent } from '../progress-circle-bar.component';

Your first game was on: - {{ userStatsInfo?.firstPlayed }} + {{ userStatsInfo?.firstPlayed | date: 'dd/MM/yyyy, HH:mm' }}

Your last game was on: - {{ userStatsInfo?.lastPlayed }} + {{ userStatsInfo?.lastPlayed | date: 'dd/MM/yyyy, HH:mm' }}

diff --git a/src/app/dashboard/dashboard.page.component.ts b/src/app/dashboard/dashboard.page.component.ts index 3c0e4a3..b3aa08c 100644 --- a/src/app/dashboard/dashboard.page.component.ts +++ b/src/app/dashboard/dashboard.page.component.ts @@ -19,6 +19,7 @@ import { UserInfoComponent } from './components/sections/user-info.component'; import { UserAccountSettingsComponent } from './components/sections/user-account-settings.component'; import { GameHandlingOptionsComponent } from './components/sections/game-handling-options.component'; import { AdminSettingsComponent } from './components/sections/admin-settings.component'; +import { RecordedGamesComponent } from './components/sections/recorded-games.component'; @Component({ selector: 'app-dashboard-page', @@ -30,6 +31,7 @@ import { AdminSettingsComponent } from './components/sections/admin-settings.com UserAccountSettingsComponent, GameHandlingOptionsComponent, AdminSettingsComponent, + RecordedGamesComponent, ], template: `
@@ -37,6 +39,7 @@ import { AdminSettingsComponent } from './components/sections/admin-settings.com [aboutMeUserInfo]="aboutMeUserInfo" [userStatsInfo]="userStatsInfo" class="flex flex-row justify-stretch w-full" /> +
From 5c631c270fab7ae21c5b29de29e210398ee20759 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Buczek?= Date: Mon, 21 Oct 2024 22:47:46 +0200 Subject: [PATCH 24/59] fix: #40 small admin section change --- .../dashboard/components/sections/admin-settings.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/dashboard/components/sections/admin-settings.component.ts b/src/app/dashboard/components/sections/admin-settings.component.ts index 8b60311..74f5bd9 100644 --- a/src/app/dashboard/components/sections/admin-settings.component.ts +++ b/src/app/dashboard/components/sections/admin-settings.component.ts @@ -12,7 +12,7 @@ import { ModalComponent } from '../modal.component';
@if (modalVisibility !== null) { -
+

{{ modalTitle }}

diff --git a/src/app/dashboard/components/sections/game-handling-options.component.ts b/src/app/dashboard/components/sections/game-handling-options.component.ts index d385f56..3428ef9 100644 --- a/src/app/dashboard/components/sections/game-handling-options.component.ts +++ b/src/app/dashboard/components/sections/game-handling-options.component.ts @@ -49,7 +49,7 @@ import {
@if (modalVisibility !== null) { -
+

{{ modalTitle }}

diff --git a/src/app/dashboard/components/sections/user-account-settings.component.ts b/src/app/dashboard/components/sections/user-account-settings.component.ts index 8d474dc..dfc3fa4 100644 --- a/src/app/dashboard/components/sections/user-account-settings.component.ts +++ b/src/app/dashboard/components/sections/user-account-settings.component.ts @@ -40,7 +40,7 @@ import { NotificationService } from 'app/shared/services/notification.service';
@if (modalVisibility !== null) { -
+

{{ modalTitle }}

From 21926075ad138fe3426fba859f1f6741e2d90f2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Buczek?= Date: Tue, 22 Oct 2024 21:54:30 +0200 Subject: [PATCH 27/59] refactor: #40 getUsers endpoint change --- .../endpoints/administration-endpoints.service.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/app/shared/services/endpoints/administration-endpoints.service.ts b/src/app/shared/services/endpoints/administration-endpoints.service.ts index 310e422..6011c12 100644 --- a/src/app/shared/services/endpoints/administration-endpoints.service.ts +++ b/src/app/shared/services/endpoints/administration-endpoints.service.ts @@ -80,14 +80,10 @@ export class AdministrationEndpointsService { ); } - public getStudents( - studyCycleYearA: number, - studyCycleYearB: number - ): Observable { + public getUsers(): Observable { return this._httpClient .get( - environment.backendApiUrl + - `/api/Administration/students?studyCycleYearA=${studyCycleYearA}&studyCycleYearB=${studyCycleYearB}`, + environment.backendApiUrl + `/api/Administration/users`, { responseType: 'json', headers: getAuthHeaders(), @@ -96,7 +92,7 @@ export class AdministrationEndpointsService { .pipe( tap({ next: () => { - console.log('Students data retrieved successfully'); + console.log('Users data retrieved successfully'); }, }), catchError((error: HttpErrorResponse) => { From d29641725685f8400ce5e5eb4d38ff532600ea19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Buczek?= Date: Tue, 22 Oct 2024 22:39:37 +0200 Subject: [PATCH 28/59] feat: #40 prepared admin settings section --- .../sections/admin-settings.component.ts | 107 +++++++++++++++--- 1 file changed, 90 insertions(+), 17 deletions(-) diff --git a/src/app/dashboard/components/sections/admin-settings.component.ts b/src/app/dashboard/components/sections/admin-settings.component.ts index 63bb217..e9af378 100644 --- a/src/app/dashboard/components/sections/admin-settings.component.ts +++ b/src/app/dashboard/components/sections/admin-settings.component.ts @@ -1,5 +1,10 @@ -import { Component } from '@angular/core'; +import { Component, inject } from '@angular/core'; import { ModalComponent } from '../modal.component'; +import { AdministrationEndpointsService } from '@endpoints/administration-endpoints.service'; +import { NotificationService } from 'app/shared/services/notification.service'; +import { NonNullableFormBuilder } from '@angular/forms'; +import { Subscription } from 'rxjs'; +import { IUserResponse } from 'app/shared/models/user.models'; @Component({ selector: 'app-admin-settings', @@ -11,6 +16,7 @@ import { ModalComponent } from '../modal.component';
-
@if (modalVisibility !== null) { @@ -48,12 +48,27 @@ import { ModalComponent } from '../modal.component';

{{ modalTitle }}

- - +
+ +
+ @if (modalButtonText !== null) { + + }
+ @if (modalVisibility === 'banUnbanUser' && selectedUserId !== 0) { +
+ + +
+ } @if (modalButtonText !== null) {
- } - @if ( + } @else if ( modalVisibility === 'changeUserRole' && selectedUserData !== null ) {
@@ -114,6 +118,31 @@ import { TRole } from 'app/shared/models/role.enum';
+ } @else if ( + modalVisibility === 'getUserDetails' && + selectedUserData !== null && + selectedUserStats !== null + ) { +
+ {{ selectedUserData.name }} + {{ selectedUserData.email }} + {{ selectedUserData.role }} + + {{ selectedUserData.studyCycleYearA }}/{{ + selectedUserData.studyCycleYearB + }} + + {{ + selectedUserStats.firstPlayed | date: 'dd/MM/yyyy, HH:mm' + }} + {{ + selectedUserData.lastPlayed | date: 'dd/MM/yyyy, HH:mm' + }} + {{ selectedUserData.banned }} + {{ selectedUserStats.games }} + {{ selectedUserStats.plays }} + {{ selectedUserStats.totalStorageMb }} +
} @if (modalButtonText !== null) { + +
+
+ } +
+
+ @if (errorMessage !== null) { +

{{ errorMessage }}

+ }
`, }) -export class RecordedGamesComponent {} +export class RecordedGamesComponent + implements OnInit, OnDestroy, AfterViewChecked +{ + private _gameRecordEndpointsService = inject(GameRecordEndpointsService); + private _gameEndpointsService = inject(GameEndpointsService); + private _notificationService = inject(NotificationService); + + private _getRecordedGamesSubscription: Subscription = new Subscription(); + private _getGamesSubscription: Subscription = new Subscription(); + + public avalaibleGamesList: IGameResponse[] = []; + public recordedGamesData: IRecordedGameResponse[] = []; + public errorMessage: string | null = null; + + public ngOnInit(): void { + this._getGamesSubscription = this._gameEndpointsService + .getGames() + .subscribe({ + next: response => { + this.avalaibleGamesList = response; + this.errorMessage = null; + this.getRecordedGames(); + }, + error: (error: string) => { + this.errorMessage = error; + }, + }); + } + + public ngAfterViewChecked(): void { + feather.replace(); //dodane, żeby feather-icons na nowo dodało się do DOM w pętli + } + + public getRecordedGames(): void { + for (const game of this.avalaibleGamesList) { + this._getRecordedGamesSubscription = this._gameRecordEndpointsService + .getAllRecordedGames(game.id) + .subscribe({ + next: response => { + this.recordedGamesData.push(...response); + this.errorMessage = null; + }, + error: (error: string) => { + this.errorMessage = error; + }, + }); + } + } + + public downloadGameRecord(recordedGameId: number): void { + this._gameRecordEndpointsService + .downloadSpecificRecordedGame(recordedGameId) + .subscribe({ + next: () => { + this._notificationService.addNotification( + `The game record download has started`, + 3000 + ); + this.errorMessage = null; + }, + error: (error: string) => { + this.errorMessage = error; + }, + }); + } + + public deleteGameRecord(recordedGameId: number): void { + // + } + + public ngOnDestroy(): void { + this._getRecordedGamesSubscription.unsubscribe(); + this._getGamesSubscription.unsubscribe(); + } +} From 6e8355d2f0fe59c8002b893c4d4906fb5777dd5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Buczek?= Date: Wed, 30 Oct 2024 18:28:24 +0100 Subject: [PATCH 41/59] feat: #40 handle deleting records and downloading changes --- package-lock.json | 15 ++++++++ package.json | 2 + .../recorded-games.component.ts | 22 +++++++++-- .../game-record-endpoints.service.ts | 38 ++++++++++++++++--- 4 files changed, 68 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1168588..bc06d8e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "@angular/platform-browser-dynamic": "^18.0.0", "@angular/router": "^18.0.0", "feather-icons": "^4.29.2", + "file-saver": "^2.0.5", "prettier": "^3.3.2", "rxjs": "~7.8.0", "tslib": "^2.3.0", @@ -29,6 +30,7 @@ "@angular/compiler-cli": "^18.0.0", "@cypress/schematic": "^2.5.2", "@types/feather-icons": "^4.29.4", + "@types/file-saver": "^2.0.7", "@types/jasmine": "~5.1.0", "angular-eslint": "18.0.1", "autoprefixer": "^10.4.7", @@ -5234,6 +5236,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/file-saver": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/file-saver/-/file-saver-2.0.7.tgz", + "integrity": "sha512-dNKVfHd/jk0SkR/exKGj2ggkB45MAkzvWCaqLUUgkyjITkGNzH8H+yUwr+BLJUBjZOe9w8X3wgmXhZDRg1ED6A==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/http-errors": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", @@ -9949,6 +9958,12 @@ "node": ">=16.0.0" } }, + "node_modules/file-saver": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", + "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==", + "license": "MIT" + }, "node_modules/filelist": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", diff --git a/package.json b/package.json index e11b9c0..0e4037e 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "@angular/platform-browser-dynamic": "^18.0.0", "@angular/router": "^18.0.0", "feather-icons": "^4.29.2", + "file-saver": "^2.0.5", "prettier": "^3.3.2", "rxjs": "~7.8.0", "tslib": "^2.3.0", @@ -35,6 +36,7 @@ "@angular/compiler-cli": "^18.0.0", "@cypress/schematic": "^2.5.2", "@types/feather-icons": "^4.29.4", + "@types/file-saver": "^2.0.7", "@types/jasmine": "~5.1.0", "angular-eslint": "18.0.1", "autoprefixer": "^10.4.7", diff --git a/src/app/dashboard/components/sections/recorded-games/recorded-games.component.ts b/src/app/dashboard/components/sections/recorded-games/recorded-games.component.ts index e21d11f..2a8569f 100644 --- a/src/app/dashboard/components/sections/recorded-games/recorded-games.component.ts +++ b/src/app/dashboard/components/sections/recorded-games/recorded-games.component.ts @@ -13,6 +13,7 @@ import { IRecordedGameResponse } from 'app/shared/models/recorded-game.models'; import { Subscription } from 'rxjs'; import * as feather from 'feather-icons'; import { NotificationService } from 'app/shared/services/notification.service'; +import { HttpResponse } from '@angular/common/http'; @Component({ selector: 'app-recorded-games', @@ -107,6 +108,7 @@ export class RecordedGamesComponent } public getRecordedGames(): void { + this.recordedGamesData = []; for (const game of this.avalaibleGamesList) { this._getRecordedGamesSubscription = this._gameRecordEndpointsService .getAllRecordedGames(game.id) @@ -128,8 +130,8 @@ export class RecordedGamesComponent .subscribe({ next: () => { this._notificationService.addNotification( - `The game record download has started`, - 3000 + `The game record file is downloading`, + 4000 ); this.errorMessage = null; }, @@ -140,7 +142,21 @@ export class RecordedGamesComponent } public deleteGameRecord(recordedGameId: number): void { - // + this._gameRecordEndpointsService + .deleteGameRecording(recordedGameId) + .subscribe({ + next: () => { + this._notificationService.addNotification( + `The game record has been removed`, + 3000 + ); + this.errorMessage = null; + this.getRecordedGames(); + }, + error: (error: string) => { + this.errorMessage = error; + }, + }); } public ngOnDestroy(): void { diff --git a/src/app/shared/services/endpoints/game-record-endpoints.service.ts b/src/app/shared/services/endpoints/game-record-endpoints.service.ts index fd7a115..432baa2 100644 --- a/src/app/shared/services/endpoints/game-record-endpoints.service.ts +++ b/src/app/shared/services/endpoints/game-record-endpoints.service.ts @@ -1,4 +1,8 @@ -import { HttpClient, HttpErrorResponse } from '@angular/common/http'; +import { + HttpClient, + HttpErrorResponse, + HttpResponse, +} from '@angular/common/http'; import { inject, Injectable } from '@angular/core'; import { environment } from '@env/environment'; import { errorHandler } from '@utils/helpers/errorHandler'; @@ -8,6 +12,7 @@ import { IRecordedGameResponse, } from 'app/shared/models/recorded-game.models'; import { catchError, Observable, tap, throwError } from 'rxjs'; +import { saveAs } from 'file-saver'; @Injectable({ providedIn: 'root', @@ -86,19 +91,40 @@ export class GameRecordEndpointsService { public downloadSpecificRecordedGame( recordedGameId: number - ): Observable { + ): Observable> { return this._httpClient - .get( + .get( environment.backendApiUrl + `/api/GameRecord/${recordedGameId}`, { headers: getAuthHeaders(), - responseType: 'json', + responseType: 'blob' as 'json', + observe: 'response', } ) .pipe( tap({ - next: () => { - console.log('Recorded game downloaded successfully'); + next: (response: HttpResponse) => { + if (response.body) { + const contentDisposition = response.headers.get( + 'content-disposition' + ); + const fileName = `game_record_${recordedGameId}.json`; + + console.log(response, contentDisposition); + if (contentDisposition) { + // const fileNameMatch = contentDisposition.match( + // /filename[^;=\n]*=(['"]?)([^'"\n]*)\1/ + // ); + // // eslint-disable-next-line max-depth + // if (fileNameMatch && fileNameMatch[2]) { + // fileName = fileNameMatch[2]; + // } + } + saveAs(response.body, fileName); + console.log('Recorded game downloaded successfully'); + } else { + console.error('No file data in response'); + } }, }), catchError((error: HttpErrorResponse) => { From df2cf0a1bd3fce5416647fc7f098acd51025ecf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Buczek?= Date: Wed, 30 Oct 2024 18:54:35 +0100 Subject: [PATCH 42/59] feat: #40 user-info rwd ready --- .../progress-circle-bar.component.ts | 4 +-- .../sections/user-info/user-info.component.ts | 25 ++++++++++--------- src/app/dashboard/dashboard.page.component.ts | 2 +- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/app/dashboard/components/sections/user-info/progress-circle-bar.component.ts b/src/app/dashboard/components/sections/user-info/progress-circle-bar.component.ts index 348bcce..aff630a 100644 --- a/src/app/dashboard/components/sections/user-info/progress-circle-bar.component.ts +++ b/src/app/dashboard/components/sections/user-info/progress-circle-bar.component.ts @@ -5,7 +5,7 @@ import { Component, Input, OnChanges } from '@angular/core'; standalone: true, imports: [], template: ` -
+
+ class="flex flex-col items-center justify-center absolute text-lg sm:text-2xl lg:text-xl xl:text-2xl font-bold text-mainCreme"> {{ usedSpace?.toPrecision(2) }}/{{ totalSpace.toPrecision(3) diff --git a/src/app/dashboard/components/sections/user-info/user-info.component.ts b/src/app/dashboard/components/sections/user-info/user-info.component.ts index b973ba3..5d5a785 100644 --- a/src/app/dashboard/components/sections/user-info/user-info.component.ts +++ b/src/app/dashboard/components/sections/user-info/user-info.component.ts @@ -11,26 +11,27 @@ import { ProgressCircleBarComponent } from './progress-circle-bar.component'; standalone: true, imports: [CommonModule, ProgressCircleBarComponent], template: ` -
-
-

+
+
+

Hello, {{ aboutMeUserInfo?.name }}!


-
-

+
+

Logged in as: {{ aboutMeUserInfo?.role | uppercase }}

-

+

Your email address: {{ aboutMeUserInfo?.email }}

-

+

Your study cycle years: {{ aboutMeUserInfo?.studyCycleYearA }}/{{ @@ -38,25 +39,25 @@ import { ProgressCircleBarComponent } from './progress-circle-bar.component'; }}

-

+

Your types of games played: {{ userStatsInfo?.games }}

-

+

Your total plays in all games: {{ userStatsInfo?.plays }}

-

+

Your first game was on: {{ userStatsInfo?.firstPlayed | date: 'dd/MM/yyyy, HH:mm' }}

-

+

Your last game was on: {{ userStatsInfo?.lastPlayed | date: 'dd/MM/yyyy, HH:mm' }} @@ -65,7 +66,7 @@ import { ProgressCircleBarComponent } from './progress-circle-bar.component';

diff --git a/src/app/dashboard/dashboard.page.component.ts b/src/app/dashboard/dashboard.page.component.ts index d86e9dc..2720009 100644 --- a/src/app/dashboard/dashboard.page.component.ts +++ b/src/app/dashboard/dashboard.page.component.ts @@ -34,7 +34,7 @@ import { RecordedGamesComponent } from './components/sections/recorded-games/rec RecordedGamesComponent, ], template: `
+ class="flex flex-col space-y-10 sm:space-y-16 font-mono w-full bg-mainGray pt-6 pb-12 xl:pt-14"> Date: Wed, 30 Oct 2024 19:11:35 +0100 Subject: [PATCH 43/59] fix: #40 fixed rwd small bug --- .../sections/recorded-games/recorded-games.component.ts | 7 ++++--- .../components/sections/user-info/user-info.component.ts | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/app/dashboard/components/sections/recorded-games/recorded-games.component.ts b/src/app/dashboard/components/sections/recorded-games/recorded-games.component.ts index 2a8569f..ff6d049 100644 --- a/src/app/dashboard/components/sections/recorded-games/recorded-games.component.ts +++ b/src/app/dashboard/components/sections/recorded-games/recorded-games.component.ts @@ -13,15 +13,16 @@ import { IRecordedGameResponse } from 'app/shared/models/recorded-game.models'; import { Subscription } from 'rxjs'; import * as feather from 'feather-icons'; import { NotificationService } from 'app/shared/services/notification.service'; -import { HttpResponse } from '@angular/common/http'; @Component({ selector: 'app-recorded-games', standalone: true, imports: [CommonModule], template: ` -

User recorded games

-
+

+ User recorded games +

+
Hello, {{ aboutMeUserInfo?.name }}!

-
+

From 00e33ad777b52ac74e8e4e567caf87c0f1b4c4a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Buczek?= Date: Wed, 30 Oct 2024 19:24:07 +0100 Subject: [PATCH 44/59] feat: #40 console logging --- .../shared/services/endpoints/game-record-endpoints.service.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/shared/services/endpoints/game-record-endpoints.service.ts b/src/app/shared/services/endpoints/game-record-endpoints.service.ts index 432baa2..6bcb6ed 100644 --- a/src/app/shared/services/endpoints/game-record-endpoints.service.ts +++ b/src/app/shared/services/endpoints/game-record-endpoints.service.ts @@ -105,12 +105,13 @@ export class GameRecordEndpointsService { tap({ next: (response: HttpResponse) => { if (response.body) { + console.log(response); const contentDisposition = response.headers.get( 'content-disposition' ); const fileName = `game_record_${recordedGameId}.json`; - console.log(response, contentDisposition); + // console.log(response, contentDisposition); if (contentDisposition) { // const fileNameMatch = contentDisposition.match( // /filename[^;=\n]*=(['"]?)([^'"\n]*)\1/ From af6a393659ad4ee0356ec64b888f18ea23cb1322 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Buczek?= Date: Wed, 30 Oct 2024 19:34:55 +0100 Subject: [PATCH 45/59] feat: #40 gameName working --- .../sections/recorded-games/recorded-games.component.ts | 4 +++- src/app/shared/models/recorded-game.models.ts | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/app/dashboard/components/sections/recorded-games/recorded-games.component.ts b/src/app/dashboard/components/sections/recorded-games/recorded-games.component.ts index ff6d049..b258b0c 100644 --- a/src/app/dashboard/components/sections/recorded-games/recorded-games.component.ts +++ b/src/app/dashboard/components/sections/recorded-games/recorded-games.component.ts @@ -42,7 +42,9 @@ import { NotificationService } from 'app/shared/services/notification.service'; $even ? 'bg-lightGray' : 'bg-darkGray' }}"> {{ $index + 1 }}. - nazwa gry + {{ + recordedGame.gameName + }} {{ recordedGame.started | date: 'dd/MM/yyyy, HH:mm' }} diff --git a/src/app/shared/models/recorded-game.models.ts b/src/app/shared/models/recorded-game.models.ts index 13d06e6..f94c42d 100644 --- a/src/app/shared/models/recorded-game.models.ts +++ b/src/app/shared/models/recorded-game.models.ts @@ -2,6 +2,7 @@ import { TExchangeData } from '@gameModels/exchange-data.type'; import { IPlayer } from './player.models'; export interface IRecordedGameResponse { id: number; + gameName: string; players: IPlayer[]; started: string; ended: string; From 594f577ea056336e56b73adb72c3aee71a18442e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Buczek?= Date: Wed, 30 Oct 2024 19:37:54 +0100 Subject: [PATCH 46/59] feat: #40 fileName works --- .../endpoints/game-record-endpoints.service.ts | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/app/shared/services/endpoints/game-record-endpoints.service.ts b/src/app/shared/services/endpoints/game-record-endpoints.service.ts index 6bcb6ed..92ead21 100644 --- a/src/app/shared/services/endpoints/game-record-endpoints.service.ts +++ b/src/app/shared/services/endpoints/game-record-endpoints.service.ts @@ -105,21 +105,19 @@ export class GameRecordEndpointsService { tap({ next: (response: HttpResponse) => { if (response.body) { - console.log(response); const contentDisposition = response.headers.get( 'content-disposition' ); - const fileName = `game_record_${recordedGameId}.json`; + let fileName = `game_record_${recordedGameId}.json`; - // console.log(response, contentDisposition); if (contentDisposition) { - // const fileNameMatch = contentDisposition.match( - // /filename[^;=\n]*=(['"]?)([^'"\n]*)\1/ - // ); - // // eslint-disable-next-line max-depth - // if (fileNameMatch && fileNameMatch[2]) { - // fileName = fileNameMatch[2]; - // } + const fileNameMatch = contentDisposition.match( + /filename[^;=\n]*=(['"]?)([^'"\n]*)\1/ + ); + // eslint-disable-next-line max-depth + if (fileNameMatch && fileNameMatch[2]) { + fileName = fileNameMatch[2]; + } } saveAs(response.body, fileName); console.log('Recorded game downloaded successfully'); From 76f80982a17e2d5afa0cb475c3346a3f1e0f53d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Buczek?= Date: Thu, 31 Oct 2024 20:54:56 +0100 Subject: [PATCH 47/59] feat: #40 table rwd + fix overflow menu bug --- .../recorded-games.component.ts | 85 ++++++++++--------- src/app/game/games/base-game.component.ts | 1 - .../user-shortcut/user-shortcut.component.ts | 8 +- 3 files changed, 48 insertions(+), 46 deletions(-) diff --git a/src/app/dashboard/components/sections/recorded-games/recorded-games.component.ts b/src/app/dashboard/components/sections/recorded-games/recorded-games.component.ts index b258b0c..830e598 100644 --- a/src/app/dashboard/components/sections/recorded-games/recorded-games.component.ts +++ b/src/app/dashboard/components/sections/recorded-games/recorded-games.component.ts @@ -23,52 +23,53 @@ import { NotificationService } from 'app/shared/services/notification.service'; User recorded games


-
-
- No. - Game name - Game start date - Game end date -
- Download - Delete -
-
- @for (recordedGame of recordedGamesData; track recordedGame.id) { +
+
- {{ $index + 1 }}. - {{ - recordedGame.gameName - }} - {{ - recordedGame.started | date: 'dd/MM/yyyy, HH:mm' - }} - {{ - recordedGame.ended | date: 'dd/MM/yyyy, HH:mm' - }} + class="flex flex-row space-x-4 justify-between bg-mainGray text-mainOrange font-bold px-4 py-2"> + No. + Game name + Game start date + Game end date
- - + Download + Delete
- } + @for (recordedGame of recordedGamesData; track recordedGame.id) { +
+ {{ $index + 1 }}. + {{ + recordedGame.gameName + }} + {{ + recordedGame.started | date: 'dd/MM/yyyy, HH:mm' + }} + {{ + recordedGame.ended | date: 'dd/MM/yyyy, HH:mm' + }} +
+ + +
+
+ } +
@if (errorMessage !== null) { diff --git a/src/app/game/games/base-game.component.ts b/src/app/game/games/base-game.component.ts index 73d6740..0768e8e 100644 --- a/src/app/game/games/base-game.component.ts +++ b/src/app/game/games/base-game.component.ts @@ -16,7 +16,6 @@ import { Observable, Subscription } from 'rxjs'; import { CanvasComponent } from '../components/canvas/canvas.component'; import { Game } from '@gameModels/game.class'; import { IPlayerInputData } from '@gameModels/player-input-data.type'; -import { PageVisibilityService } from 'app/shared/services/page-visibility.service'; @Component({ selector: 'app-base-game-window', diff --git a/src/app/shared/components/navbar/components/user-shortcut/user-shortcut.component.ts b/src/app/shared/components/navbar/components/user-shortcut/user-shortcut.component.ts index aacc32b..f1128ef 100644 --- a/src/app/shared/components/navbar/components/user-shortcut/user-shortcut.component.ts +++ b/src/app/shared/components/navbar/components/user-shortcut/user-shortcut.component.ts @@ -18,9 +18,11 @@ import { Subscription } from 'rxjs'; class="hover:text-green-500 ease-in-out transition-all duration-500 size-8 2xs:size-9">
+ class="flex flex-col space-y-1 text-sm 2xs:text-base absolute h-24 2xs:h-28 overflow-y-hidden -z-10 transition-all ease-in-out duration-300 {{ + isUserInfoVisible + ? 'right-0 opacity-100 w-48 md:w-52 lg:w-44 xl:w-56 p-3' + : '-right-20 opacity-0 w-0 overflow-x-hidden p-0' + }} top-[75px] 2xs:top-[83px] bg-mainGray shadow-userInfoShadow rounded-bl-lg"> Your role: {{ currentUserRole | uppercase }} From 2ea4ba35394ef28ca0f558496be3644ef053e194 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Buczek?= Date: Thu, 31 Oct 2024 21:27:12 +0100 Subject: [PATCH 48/59] feat: rwd next section --- .../recorded-games.component.ts | 50 +++++++++---------- .../user-account-settings.component.ts | 21 ++++---- src/app/dashboard/dashboard.page.component.ts | 8 +-- tailwind.config.js | 8 +++ 4 files changed, 45 insertions(+), 42 deletions(-) diff --git a/src/app/dashboard/components/sections/recorded-games/recorded-games.component.ts b/src/app/dashboard/components/sections/recorded-games/recorded-games.component.ts index 830e598..1da03d7 100644 --- a/src/app/dashboard/components/sections/recorded-games/recorded-games.component.ts +++ b/src/app/dashboard/components/sections/recorded-games/recorded-games.component.ts @@ -24,21 +24,19 @@ import { NotificationService } from 'app/shared/services/notification.service';
-
+
+ class="flex flex-row space-x-4 justify-between bg-mainGray text-mainOrange text-sm xs:text-base font-bold px-4 py-2"> No. Game name Game start date Game end date -
- Download - Delete -
+ Download + Delete
@for (recordedGame of recordedGamesData; track recordedGame.id) {
{{ $index + 1 }}. @@ -51,31 +49,29 @@ import { NotificationService } from 'app/shared/services/notification.service'; {{ recordedGame.ended | date: 'dd/MM/yyyy, HH:mm' }} -
- - -
+ +
}
-
- @if (errorMessage !== null) { + @if (errorMessage !== null) { +

{{ errorMessage }}

- } -
+
+ } `, }) export class RecordedGamesComponent diff --git a/src/app/dashboard/components/sections/user-account-settings/user-account-settings.component.ts b/src/app/dashboard/components/sections/user-account-settings/user-account-settings.component.ts index 5411a15..ab04d04 100644 --- a/src/app/dashboard/components/sections/user-account-settings/user-account-settings.component.ts +++ b/src/app/dashboard/components/sections/user-account-settings/user-account-settings.component.ts @@ -16,26 +16,25 @@ import { NotificationService } from 'app/shared/services/notification.service'; standalone: true, imports: [ModalComponent, ReactiveFormsModule], template: ` -

User account settings

-
-
+

+ User account settings +

+
+
@if (modalVisibility !== null) { diff --git a/src/app/dashboard/dashboard.page.component.ts b/src/app/dashboard/dashboard.page.component.ts index 2720009..0a812ce 100644 --- a/src/app/dashboard/dashboard.page.component.ts +++ b/src/app/dashboard/dashboard.page.component.ts @@ -40,10 +40,10 @@ import { RecordedGamesComponent } from './components/sections/recorded-games/rec [userStatsInfo]="userStatsInfo" class="flex flex-row justify-stretch w-full" /> -
- - - +
+ + +
`, }) diff --git a/tailwind.config.js b/tailwind.config.js index 972d08d..329e28a 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -87,6 +87,14 @@ module.exports = { '@apply flex flex-col shadow-sideMenuShadow z-20 absolute transition-all ease-in-out duration-700': {}, }, + '.dashboard-button': { + '@apply flex flex-row items-center justify-center space-x-4 xs:space-x-2 rounded-lg mt-1 sm:mt-2 px-2 sm:px-3 py-1 sm:py-2 bg-mainGray text-sm sm:text-base text-mainOrange border-2 border-mainOrange transition-all ease-in-out hover:bg-mainOrange hover:text-mainGray': + {}, + }, + '.dashboard-icon': { + '@apply text-mainOrange group-hover:text-mainGray transition-all ease-in-out size-4 sm:size-5': + {}, + }, }); }, ], From fe70df8c4c68a3b2b25732e45c1263b4539264ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Buczek?= Date: Thu, 31 Oct 2024 22:48:08 +0100 Subject: [PATCH 49/59] feat: #40 rwd ready for all sections --- .../admin-settings.component.ts | 28 +++++++++---------- .../game-handling-options.component.ts | 28 +++++++++---------- .../recorded-games.component.ts | 3 +- .../user-account-settings.component.ts | 3 +- .../sections/user-info/user-info.component.ts | 6 ++-- tailwind.config.js | 2 +- 6 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/app/dashboard/components/sections/admin-settings/admin-settings.component.ts b/src/app/dashboard/components/sections/admin-settings/admin-settings.component.ts index 341711f..30e6129 100644 --- a/src/app/dashboard/components/sections/admin-settings/admin-settings.component.ts +++ b/src/app/dashboard/components/sections/admin-settings/admin-settings.component.ts @@ -18,35 +18,33 @@ import { SelectedUserInfoComponent } from './selected-user-info.component'; standalone: true, imports: [ModalComponent, CommonModule, SelectedUserInfoComponent], template: ` -

Administration settings

-
-
+

+ Administration settings +

+
+
@if (modalVisibility !== null) { diff --git a/src/app/dashboard/components/sections/game-handling-options/game-handling-options.component.ts b/src/app/dashboard/components/sections/game-handling-options/game-handling-options.component.ts index 8eccdff..364df48 100644 --- a/src/app/dashboard/components/sections/game-handling-options/game-handling-options.component.ts +++ b/src/app/dashboard/components/sections/game-handling-options/game-handling-options.component.ts @@ -16,35 +16,33 @@ import { standalone: true, imports: [ModalComponent, ReactiveFormsModule], template: ` -

Game handling options

-
-
+

+ Game handling options +

+
+
@if (modalVisibility !== null) { diff --git a/src/app/dashboard/components/sections/recorded-games/recorded-games.component.ts b/src/app/dashboard/components/sections/recorded-games/recorded-games.component.ts index 1da03d7..404d0d9 100644 --- a/src/app/dashboard/components/sections/recorded-games/recorded-games.component.ts +++ b/src/app/dashboard/components/sections/recorded-games/recorded-games.component.ts @@ -19,7 +19,8 @@ import { NotificationService } from 'app/shared/services/notification.service'; standalone: true, imports: [CommonModule], template: ` -

+

User recorded games


diff --git a/src/app/dashboard/components/sections/user-account-settings/user-account-settings.component.ts b/src/app/dashboard/components/sections/user-account-settings/user-account-settings.component.ts index ab04d04..29c2a89 100644 --- a/src/app/dashboard/components/sections/user-account-settings/user-account-settings.component.ts +++ b/src/app/dashboard/components/sections/user-account-settings/user-account-settings.component.ts @@ -16,7 +16,8 @@ import { NotificationService } from 'app/shared/services/notification.service'; standalone: true, imports: [ModalComponent, ReactiveFormsModule], template: ` -

+

User account settings


diff --git a/src/app/dashboard/components/sections/user-info/user-info.component.ts b/src/app/dashboard/components/sections/user-info/user-info.component.ts index 5acec84..da25969 100644 --- a/src/app/dashboard/components/sections/user-info/user-info.component.ts +++ b/src/app/dashboard/components/sections/user-info/user-info.component.ts @@ -13,10 +13,12 @@ import { ProgressCircleBarComponent } from './progress-circle-bar.component'; template: `
-

+

Hello, {{ aboutMeUserInfo?.name }}!

-
+

diff --git a/tailwind.config.js b/tailwind.config.js index 329e28a..c06f1e3 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -92,7 +92,7 @@ module.exports = { {}, }, '.dashboard-icon': { - '@apply text-mainOrange group-hover:text-mainGray transition-all ease-in-out size-4 sm:size-5': + '@apply text-mainOrange group-hover:text-mainGray transition-all ease-in-out min-w-fit size-4 sm:size-5': {}, }, }); From dd45297368029de669a70bd2f34cc95e1d211708 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Buczek?= Date: Thu, 31 Oct 2024 23:22:22 +0100 Subject: [PATCH 50/59] feat: #40 finished dashboard rwd --- .../admin-settings.component.ts | 29 +++++++++++-------- .../selected-user-info.component.ts | 24 ++++++++------- .../game-handling-options.component.ts | 12 ++++---- .../user-account-settings.component.ts | 13 +++++---- .../components/shared/modal.component.ts | 2 +- 5 files changed, 44 insertions(+), 36 deletions(-) diff --git a/src/app/dashboard/components/sections/admin-settings/admin-settings.component.ts b/src/app/dashboard/components/sections/admin-settings/admin-settings.component.ts index 30e6129..fd14d69 100644 --- a/src/app/dashboard/components/sections/admin-settings/admin-settings.component.ts +++ b/src/app/dashboard/components/sections/admin-settings/admin-settings.component.ts @@ -50,10 +50,11 @@ import { SelectedUserInfoComponent } from './selected-user-info.component'; @if (modalVisibility !== null) {
-

+

{{ modalTitle }}

-
+
@@ -122,21 +124,21 @@ import { SelectedUserInfoComponent } from './selected-user-info.component'; + class="flex flex-col pt-2 xs:pt-4 items-start text-sm 2xs:text-base sm:text-lg" /> } @if (modalButtonText !== null) { } -
+
@if (errorMessage !== null) {

{{ errorMessage }}

} @@ -172,16 +174,19 @@ export class AdminSettingsComponent implements OnDestroy { public modalButtonText: string | null = ''; public modalButtonFunction!: () => void; + // eslint-disable-next-line complexity public setSelectedUser(event: Event): void { const target = event.target as HTMLSelectElement; const selectedId = parseInt(target?.value, 10); - if (this.usersList) { + if (this.usersList && selectedId !== 0) { this.usersList.map(user => { if (user.id === selectedId) { this.isBanned = user.banned; this.selectedUserData = user; } }); + } else { + this.selectedUserData = null; } if ( this.modalVisibility === 'getUserDetails' && diff --git a/src/app/dashboard/components/sections/admin-settings/selected-user-info.component.ts b/src/app/dashboard/components/sections/admin-settings/selected-user-info.component.ts index ad53e35..de935cc 100644 --- a/src/app/dashboard/components/sections/admin-settings/selected-user-info.component.ts +++ b/src/app/dashboard/components/sections/admin-settings/selected-user-info.component.ts @@ -10,26 +10,28 @@ import { standalone: true, imports: [CommonModule], template: ` -

User's info:

-

+

+ User's info: +

+

Name: {{ selectedUserData.name }}

-

+

Email: {{ selectedUserData.email }}

-

+

Role: {{ selectedUserData.role }}

-

+

Study cycle years: {{ selectedUserData.studyCycleYearA }}/{{ @@ -37,37 +39,37 @@ import { }}

-

+

Ban status: {{ selectedUserData.banned ? 'BANNED' : 'NOT BANNED' }}

-

+

Types of games played: {{ selectedUserStats.games }}

-

+

Total plays in all games: {{ selectedUserStats.plays }}

-

+

Total disk space used: {{ selectedUserStats.totalStorageMb.toPrecision(2) }}MB

-

+

First game was on: {{ selectedUserStats.firstPlayed | date: 'dd/MM/yyyy, HH:mm' }}

-

+

Last game was on: {{ selectedUserStats.lastPlayed | date: 'dd/MM/yyyy, HH:mm' }} diff --git a/src/app/dashboard/components/sections/game-handling-options/game-handling-options.component.ts b/src/app/dashboard/components/sections/game-handling-options/game-handling-options.component.ts index 364df48..fda78cd 100644 --- a/src/app/dashboard/components/sections/game-handling-options/game-handling-options.component.ts +++ b/src/app/dashboard/components/sections/game-handling-options/game-handling-options.component.ts @@ -48,12 +48,13 @@ import { @if (modalVisibility !== null) {
-

+

{{ modalTitle }}

+ class="flex flex-col space-y-4 w-full text-sm sm:text-base"> @if (modalVisibility === 'addNewGame') {