From 20dec1e4eb2b6a4d53ab93b36ca2cc74286573e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20G=C3=B3reczny?= Date: Thu, 21 Jan 2021 18:19:24 +0100 Subject: [PATCH 1/7] get all shopkeepers --- .../ClientApp/src/app/app.module.ts | 2 + .../game-master-dashboard.component.ts | 1 + ...me-master-shopkeepers-table.component.html | 10 +--- ...game-master-shopkeepers-table.component.ts | 60 ++++++++++++------- .../src/services/shopkeeper.service.ts | 34 +++++++++++ .../ShopkeeperViewModel.ts | 5 ++ .../Controllers/ShopkeeperController.cs | 2 +- 7 files changed, 83 insertions(+), 31 deletions(-) create mode 100644 SessionCompanion/SessionCompanion/ClientApp/src/services/shopkeeper.service.ts create mode 100644 SessionCompanion/SessionCompanion/ClientApp/src/types/viewmodels/shopkeeper-viewmodels/ShopkeeperViewModel.ts diff --git a/SessionCompanion/SessionCompanion/ClientApp/src/app/app.module.ts b/SessionCompanion/SessionCompanion/ClientApp/src/app/app.module.ts index 85aee6f..978514c 100644 --- a/SessionCompanion/SessionCompanion/ClientApp/src/app/app.module.ts +++ b/SessionCompanion/SessionCompanion/ClientApp/src/app/app.module.ts @@ -53,6 +53,7 @@ import { GameMasterShopkeepersTableComponent } from './components/game-master-sh import { GameMasterTurntrackerComponent } from './components/game-master-turntracker/game-master-turntracker.component'; import { DragDropModule } from '@angular/cdk/drag-drop'; import { ChooseMonsterDialogComponent } from './components/choose-monster-dialog/choose-monster-dialog.component'; +import { ShopkeeperService } from '../services/shopkeeper.service'; @NgModule({ declarations: [ @@ -113,6 +114,7 @@ import { ChooseMonsterDialogComponent } from './components/choose-monster-dialog ArmorService, OtherEquipmentService, MonsterService, + ShopkeeperService, ], bootstrap: [AppComponent], entryComponents: [ diff --git a/SessionCompanion/SessionCompanion/ClientApp/src/app/components/game-master-dashboard/game-master-dashboard.component.ts b/SessionCompanion/SessionCompanion/ClientApp/src/app/components/game-master-dashboard/game-master-dashboard.component.ts index 78f106c..11044af 100644 --- a/SessionCompanion/SessionCompanion/ClientApp/src/app/components/game-master-dashboard/game-master-dashboard.component.ts +++ b/SessionCompanion/SessionCompanion/ClientApp/src/app/components/game-master-dashboard/game-master-dashboard.component.ts @@ -97,6 +97,7 @@ export class GameMasterDashboardComponent implements OnInit, OnDestroy { ngOnInit() { this.signalRService.Login(); + this.UpdateCharactersList(); } UpdateSidenavStatus(sidenav: string, newValue: boolean) { diff --git a/SessionCompanion/SessionCompanion/ClientApp/src/app/components/game-master-shopkeepers-table/game-master-shopkeepers-table.component.html b/SessionCompanion/SessionCompanion/ClientApp/src/app/components/game-master-shopkeepers-table/game-master-shopkeepers-table.component.html index 2d8e425..9d7b8bf 100644 --- a/SessionCompanion/SessionCompanion/ClientApp/src/app/components/game-master-shopkeepers-table/game-master-shopkeepers-table.component.html +++ b/SessionCompanion/SessionCompanion/ClientApp/src/app/components/game-master-shopkeepers-table/game-master-shopkeepers-table.component.html @@ -11,20 +11,16 @@ {{row.name}} - - Items Count - {{row.itemsCount}} - - Actions - - - @@ -40,3 +40,7 @@ + + diff --git a/SessionCompanion/SessionCompanion/ClientApp/src/app/components/game-master-shopkeepers-table/game-master-shopkeepers-table.component.ts b/SessionCompanion/SessionCompanion/ClientApp/src/app/components/game-master-shopkeepers-table/game-master-shopkeepers-table.component.ts index 6c6a000..17ead8c 100644 --- a/SessionCompanion/SessionCompanion/ClientApp/src/app/components/game-master-shopkeepers-table/game-master-shopkeepers-table.component.ts +++ b/SessionCompanion/SessionCompanion/ClientApp/src/app/components/game-master-shopkeepers-table/game-master-shopkeepers-table.component.ts @@ -8,6 +8,8 @@ import { ShopkeeperService } from '../../../services/shopkeeper.service'; import { first } from 'rxjs/operators'; import { ErrorResponse } from '../../../types/ErrorResponse'; import { HttpErrorResponse } from '@angular/common/http'; +import { MatDialog } from '@angular/material'; +import { NewShopkeeperDialogComponent } from './shopkeeper-dialogs/new-shopkeeper-dialog/new-shopkeeper-dialog.component'; @Component({ selector: 'app-game-master-shopkeepers-table', @@ -17,13 +19,21 @@ import { HttpErrorResponse } from '@angular/common/http'; export class GameMasterShopkeepersTableComponent implements OnInit { displayedColumns: string[] = ['name', 'actions']; dataSource: MatTableDataSource; + isAnyAvailable: boolean = false; @ViewChild(MatPaginator, { static: true }) paginator: MatPaginator; @ViewChild(MatSort, { static: true }) sort: MatSort; - constructor(private shopkeeperService: ShopkeeperService) {} + constructor( + private shopkeeperService: ShopkeeperService, + public dialog: MatDialog + ) {} ngOnInit() { + this.GetAllShopkeepers(); + } + + GetAllShopkeepers() { this.shopkeeperService .GetAllShopkeepers() .pipe(first()) @@ -32,6 +42,9 @@ export class GameMasterShopkeepersTableComponent implements OnInit { this.dataSource = new MatTableDataSource(result); this.dataSource.sort = this.sort; this.dataSource.paginator = this.paginator; + if (result.find((e) => e.isAvailable) != null) { + this.isAnyAvailable = true; + } }, (error: ErrorResponse | HttpErrorResponse) => { console.error(error); @@ -44,15 +57,80 @@ export class GameMasterShopkeepersTableComponent implements OnInit { } ActivateShopkeeper(shopkeeperId: number) { - if (this.dataSource.data.find((e) => e.isAvailable) != null) { - this.dataSource.data.find((e) => e.id == shopkeeperId).isAvailable = true; - this.shopkeeperService.ChangeShopkeeperStatus(shopkeeperId, true); - } + this.shopkeeperService + .ChangeShopkeeperStatus(shopkeeperId, true) + .pipe(first()) + .subscribe( + (result) => { + this.dataSource.data.find( + (e) => e.id == shopkeeperId + ).isAvailable = true; + this.isAnyAvailable = true; + }, + (error: ErrorResponse | HttpErrorResponse) => { + console.error(error); + if (error instanceof HttpErrorResponse) { + error = error.error as ErrorResponse; + } + console.error(error.message); + } + ); } DeactivateShopkeeper(shopkeeperId: number) { - this.dataSource.data.find((e) => e.id == shopkeeperId).isAvailable = false; - this.shopkeeperService.ChangeShopkeeperStatus(shopkeeperId, false); + this.shopkeeperService + .ChangeShopkeeperStatus(shopkeeperId, false) + .pipe(first()) + .subscribe( + (result) => { + this.dataSource.data.find( + (e) => e.id == shopkeeperId + ).isAvailable = false; + this.isAnyAvailable = false; + }, + (error: ErrorResponse | HttpErrorResponse) => { + console.error(error); + if (error instanceof HttpErrorResponse) { + error = error.error as ErrorResponse; + } + console.error(error.message); + } + ); + } + + RemoveShopkeeper(shopkeeperId: number) { + let currentStatus = this.dataSource.data.find((e) => e.id == shopkeeperId) + .isAvailable; + this.shopkeeperService + .RemoveShopkeeper(shopkeeperId, currentStatus) + .pipe(first()) + .subscribe( + (result) => { + if (currentStatus == true) { + this.isAnyAvailable = false; + } + this.dataSource.data = this.dataSource.data.filter( + (e) => e.id != shopkeeperId + ); + }, + (error: ErrorResponse | HttpErrorResponse) => { + console.error(error); + if (error instanceof HttpErrorResponse) { + error = error.error as ErrorResponse; + } + console.error(error.message); + } + ); + } + + AddNewShopkeeper() { + this.dialog + .open(NewShopkeeperDialogComponent) + .afterClosed() + .pipe(first()) + .subscribe(() => { + this.GetAllShopkeepers(); + }); } applyFilter(event: Event) { diff --git a/SessionCompanion/SessionCompanion/ClientApp/src/app/components/game-master-shopkeepers-table/shopkeeper-dialogs/new-shopkeeper-dialog/new-shopkeeper-dialog.component.css b/SessionCompanion/SessionCompanion/ClientApp/src/app/components/game-master-shopkeepers-table/shopkeeper-dialogs/new-shopkeeper-dialog/new-shopkeeper-dialog.component.css new file mode 100644 index 0000000..6dbe130 --- /dev/null +++ b/SessionCompanion/SessionCompanion/ClientApp/src/app/components/game-master-shopkeepers-table/shopkeeper-dialogs/new-shopkeeper-dialog/new-shopkeeper-dialog.component.css @@ -0,0 +1,62 @@ +::ng-deep .mat-horizontal-content-container { + overflow-y: auto !important; + max-height: 500px; +} + +table { + background-color: initial; +} + +mat-paginator { + background-color: initial; + color: white; +} + +::ng-deep .mat-select-arrow { + color: whitesmoke; +} + +::ng-deep .mat-select-value { + color: white; +} + +.button-to-right { + float: right; +} + +.button-to-right > button { + color: whitesmoke; +} + +td, +th { + color: whitesmoke; +} + +.shopkeeper-create-button { + color: #7bce7b !important; +} + +.shopkeeper-stepper-main { + background-color: #4f5c69; +} + +::ng-deep .mat-step-label { + color: white !important; +} + +::ng-deep .mat-step-icon-selected { + background-color: #df7c0f !important; +} + +::ng-deep .mat-step-icon-state-edit { + background-color: #df7c0f !important; +} + +::ng-deep mat-dialog-container { + background-color: #4f5c69 !important; +} + +::ng-deep .mat-checkbox-checked.mat-accent .mat-checkbox-background { + background-color: #df7c0f; +} diff --git a/SessionCompanion/SessionCompanion/ClientApp/src/app/components/game-master-shopkeepers-table/shopkeeper-dialogs/new-shopkeeper-dialog/new-shopkeeper-dialog.component.html b/SessionCompanion/SessionCompanion/ClientApp/src/app/components/game-master-shopkeepers-table/shopkeeper-dialogs/new-shopkeeper-dialog/new-shopkeeper-dialog.component.html new file mode 100644 index 0000000..8d3e06f --- /dev/null +++ b/SessionCompanion/SessionCompanion/ClientApp/src/app/components/game-master-shopkeepers-table/shopkeeper-dialogs/new-shopkeeper-dialog/new-shopkeeper-dialog.component.html @@ -0,0 +1,104 @@ + + +
+ Fill out your shop name + + Name + + +
+
+ +
+
+ + Select items +
+ + + + + + + + + + + + + + + + + + + + + + + +
Select + + + + Name {{row.itemName}} Type {{row.itemType}}
No data found
+ + +
+
+ + +
+
+ + Set a quantity +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Name {{row.itemName}} Type {{row.itemType}} Amount + + + + Remove + +
No data found
+ + +
+
+ + +
+
+
+ diff --git a/SessionCompanion/SessionCompanion/ClientApp/src/app/components/game-master-shopkeepers-table/shopkeeper-dialogs/new-shopkeeper-dialog/new-shopkeeper-dialog.component.ts b/SessionCompanion/SessionCompanion/ClientApp/src/app/components/game-master-shopkeepers-table/shopkeeper-dialogs/new-shopkeeper-dialog/new-shopkeeper-dialog.component.ts new file mode 100644 index 0000000..59412fc --- /dev/null +++ b/SessionCompanion/SessionCompanion/ClientApp/src/app/components/game-master-shopkeepers-table/shopkeeper-dialogs/new-shopkeeper-dialog/new-shopkeeper-dialog.component.ts @@ -0,0 +1,170 @@ +import { Component, OnInit, ViewChild } from '@angular/core'; +import { MatTableDataSource } from '@angular/material/table'; +import { MatPaginator } from '@angular/material/paginator'; +import { MatSort } from '@angular/material/sort'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { WeaponService } from '../../../../../services/weapon.service'; +import { ArmorService } from '../../../../../services/armor.service'; +import { ShopkeeperItemsWithItemNameAndTypeViewModel } from '../../../../../types/viewmodels/shopkeeper-viewmodels/ShopkeeperItemsWithItemNameAndTypeViewModel'; +import { first, map } from 'rxjs/operators'; +import { forkJoin, Observable } from 'rxjs'; +import { SelectionModel } from '@angular/cdk/collections'; +import { + MatDialog, + MatDialogRef, + MatHorizontalStepper, +} from '@angular/material'; +import { ShopkeeperService } from '../../../../../services/shopkeeper.service'; +import { ShopkeeperWithItemsViewModel } from '../../../../../types/viewmodels/shopkeeper-viewmodels/ShopkeeperWithItemsViewModel'; +import { ShopkeeperItemsViewModel } from '../../../../../types/viewmodels/shopkeeper-viewmodels/ShopkeeperItemsViewModel'; + +@Component({ + selector: 'app-new-shopkeeper-dialog', + templateUrl: './new-shopkeeper-dialog.component.html', + styleUrls: ['./new-shopkeeper-dialog.component.css'], +}) +export class NewShopkeeperDialogComponent implements OnInit { + displayedColumnsForFinal: string[] = [ + 'itemName', + 'itemType', + 'amount', + 'actions', + ]; + displayedColumns: string[] = ['select', 'itemName', 'itemType']; + dataSourceWithAllitems: MatTableDataSource; + selection = new SelectionModel( + true, + [] + ); + finalDataSource: MatTableDataSource; + nameFormGroup: FormGroup; + + @ViewChild(MatPaginator, { static: true }) paginator: MatPaginator; + @ViewChild(MatSort, { static: true }) sort: MatSort; + @ViewChild(MatPaginator, { static: true }) paginatorForFinal: MatPaginator; + @ViewChild(MatSort, { static: true }) sortForFinal: MatSort; + + constructor( + private formBuilder: FormBuilder, + private weaponService: WeaponService, + private armorService: ArmorService, + private shopkeeperService: ShopkeeperService, + public dialogRef: MatDialogRef + ) {} + + ngOnInit() { + this.GetAllWeaponsAndArmors() + .pipe(first()) + .subscribe((res) => { + this.dataSourceWithAllitems = new MatTableDataSource( + res + ); + this.dataSourceWithAllitems.sort = this.sort; + this.dataSourceWithAllitems.paginator = this.paginator; + }); + + this.finalDataSource = new MatTableDataSource( + [] + ); + this.finalDataSource.paginator = this.paginatorForFinal; + this.finalDataSource.sort = this.sortForFinal; + + this.nameFormGroup = this.formBuilder.group({ + name: ['', Validators.required], + }); + } + + GetAllWeaponsAndArmors(): Observable< + ShopkeeperItemsWithItemNameAndTypeViewModel[] + > { + return forkJoin([ + this.armorService.GetAllArmors(), + this.weaponService.GetAllWeapons(), + ]).pipe( + first(), + map(([armors, weapons]) => { + let resultArray: ShopkeeperItemsWithItemNameAndTypeViewModel[]; + + resultArray = weapons.map( + (e) => { + return { + shopkeeperId: null, + amount: 1, + armorId: null, + weaponId: e.id, + itemName: e.name, + itemType: 'Weapon', + }; + } + ); + + return [ + ...resultArray, + ...armors.map((e) => { + return { + shopkeeperId: null, + amount: 1, + armorId: null, + weaponId: e.id, + itemName: e.name, + itemType: 'Armor', + }; + }), + ]; + }) + ); + } + + ToFinalStep(stepper: MatHorizontalStepper) { + this.selection.selected.forEach((item) => { + if ( + this.finalDataSource.data.filter( + (e) => e.armorId == item.armorId && e.weaponId == item.weaponId + ).length == 0 + ) { + this.finalDataSource.data = [...this.finalDataSource.data, item]; + } + }); + + this.selection.clear(); + + stepper.next(); + } + + CreateShopkeeper() { + if (this.finalDataSource.data.length > 0) { + let filteredData = this.finalDataSource.data.filter((e) => e.amount > 0); + let shopkeeperModel: ShopkeeperWithItemsViewModel = { + id: null, + name: this.nameFormGroup.value.name, + isAvailable: false, + items: filteredData.map((item) => { + return { + shopkeeperId: null, + armorId: item.armorId, + weaponId: item.weaponId, + amount: item.amount, + }; + }), + }; + this.shopkeeperService + .CreateShopkeeper(shopkeeperModel) + .pipe(first()) + .subscribe((result) => { + this.dialogRef.close(); + }); + } + } + + RemoveItem(weaponId: number, armorId: number) { + if (weaponId != null) { + this.finalDataSource.data = this.finalDataSource.data.filter( + (e) => e.weaponId != weaponId + ); + } else if (armorId != null) { + this.finalDataSource.data = this.finalDataSource.data.filter( + (e) => e.armorId != armorId + ); + } + } +} diff --git a/SessionCompanion/SessionCompanion/ClientApp/src/services/shopkeeper.service.ts b/SessionCompanion/SessionCompanion/ClientApp/src/services/shopkeeper.service.ts index e3f8cf6..f29a9d0 100644 --- a/SessionCompanion/SessionCompanion/ClientApp/src/services/shopkeeper.service.ts +++ b/SessionCompanion/SessionCompanion/ClientApp/src/services/shopkeeper.service.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@angular/core'; -import { HttpClient, HttpParams } from '@angular/common/http'; +import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; import { Observable, of, throwError } from 'rxjs'; import { ShopkeeperViewModel } from '../types/viewmodels/shopkeeper-viewmodels/ShopkeeperViewModel'; import { Either } from '../types/Either'; @@ -7,6 +7,7 @@ import { ErrorResponse } from '../types/ErrorResponse'; import { switchMap } from 'rxjs/operators'; import { SuccessResponse } from '../types/SuccessResponse'; import { ShopkeeperWithItemsDetailsViewModel } from '../types/viewmodels/shopkeeper-viewmodels/ShopkeeperWithItemsDetailsViewModel'; +import { ShopkeeperWithItemsViewModel } from '../types/viewmodels/shopkeeper-viewmodels/ShopkeeperWithItemsViewModel'; Injectable({ providedIn: 'root', @@ -24,7 +25,6 @@ export class ShopkeeperService { ) .pipe( switchMap((response) => { - debugger; if (response.isLeft) { return of(response.left); } else { @@ -41,7 +41,6 @@ export class ShopkeeperService { ) .pipe( switchMap((response) => { - debugger; if (response.isLeft) { return of(response.left); } else { @@ -51,17 +50,82 @@ export class ShopkeeperService { ); } - ChangeShopkeeperStatus(shopkeeperId: number, newStatus: boolean) { + CreateShopkeeper( + shopkeeperModel: ShopkeeperWithItemsViewModel + ): Observable { + const params = new HttpParams().set( + 'shopkeeperWithItemsViewModel', + JSON.stringify(shopkeeperModel) + ); + const httpOptions = { + headers: new HttpHeaders({ 'Content-Type': 'application/json' }), + }; + + console.log(params); + + return this.http + .post>( + this.baseUrl + 'createNewShopkeeper', + shopkeeperModel, + httpOptions + ) + .pipe( + switchMap((response) => { + if (response.isLeft) { + return of(response.left); + } else { + return throwError(response.right); + } + }) + ); + } + + ChangeShopkeeperStatus( + shopkeeperId: number, + newStatus: boolean + ): Observable { const params = new HttpParams() .set('shopkeeperId', shopkeeperId.toString()) .set('availability', newStatus.toString()); - this.http.post(this.baseUrl + 'getShopkeepers', params); + + return this.http + .put>( + this.baseUrl + 'changeShopkeeperStatus', + null, + { params } + ) + .pipe( + switchMap((response) => { + if (response.isLeft) { + return of(response.left); + } else { + return throwError(response.right); + } + }) + ); } - RemoveShopkeeper(shopkeeperId: number, currentStatus: boolean) { + RemoveShopkeeper( + shopkeeperId: number, + currentStatus: boolean + ): Observable { const params = new HttpParams() .set('shopkeeperId', shopkeeperId.toString()) .set('wasAvailable', currentStatus.toString()); - this.http.post(this.baseUrl + 'getShopkeepers', params); + + return this.http + .delete>( + this.baseUrl + 'removeShopkeeper', + { params } + ) + .pipe( + switchMap((response) => { + if (response.isLeft) { + return of(response.left); + } else { + return throwError(response.right); + } + }) + ); } } diff --git a/SessionCompanion/SessionCompanion/ClientApp/src/types/viewmodels/shopkeeper-viewmodels/ShopkeeperItemsViewModel.ts b/SessionCompanion/SessionCompanion/ClientApp/src/types/viewmodels/shopkeeper-viewmodels/ShopkeeperItemsViewModel.ts new file mode 100644 index 0000000..8d68c64 --- /dev/null +++ b/SessionCompanion/SessionCompanion/ClientApp/src/types/viewmodels/shopkeeper-viewmodels/ShopkeeperItemsViewModel.ts @@ -0,0 +1,6 @@ +export interface ShopkeeperItemsViewModel { + shopkeeperId: number; + armorId: number; + weaponId: number; + amount: number; +} diff --git a/SessionCompanion/SessionCompanion/ClientApp/src/types/viewmodels/shopkeeper-viewmodels/ShopkeeperItemsWithItemNameAndTypeViewModel.ts b/SessionCompanion/SessionCompanion/ClientApp/src/types/viewmodels/shopkeeper-viewmodels/ShopkeeperItemsWithItemNameAndTypeViewModel.ts new file mode 100644 index 0000000..d571708 --- /dev/null +++ b/SessionCompanion/SessionCompanion/ClientApp/src/types/viewmodels/shopkeeper-viewmodels/ShopkeeperItemsWithItemNameAndTypeViewModel.ts @@ -0,0 +1,8 @@ +export interface ShopkeeperItemsWithItemNameAndTypeViewModel { + shopkeeperId: number; + armorId: number; + weaponId: number; + amount: number; + itemName: string; + itemType: string; +} diff --git a/SessionCompanion/SessionCompanion/ClientApp/src/types/viewmodels/shopkeeper-viewmodels/ShopkeeperWithItemsViewModel.ts b/SessionCompanion/SessionCompanion/ClientApp/src/types/viewmodels/shopkeeper-viewmodels/ShopkeeperWithItemsViewModel.ts new file mode 100644 index 0000000..0271936 --- /dev/null +++ b/SessionCompanion/SessionCompanion/ClientApp/src/types/viewmodels/shopkeeper-viewmodels/ShopkeeperWithItemsViewModel.ts @@ -0,0 +1,8 @@ +import { ShopkeeperItemsViewModel } from './ShopkeeperItemsViewModel'; + +export interface ShopkeeperWithItemsViewModel { + id: number; + name: string; + isAvailable: boolean; + items: ShopkeeperItemsViewModel[]; +} diff --git a/SessionCompanion/SessionCompanion/Controllers/ShopkeeperController.cs b/SessionCompanion/SessionCompanion/Controllers/ShopkeeperController.cs index 0a05544..dfcc2b0 100644 --- a/SessionCompanion/SessionCompanion/Controllers/ShopkeeperController.cs +++ b/SessionCompanion/SessionCompanion/Controllers/ShopkeeperController.cs @@ -55,7 +55,7 @@ namespace SessionCompanion.Controllers /// /// SuccesResponse/ErrorResponse [HttpPut("changeShopkeeperStatus")] - public async Task> ChangeShopkeeperStatus([Required] int shopkeeperId, [Required] bool availability) + public async Task> ChangeShopkeeperStatus([FromQuery][Required] int shopkeeperId, [FromQuery][Required] bool availability) { var result = await _service.ChangeShopkeeperStatus(shopkeeperId, availability); if (result.IsLeft && availability) @@ -74,11 +74,14 @@ namespace SessionCompanion.Controllers public async Task> CreateNewShopKeeper([Required] ShopkeeperWithItemsViewModel shopkeeperWithItemsViewModel) { if (!ModelState.IsValid) + { return new ErrorResponse() { StatusCode = 500, Message = "Model is invalid" }; + } + return await _service.CreateNewShopKeeper(shopkeeperWithItemsViewModel); } - [HttpPost("removeShopkeeper")] + [HttpDelete("removeShopkeeper")] public async Task> RemoveShopkeeper([Required] int shopkeeperId, bool wasAvailable) { await this._service.Delete(shopkeeperId); From 9d36c166003d79da95406d071bf6715f4eeb4404 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20G=C3=B3reczny?= Date: Sat, 23 Jan 2021 19:36:24 +0100 Subject: [PATCH 6/7] SES-160 function to buy items --- .../Interfaces/IShopkeeperItemService.cs | 4 + .../Services/ShopkeeperItemService.cs | 46 +++++- .../Services/ShopkeeperService.cs | 2 +- .../ClientApp/src/app/app.module.ts | 3 + .../player-dashboard.component.css | 13 +- .../player-dashboard.component.html | 6 +- .../player-dashboard.component.ts | 22 ++- .../player-shop/player-shop.component.css | 60 ++++++++ .../player-shop/player-shop.component.html | 70 +++++++++ .../player-shop/player-shop.component.ts | 144 ++++++++++++++++++ .../player-weapons-table.component.ts | 16 +- .../signalR-service/player-signalR.service.ts | 24 +++ .../src/services/character.service.ts | 124 +++++++++------ .../src/services/shopkeeper.service.ts | 42 +++++ .../CharacterBasicInfoViewModel.ts | 9 ++ ...msWithItemNameAndTypeForPlayerViewModel.ts | 9 ++ .../Controllers/ShopkeeperController.cs | 87 ++++++++++- 17 files changed, 617 insertions(+), 64 deletions(-) create mode 100644 SessionCompanion/SessionCompanion/ClientApp/src/app/components/player-shop/player-shop.component.css create mode 100644 SessionCompanion/SessionCompanion/ClientApp/src/app/components/player-shop/player-shop.component.html create mode 100644 SessionCompanion/SessionCompanion/ClientApp/src/app/components/player-shop/player-shop.component.ts create mode 100644 SessionCompanion/SessionCompanion/ClientApp/src/types/viewmodels/character-viewmodels/CharacterBasicInfoViewModel.ts create mode 100644 SessionCompanion/SessionCompanion/ClientApp/src/types/viewmodels/shopkeeper-viewmodels/ShopkeeperItemsWithItemNameAndTypeForPlayerViewModel.ts diff --git a/SessionCompanion/SessionCompanion.Services/Interfaces/IShopkeeperItemService.cs b/SessionCompanion/SessionCompanion.Services/Interfaces/IShopkeeperItemService.cs index 6f6bb45..197522e 100644 --- a/SessionCompanion/SessionCompanion.Services/Interfaces/IShopkeeperItemService.cs +++ b/SessionCompanion/SessionCompanion.Services/Interfaces/IShopkeeperItemService.cs @@ -9,7 +9,11 @@ using System.Threading.Tasks; namespace SessionCompanion.Services.Interfaces { + using SessionCompanion.Extensions.EitherType; + using SessionCompanion.ViewModels.ApiResponses; + public interface IShopkeeperItemService : IServiceBase { + Task> GetActiveShkopkeeperWithItems(int shopkeeperId, int amount, int? weaponId, int? armorId); } } diff --git a/SessionCompanion/SessionCompanion.Services/Services/ShopkeeperItemService.cs b/SessionCompanion/SessionCompanion.Services/Services/ShopkeeperItemService.cs index 271e927..1778f59 100644 --- a/SessionCompanion/SessionCompanion.Services/Services/ShopkeeperItemService.cs +++ b/SessionCompanion/SessionCompanion.Services/Services/ShopkeeperItemService.cs @@ -5,16 +5,56 @@ using SessionCompanion.Services.Base; using SessionCompanion.Services.Interfaces; using SessionCompanion.ViewModels.ShopkeeperItemsViewModels; using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Threading.Tasks; namespace SessionCompanion.Services.Services { + using System.Linq; + + using Microsoft.EntityFrameworkCore; + + using SessionCompanion.Extensions.EitherType; + using SessionCompanion.ViewModels.ApiResponses; + using SessionCompanion.ViewModels.ShopkeeperViewModels; + public class ShopkeeperItemService : ServiceBase, IShopkeeperItemService { public ShopkeeperItemService(IMapper mapper, IRepository repository) : base(mapper, repository) { } + + public async Task> GetActiveShkopkeeperWithItems(int shopkeeperId, int amount, int? weaponId, int? armorId) + { + try + { + var shopkeeperItem = await Repository.Get(c => c.ShopkeeperId.Equals(shopkeeperId) && c.WeaponId.Equals(weaponId) && c.ArmorId.Equals(armorId)).FirstOrDefaultAsync(); + + if (shopkeeperItem == null) + { + throw new Exception("Shopkeeper with given id's couldn't have been found"); + } + + if (shopkeeperItem.Amount - amount < 0) + { + throw new Exception("Shopkeeper does not have that quantity of the item"); + } + else if (shopkeeperItem.Amount - amount == 0) + { + this.Repository.Delete(shopkeeperItem); + await this.Repository.Save(); + return new SuccessResponse("Items were deducted from the shopkeeper"); + } + else + { + shopkeeperItem.Amount -= amount; + await this.Repository.Update(shopkeeperItem); + await this.Repository.Save(); + return new SuccessResponse("Items were deducted from the shopkeeper"); + } + } + catch (Exception e) + { + return new ErrorResponse() { StatusCode = 500, Message = e.Message }; + } + } } } diff --git a/SessionCompanion/SessionCompanion.Services/Services/ShopkeeperService.cs b/SessionCompanion/SessionCompanion.Services/Services/ShopkeeperService.cs index 73ec699..d859da0 100644 --- a/SessionCompanion/SessionCompanion.Services/Services/ShopkeeperService.cs +++ b/SessionCompanion/SessionCompanion.Services/Services/ShopkeeperService.cs @@ -86,7 +86,7 @@ namespace SessionCompanion.Services.Services .Include(t => t.ShopkeeperItems) .ThenInclude(t => t.Armor) .Include(t => t.ShopkeeperItems) - .ThenInclude(t => t.Weapon).SingleAsync(); + .ThenInclude(t => t.Weapon).FirstOrDefaultAsync(); var result = Mapper.Map(activeShopkeeper); return result; } diff --git a/SessionCompanion/SessionCompanion/ClientApp/src/app/app.module.ts b/SessionCompanion/SessionCompanion/ClientApp/src/app/app.module.ts index 1b290e7..a26051f 100644 --- a/SessionCompanion/SessionCompanion/ClientApp/src/app/app.module.ts +++ b/SessionCompanion/SessionCompanion/ClientApp/src/app/app.module.ts @@ -65,6 +65,7 @@ import { DragDropModule } from '@angular/cdk/drag-drop'; import { ChooseMonsterDialogComponent } from './components/choose-monster-dialog/choose-monster-dialog.component'; import { ShopkeeperService } from '../services/shopkeeper.service'; import { NewShopkeeperDialogComponent } from './components/game-master-shopkeepers-table/shopkeeper-dialogs/new-shopkeeper-dialog/new-shopkeeper-dialog.component'; +import { PlayerShopComponent } from './components/player-shop/player-shop.component'; @NgModule({ declarations: [ @@ -94,6 +95,7 @@ import { NewShopkeeperDialogComponent } from './components/game-master-shopkeepe GameMasterTurntrackerComponent, ChooseMonsterDialogComponent, NewShopkeeperDialogComponent, + PlayerShopComponent, ], imports: [ BrowserModule.withServerTransition({ appId: 'ng-cli-universal' }), @@ -158,6 +160,7 @@ import { NewShopkeeperDialogComponent } from './components/game-master-shopkeepe GameMasterTurntrackerComponent, ChooseMonsterDialogComponent, NewShopkeeperDialogComponent, + PlayerShopComponent, ], }) export class AppModule {} diff --git a/SessionCompanion/SessionCompanion/ClientApp/src/app/components/player-dashboard/player-dashboard.component.css b/SessionCompanion/SessionCompanion/ClientApp/src/app/components/player-dashboard/player-dashboard.component.css index 95aaff4..9e7e493 100644 --- a/SessionCompanion/SessionCompanion/ClientApp/src/app/components/player-dashboard/player-dashboard.component.css +++ b/SessionCompanion/SessionCompanion/ClientApp/src/app/components/player-dashboard/player-dashboard.component.css @@ -1,9 +1,16 @@ -@import "../../../styles.css"; +@import '../../../styles.css'; -.toggle-class{ +.toggle-class { margin: 5px; } +.character-name { + width: 240px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + .menu-spacer { flex: 1 1 auto; } @@ -36,7 +43,7 @@ background-color: #102028; } -.mat-list-item.active{ +.mat-list-item.active { color: #e9cca7; } diff --git a/SessionCompanion/SessionCompanion/ClientApp/src/app/components/player-dashboard/player-dashboard.component.html b/SessionCompanion/SessionCompanion/ClientApp/src/app/components/player-dashboard/player-dashboard.component.html index 9920974..797b0dd 100644 --- a/SessionCompanion/SessionCompanion/ClientApp/src/app/components/player-dashboard/player-dashboard.component.html +++ b/SessionCompanion/SessionCompanion/ClientApp/src/app/components/player-dashboard/player-dashboard.component.html @@ -5,8 +5,8 @@ menu -
- Session Companion +
+ {{characterName}}
@@ -47,7 +47,7 @@ Profile --> - + shopping_cart Shop diff --git a/SessionCompanion/SessionCompanion/ClientApp/src/app/components/player-dashboard/player-dashboard.component.ts b/SessionCompanion/SessionCompanion/ClientApp/src/app/components/player-dashboard/player-dashboard.component.ts index 2041330..02ef477 100644 --- a/SessionCompanion/SessionCompanion/ClientApp/src/app/components/player-dashboard/player-dashboard.component.ts +++ b/SessionCompanion/SessionCompanion/ClientApp/src/app/components/player-dashboard/player-dashboard.component.ts @@ -12,6 +12,8 @@ import { Router } from '@angular/router'; import { ClearCharacterId } from '../../store/actions/player.action'; import { MatSnackBar } from '@angular/material'; import { SnackbarComponent } from '../../shared/snackbar/snackbar.component'; +import { CharacterService } from '../../../services/character.service'; +import { PlayerShopComponent } from '../player-shop/player-shop.component'; @Component({ selector: 'app-player-dashboard', @@ -22,12 +24,14 @@ export class PlayerDashboardComponent implements OnInit { middleComponent; isExpanded = false; selected = false; + characterName: string = ''; constructor( private signalRService: PlayerSignalRService, private store: Store, private router: Router, - private _snackBar: MatSnackBar + private _snackBar: MatSnackBar, + private characterService: CharacterService ) {} ngOnInit() { @@ -38,6 +42,13 @@ export class PlayerDashboardComponent implements OnInit { this.SubscribeToEvents(); this.signalRService.Login(id); this.SwitchMiddleComponent('AbilitiesComponent'); + this.characterService + .GetCharacterBasicInfo(id) + .pipe(first()) + .subscribe((result) => { + console.log(result); + this.characterName = result.name; + }); }); } @@ -59,6 +70,9 @@ export class PlayerDashboardComponent implements OnInit { case 'PlayerOtherEquipmentTableComponent': this.middleComponent = PlayerOtherEquipmentTableComponent; break; + case 'PlayerShopComponent': + this.middleComponent = PlayerShopComponent; + break; } } @@ -82,6 +96,12 @@ export class PlayerDashboardComponent implements OnInit { gmMessage: result.parameters.message, }, }); + break; + case 'RefreshShopComponent': + this.middleComponent = ''; + + this.middleComponent = PlayerShopComponent; + break; } } ); diff --git a/SessionCompanion/SessionCompanion/ClientApp/src/app/components/player-shop/player-shop.component.css b/SessionCompanion/SessionCompanion/ClientApp/src/app/components/player-shop/player-shop.component.css new file mode 100644 index 0000000..eb0329a --- /dev/null +++ b/SessionCompanion/SessionCompanion/ClientApp/src/app/components/player-shop/player-shop.component.css @@ -0,0 +1,60 @@ +.no-shops-button { + color: whitesmoke !important; + background-color: #ad2424 !important; +} + +table { + background-color: initial; +} + +mat-paginator { + background-color: initial; + color: white; +} + +::ng-deep .mat-select-arrow { + color: whitesmoke; +} + +::ng-deep .mat-select-value { + color: white; +} + +.mat-sort-header-container { + color: whitesmoke !important; +} + +.mat-form-field { + font-size: 14px; + width: 100%; +} + +td, +th { + color: whitesmoke; +} + +.mat-header-cell { + flex-direction: column; + justify-content: center; + text-align: center; +} + +.mat-cell { + text-align: center; + justify-content: center; +} + +::ng-deep td.mat-cell:first-of-type, +::ng-deep th.mat-header-cell:first-of-type { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + max-width: 100px; +} + +.player-shop-buy-button { + background-color: #1f4416; + margin-left: 5px; + color: whitesmoke; +} diff --git a/SessionCompanion/SessionCompanion/ClientApp/src/app/components/player-shop/player-shop.component.html b/SessionCompanion/SessionCompanion/ClientApp/src/app/components/player-shop/player-shop.component.html new file mode 100644 index 0000000..5f81186 --- /dev/null +++ b/SessionCompanion/SessionCompanion/ClientApp/src/app/components/player-shop/player-shop.component.html @@ -0,0 +1,70 @@ +
+ +
+
+

+ {{shopkeeper.name}} +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Name + + {{row.armor.name}} + + + {{row.weapon.name}} + + Type + + Armor + + + Weapon + + Amount {{row.amount}} How many + + + + Actions + +
No data found
+ + +
+
diff --git a/SessionCompanion/SessionCompanion/ClientApp/src/app/components/player-shop/player-shop.component.ts b/SessionCompanion/SessionCompanion/ClientApp/src/app/components/player-shop/player-shop.component.ts new file mode 100644 index 0000000..f6b76a0 --- /dev/null +++ b/SessionCompanion/SessionCompanion/ClientApp/src/app/components/player-shop/player-shop.component.ts @@ -0,0 +1,144 @@ +import { Component, OnInit, ViewChild } from '@angular/core'; +import { ShopkeeperWithItemsDetailsViewModel } from '../../../types/viewmodels/shopkeeper-viewmodels/ShopkeeperWithItemsDetailsViewModel'; +import { ShopkeeperService } from '../../../services/shopkeeper.service'; +import { first } from 'rxjs/operators'; +import { PlayerSignalRService } from '../../shared/signalR-service/player-signalR.service'; +import { MatPaginator } from '@angular/material/paginator'; +import { MatSort } from '@angular/material/sort'; +import { MatTableDataSource } from '@angular/material/table'; +import { ShopkeeperItemsWithItemNameAndTypeForPlayerViewModel } from '../../../types/viewmodels/shopkeeper-viewmodels/ShopkeeperItemsWithItemNameAndTypeForPlayerViewModel'; +import { Store } from '@ngrx/store'; +import { AppState } from '../../store/models/app-state.model'; + +@Component({ + selector: 'app-player-shop', + templateUrl: './player-shop.component.html', + styleUrls: ['./player-shop.component.css'], +}) +export class PlayerShopComponent implements OnInit { + shopkeeper: ShopkeeperWithItemsDetailsViewModel; + + displayedColumns: string[] = [ + 'itemName', + 'itemType', + 'amount', + 'amountToBuy', + 'actions', + ]; + dataSource: MatTableDataSource; + @ViewChild(MatPaginator, { static: true }) paginator: MatPaginator; + @ViewChild(MatSort, { static: true }) sort: MatSort; + + constructor( + private shopkeeperService: ShopkeeperService, + private signalRService: PlayerSignalRService, + private store: Store + ) {} + + ngOnInit() { + this.SubscribeToEvents(); + this.GetActiveShopkeeper(); + } + + GetActiveShopkeeper(): void { + this.shopkeeperService + .GetActiveShopkeeper() + .pipe(first()) + .subscribe((result) => { + this.shopkeeper = result; + + if (this.shopkeeper != null) { + this.dataSource = new MatTableDataSource( + result.items.map( + (item) => { + return { + shopkeeperId: item.shopkeeperId, + weaponId: item.weaponId, + armorId: item.armorId, + amount: item.amount, + amountToBuy: 1, + armor: item.armor, + weapon: item.weapon, + }; + } + ) + ); + this.dataSource.sort = this.sort; + this.dataSource.paginator = this.paginator; + } + }); + } + + BuyItem(weaponId: number, armorId: number) { + this.store + .select((s) => s.playerStore.characterId) + .pipe(first()) + .subscribe((chracterId) => { + let amount = this.dataSource.data.find( + (item) => item.weaponId == weaponId && item.armorId == armorId + ).amountToBuy; + + this.shopkeeperService + .BuyItemFromShopkeeper( + this.shopkeeper.id, + chracterId, + weaponId, + armorId, + amount + ) + .pipe(first()) + .subscribe( + (result) => {}, + (error) => { + console.error(error); + } + ); + }); + } + + RefreshAmountOnList() { + this.shopkeeperService + .GetActiveShopkeeper() + .pipe(first()) + .subscribe((result) => { + this.dataSource.data.forEach((item, index, object) => { + if (item.weaponId != null) { + let lineWithSameWeapon = result.items.find( + (e) => e.weaponId == item.weaponId + ); + if (lineWithSameWeapon == null) { + object.splice(index, 1); + } else { + object[index].amount = lineWithSameWeapon.amount; + } + } else if (item.armorId != null) { + let lineWithSameArmor = result.items.find( + (e) => e.armorId == item.armorId + ); + if (lineWithSameArmor == null) { + object.splice(index, 1); + } else { + object[index].amount = lineWithSameArmor.amount; + } + } + }); + this.dataSource.sort = this.sort; + this.dataSource.paginator = this.paginator; + }); + } + + private SubscribeToEvents(): void { + this.signalRService.runMethod.subscribe( + (result: { methodName: string; parameters }) => { + switch (result.methodName) { + case 'RefreshShopComponent': + this.GetActiveShopkeeper(); + break; + case 'RefreshShopkeeperItems': + this.RefreshAmountOnList(); + break; + } + } + ); + } +} diff --git a/SessionCompanion/SessionCompanion/ClientApp/src/app/components/player-weapons-table/player-weapons-table.component.ts b/SessionCompanion/SessionCompanion/ClientApp/src/app/components/player-weapons-table/player-weapons-table.component.ts index 89d454a..b9db9f7 100644 --- a/SessionCompanion/SessionCompanion/ClientApp/src/app/components/player-weapons-table/player-weapons-table.component.ts +++ b/SessionCompanion/SessionCompanion/ClientApp/src/app/components/player-weapons-table/player-weapons-table.component.ts @@ -13,22 +13,20 @@ import { AppState } from '../../store/models/app-state.model'; @Component({ selector: 'app-player-weapons-table', templateUrl: './player-weapons-table.component.html', - styleUrls: ['./player-weapons-table.component.css'] + styleUrls: ['./player-weapons-table.component.css'], }) export class PlayerWeaponsTableComponent implements OnInit { weapons: WeaponViewModel[]; - displayedColumns: string[] = [ - 'name', - 'weaponType', - 'weight', - 'cost', - ]; + displayedColumns: string[] = ['name', 'weaponType', 'weight', 'cost']; dataSource: MatTableDataSource; @ViewChild(MatPaginator, { static: true }) paginator: MatPaginator; @ViewChild(MatSort, { static: true }) sort: MatSort; - constructor(private store: Store, private equipmentService: EquipmentService) {} + constructor( + private store: Store, + private equipmentService: EquipmentService + ) {} ngOnInit() { this.getCharacterWeapons(); @@ -40,7 +38,7 @@ export class PlayerWeaponsTableComponent implements OnInit { .pipe(first()) .subscribe((characterId) => { this.equipmentService - .getCharacterWeapons(1) + .getCharacterWeapons(characterId) .pipe(first()) .subscribe( (result) => { diff --git a/SessionCompanion/SessionCompanion/ClientApp/src/app/shared/signalR-service/player-signalR.service.ts b/SessionCompanion/SessionCompanion/ClientApp/src/app/shared/signalR-service/player-signalR.service.ts index 4216e7f..c085d1e 100644 --- a/SessionCompanion/SessionCompanion/ClientApp/src/app/shared/signalR-service/player-signalR.service.ts +++ b/SessionCompanion/SessionCompanion/ClientApp/src/app/shared/signalR-service/player-signalR.service.ts @@ -33,5 +33,29 @@ export class PlayerSignalRService { }); } ); + this.signalR.hubConnection.on('NewShopkeeperArrived', () => { + this.runMethod.next({ + methodName: 'RefreshShopComponent', + parameters: {}, + }); + }); + this.signalR.hubConnection.on('ShopkeeperLeft', () => { + this.runMethod.next({ + methodName: 'RefreshShopComponent', + parameters: {}, + }); + }); + this.signalR.hubConnection.on('ShopkeeperRemoved', () => { + this.runMethod.next({ + methodName: 'RefreshShopComponent', + parameters: {}, + }); + }); + this.signalR.hubConnection.on('ItemFromShopkeeperWasBought', () => { + this.runMethod.next({ + methodName: 'RefreshShopkeeperItems', + parameters: {}, + }); + }); } } diff --git a/SessionCompanion/SessionCompanion/ClientApp/src/services/character.service.ts b/SessionCompanion/SessionCompanion/ClientApp/src/services/character.service.ts index db70675..59ddd7f 100644 --- a/SessionCompanion/SessionCompanion/ClientApp/src/services/character.service.ts +++ b/SessionCompanion/SessionCompanion/ClientApp/src/services/character.service.ts @@ -1,16 +1,17 @@ -import {Inject, Injectable} from '@angular/core'; -import {HttpClient, HttpParams} from '@angular/common/http'; -import {Observable, of, throwError} from 'rxjs'; -import {ErrorResponse} from '../types/ErrorResponse'; -import {Either} from '../types/Either'; -import {CharacterForLoginViewModel} from '../types/viewmodels/character-viewmodels/CharacterForLoginViewModel'; -import {switchMap, retry} from 'rxjs/operators'; -import {LoggedCharactersViewModel} from '../types/viewmodels/character-viewmodels/LoggedCharactersViewModel'; -import {CharacterStatsViewModel} from "../types/viewmodels/character-viewmodels/CharacterStatsViewModel"; +import { Inject, Injectable } from '@angular/core'; +import { HttpClient, HttpParams } from '@angular/common/http'; +import { Observable, of, throwError } from 'rxjs'; +import { ErrorResponse } from '../types/ErrorResponse'; +import { Either } from '../types/Either'; +import { CharacterForLoginViewModel } from '../types/viewmodels/character-viewmodels/CharacterForLoginViewModel'; +import { switchMap, retry } from 'rxjs/operators'; +import { LoggedCharactersViewModel } from '../types/viewmodels/character-viewmodels/LoggedCharactersViewModel'; +import { CharacterStatsViewModel } from '../types/viewmodels/character-viewmodels/CharacterStatsViewModel'; +import { CharacterBasicInfoViewModel } from '../types/viewmodels/character-viewmodels/CharacterBasicInfoViewModel'; Injectable({ - providedIn: 'root' -}) + providedIn: 'root', +}); export class CharacterService { private baseUrl = 'api/character/'; constructor(private http: HttpClient, @Inject('BASE_URL') baseUrl: string) { @@ -18,42 +19,79 @@ export class CharacterService { } getLoggedCharacters(): Observable { - return this.http.get>(this.baseUrl + 'loggedCharacters').pipe( - switchMap(response => { - if (response.isLeft) { - return of(response.left); - } else { - return throwError(response.right); - } - }), - retry(3) - ); + return this.http + .get>( + this.baseUrl + 'loggedCharacters' + ) + .pipe( + switchMap((response) => { + if (response.isLeft) { + return of(response.left); + } else { + return throwError(response.right); + } + }), + retry(3) + ); } - getUserCharactersList(userId: number): Observable { - const params = new HttpParams().set('userId', userId.toString()) - return this.http.get>(this.baseUrl + 'userCharactersList', {params}).pipe( - switchMap(response => { - if (response.isLeft) { - return of(response.left); - } else { - return throwError(response.right); - } - }) - ); + getUserCharactersList( + userId: number + ): Observable { + const params = new HttpParams().set('userId', userId.toString()); + return this.http + .get>( + this.baseUrl + 'userCharactersList', + { params } + ) + .pipe( + switchMap((response) => { + if (response.isLeft) { + return of(response.left); + } else { + return throwError(response.right); + } + }) + ); } - getCharacterStats(characterId: number): Observable { - const params = new HttpParams().set('characterId', characterId.toString()) - return this.http.get>( this.baseUrl + 'characterStats', {params}).pipe( - switchMap( response => { - if (response.isLeft) { - return of(response.left); - } else { - return throwError(response.right); - } - }) - ); + getCharacterStats( + characterId: number + ): Observable { + const params = new HttpParams().set('characterId', characterId.toString()); + return this.http + .get>( + this.baseUrl + 'characterStats', + { params } + ) + .pipe( + switchMap((response) => { + if (response.isLeft) { + return of(response.left); + } else { + return throwError(response.right); + } + }) + ); } + GetCharacterBasicInfo( + characterId: number + ): Observable { + const params = new HttpParams().set('characterId', characterId.toString()); + return this.http + .get>( + this.baseUrl + 'characterBasicInfo', + { params } + ) + .pipe( + switchMap((response) => { + if (response.isLeft) { + return of(response.left); + } else { + return throwError(response.right); + } + }) + ); + } } diff --git a/SessionCompanion/SessionCompanion/ClientApp/src/services/shopkeeper.service.ts b/SessionCompanion/SessionCompanion/ClientApp/src/services/shopkeeper.service.ts index f29a9d0..ad66d52 100644 --- a/SessionCompanion/SessionCompanion/ClientApp/src/services/shopkeeper.service.ts +++ b/SessionCompanion/SessionCompanion/ClientApp/src/services/shopkeeper.service.ts @@ -128,4 +128,46 @@ export class ShopkeeperService { }) ); } + + BuyItemFromShopkeeper( + shopkeeperId: number, + characterId: number, + weaponId: number, + armorId: number, + amount: number + ): Observable { + let params = new HttpParams(); + + if (armorId != null) { + params = new HttpParams() + .set('shopkeeperId', shopkeeperId.toString()) + .set('characterId', characterId.toString()) + .set('amount', amount.toString()) + .set('armorId', armorId.toString()); + } + + if (weaponId != null) { + params = new HttpParams() + .set('shopkeeperId', shopkeeperId.toString()) + .set('characterId', characterId.toString()) + .set('amount', amount.toString()) + .set('weaponId', weaponId.toString()); + } + + return this.http + .put>( + this.baseUrl + 'buyItem', + null, + { params } + ) + .pipe( + switchMap((response) => { + if (response.isLeft) { + return of(response.left); + } else { + return throwError(response.right); + } + }) + ); + } } diff --git a/SessionCompanion/SessionCompanion/ClientApp/src/types/viewmodels/character-viewmodels/CharacterBasicInfoViewModel.ts b/SessionCompanion/SessionCompanion/ClientApp/src/types/viewmodels/character-viewmodels/CharacterBasicInfoViewModel.ts new file mode 100644 index 0000000..c2d6347 --- /dev/null +++ b/SessionCompanion/SessionCompanion/ClientApp/src/types/viewmodels/character-viewmodels/CharacterBasicInfoViewModel.ts @@ -0,0 +1,9 @@ +export interface CharacterBasicInfoViewModel { + id: number; + name: string; + level: number; + currentHealthPoints: number; + maxHealthPoints: number; + class: string; + race: string; +} diff --git a/SessionCompanion/SessionCompanion/ClientApp/src/types/viewmodels/shopkeeper-viewmodels/ShopkeeperItemsWithItemNameAndTypeForPlayerViewModel.ts b/SessionCompanion/SessionCompanion/ClientApp/src/types/viewmodels/shopkeeper-viewmodels/ShopkeeperItemsWithItemNameAndTypeForPlayerViewModel.ts new file mode 100644 index 0000000..377004d --- /dev/null +++ b/SessionCompanion/SessionCompanion/ClientApp/src/types/viewmodels/shopkeeper-viewmodels/ShopkeeperItemsWithItemNameAndTypeForPlayerViewModel.ts @@ -0,0 +1,9 @@ +export interface ShopkeeperItemsWithItemNameAndTypeForPlayerViewModel { + shopkeeperId: number; + armorId: number; + weaponId: number; + amount: number; + amountToBuy: number; + armor: {}; + weapon: {}; +} diff --git a/SessionCompanion/SessionCompanion/Controllers/ShopkeeperController.cs b/SessionCompanion/SessionCompanion/Controllers/ShopkeeperController.cs index dfcc2b0..674d264 100644 --- a/SessionCompanion/SessionCompanion/Controllers/ShopkeeperController.cs +++ b/SessionCompanion/SessionCompanion/Controllers/ShopkeeperController.cs @@ -14,17 +14,28 @@ namespace SessionCompanion.Controllers using Microsoft.AspNetCore.SignalR; using SessionCompanion.Hubs; + using SessionCompanion.ViewModels.CharacterArmorViewModels; + using SessionCompanion.ViewModels.CharacterWeaponViewModels; [ApiController] [Route("api/shopkeeper")] public class ShopkeeperController : Controller { private IShopkeeperService _service; + + private IShopkeeperItemService _shopkeeperItemsService; + + private ICharacterArmorService _characterArmorService; + + private ICharacterWeaponService _characterWeaponService; private IHubContext _sessionHub; - public ShopkeeperController(IShopkeeperService shopkeeperService, IHubContext sessionHub) + public ShopkeeperController(IShopkeeperService shopkeeperService, IShopkeeperItemService shopkeeperItemsService, ICharacterWeaponService characterWeaponService, ICharacterArmorService characterArmorService, IHubContext sessionHub) { this._service = shopkeeperService; + this._shopkeeperItemsService = shopkeeperItemsService; + this._characterArmorService = characterArmorService; + this._characterWeaponService = characterWeaponService; this._sessionHub = sessionHub; } @@ -70,6 +81,80 @@ namespace SessionCompanion.Controllers return result; } + /// + /// Metoda kupna przedmiotu od Shopkeepera + /// + /// id sprzedawcy + /// id postaci + /// id broni + /// id pancerza + /// ilosc przedmiotu do kupienia + /// Sukces bądź błąd + [HttpPut("buyItem")] + public async Task> BuyItemFromShopkeeper([Required] int shopkeeperId, [Required] int characterId, int? weaponId, int? armorId, [Required] int amount) + { + if (amount <= 0) + { + return new ErrorResponse("Given amount is 0 or less"); + } + if (shopkeeperId <= 0) + { + return new ErrorResponse("Wrong Shopkeeper Id"); + } + + if (characterId <= 0) + { + return new ErrorResponse("Wrong Character Id"); + } + + var substrackResult = await this._shopkeeperItemsService.GetActiveShkopkeeperWithItems(shopkeeperId, amount, weaponId, armorId); + + if (substrackResult.IsLeft) + { + await this._sessionHub.Clients.Groups("Players").SendAsync("ItemFromShopkeeperWasBought"); + + if (weaponId.HasValue) + { + for (int i = 0; i < amount; i++) + { + await this._characterWeaponService.Create( + new CharacterWeaponViewModel() + { + CharacterId = characterId, + HoldInLeftHand = false, + HoldInRightHand = false, + InUse = false, + WeaponId = weaponId.Value + }); + await this._characterWeaponService.SaveAsync(); + } + } + + if (armorId.HasValue) + { + for (int i = 0; i < amount; i++) + { + await this._characterArmorService.Create( + new CharacterArmorViewModel() + { + ArmorId = armorId.Value, + CharacterId = characterId, + InUse = false + }); + await this._characterArmorService.SaveAsync(); + } + } + + return new SuccessResponse("Ttems have been purchased"); + } + else + { + return substrackResult; + } + } + + + [HttpPost("createNewShopkeeper")] public async Task> CreateNewShopKeeper([Required] ShopkeeperWithItemsViewModel shopkeeperWithItemsViewModel) { From 123d1673b69d8cecbf9efcb21481b9bf94bbfc2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20G=C3=B3reczny?= Date: Sat, 23 Jan 2021 19:53:19 +0100 Subject: [PATCH 7/7] SES-160 log fix --- .../send-message-action/send-message-action.component.ts | 1 - .../game-master-character-actions-dialog.component.ts | 1 - .../game-master-turntracker.component.ts | 1 - .../player-dashboard/player-dashboard.component.ts | 1 - .../ClientApp/src/services/shopkeeper.service.ts | 6 ------ 5 files changed, 10 deletions(-) diff --git a/SessionCompanion/SessionCompanion/ClientApp/src/app/components/game-master-character-actions-dialog/actions-components/send-message-action/send-message-action.component.ts b/SessionCompanion/SessionCompanion/ClientApp/src/app/components/game-master-character-actions-dialog/actions-components/send-message-action/send-message-action.component.ts index a829ac7..b275ac0 100644 --- a/SessionCompanion/SessionCompanion/ClientApp/src/app/components/game-master-character-actions-dialog/actions-components/send-message-action/send-message-action.component.ts +++ b/SessionCompanion/SessionCompanion/ClientApp/src/app/components/game-master-character-actions-dialog/actions-components/send-message-action/send-message-action.component.ts @@ -17,7 +17,6 @@ export class SendMessageActionComponent implements OnInit { ngOnInit() {} SendMessage(message: string) { - debugger; this.signalRService.SendMessageToPlayer(this.characterId, message); this.Close(); } diff --git a/SessionCompanion/SessionCompanion/ClientApp/src/app/components/game-master-character-actions-dialog/game-master-character-actions-dialog.component.ts b/SessionCompanion/SessionCompanion/ClientApp/src/app/components/game-master-character-actions-dialog/game-master-character-actions-dialog.component.ts index b3f457b..1362b9b 100644 --- a/SessionCompanion/SessionCompanion/ClientApp/src/app/components/game-master-character-actions-dialog/game-master-character-actions-dialog.component.ts +++ b/SessionCompanion/SessionCompanion/ClientApp/src/app/components/game-master-character-actions-dialog/game-master-character-actions-dialog.component.ts @@ -31,7 +31,6 @@ export class GameMasterCharacterActionsDialogComponent implements OnInit { this.characterId = this.data.characterid; this.characterName = this.data.characterName; this.inputs = { characterId: this.characterId }; - console.log(this.inputs); } ChangeActionComponent(componentName: string): void { diff --git a/SessionCompanion/SessionCompanion/ClientApp/src/app/components/game-master-turntracker/game-master-turntracker.component.ts b/SessionCompanion/SessionCompanion/ClientApp/src/app/components/game-master-turntracker/game-master-turntracker.component.ts index 00892c3..990f543 100644 --- a/SessionCompanion/SessionCompanion/ClientApp/src/app/components/game-master-turntracker/game-master-turntracker.component.ts +++ b/SessionCompanion/SessionCompanion/ClientApp/src/app/components/game-master-turntracker/game-master-turntracker.component.ts @@ -116,7 +116,6 @@ export class GameMasterTurntrackerComponent implements OnInit, OnDestroy { ); result.forEach((ele) => { if (monstersOnList.map((e) => e.name).includes(ele.name)) { - debugger; let maxCurrentId = Math.max( ...monstersOnList.map((e) => e.monsterId), 0 diff --git a/SessionCompanion/SessionCompanion/ClientApp/src/app/components/player-dashboard/player-dashboard.component.ts b/SessionCompanion/SessionCompanion/ClientApp/src/app/components/player-dashboard/player-dashboard.component.ts index 02ef477..d74ac9c 100644 --- a/SessionCompanion/SessionCompanion/ClientApp/src/app/components/player-dashboard/player-dashboard.component.ts +++ b/SessionCompanion/SessionCompanion/ClientApp/src/app/components/player-dashboard/player-dashboard.component.ts @@ -46,7 +46,6 @@ export class PlayerDashboardComponent implements OnInit { .GetCharacterBasicInfo(id) .pipe(first()) .subscribe((result) => { - console.log(result); this.characterName = result.name; }); }); diff --git a/SessionCompanion/SessionCompanion/ClientApp/src/services/shopkeeper.service.ts b/SessionCompanion/SessionCompanion/ClientApp/src/services/shopkeeper.service.ts index ad66d52..9cdd2e4 100644 --- a/SessionCompanion/SessionCompanion/ClientApp/src/services/shopkeeper.service.ts +++ b/SessionCompanion/SessionCompanion/ClientApp/src/services/shopkeeper.service.ts @@ -53,16 +53,10 @@ export class ShopkeeperService { CreateShopkeeper( shopkeeperModel: ShopkeeperWithItemsViewModel ): Observable { - const params = new HttpParams().set( - 'shopkeeperWithItemsViewModel', - JSON.stringify(shopkeeperModel) - ); const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json' }), }; - console.log(params); - return this.http .post>( this.baseUrl + 'createNewShopkeeper',