SES-156 create character from template #86
@ -5,6 +5,8 @@ import { RegistrationComponent } from './app/components/registration/registratio
|
|||||||
import {GameMasterDashboardComponent} from './app/components/game-master-dashboard/game-master-dashboard.component';
|
import {GameMasterDashboardComponent} from './app/components/game-master-dashboard/game-master-dashboard.component';
|
||||||
import {PlayerDashboardComponent} from './app/components/player-dashboard/player-dashboard.component';
|
import {PlayerDashboardComponent} from './app/components/player-dashboard/player-dashboard.component';
|
||||||
import { SelectCharacterComponent } from './app/components/select-character/select-character.component';
|
import { SelectCharacterComponent } from './app/components/select-character/select-character.component';
|
||||||
|
import {CreateCharacterComponent} from "./app/components/create-character/create-character.component";
|
||||||
|
import {PersonalizeTemplateComponent} from "./app/components/personalize-template/personalize-template.component";
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
@ -37,6 +39,16 @@ const routes: Routes = [
|
|||||||
component: SelectCharacterComponent,
|
component: SelectCharacterComponent,
|
||||||
pathMatch: 'full'
|
pathMatch: 'full'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'create-character-step-1',
|
||||||
|
component: CreateCharacterComponent,
|
||||||
|
pathMatch: 'full'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'create-character-step-2',
|
||||||
|
component: PersonalizeTemplateComponent,
|
||||||
|
pathMatch: 'full'
|
||||||
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
export const appRoutingModule = RouterModule.forRoot(routes);
|
export const appRoutingModule = RouterModule.forRoot(routes);
|
||||||
|
@ -62,6 +62,8 @@ import { MessageDialogComponent } from './shared/message-dialog/message-dialog.c
|
|||||||
import { GameMasterTurntrackerComponent } from './components/game-master-turntracker/game-master-turntracker.component';
|
import { GameMasterTurntrackerComponent } from './components/game-master-turntracker/game-master-turntracker.component';
|
||||||
import { DragDropModule } from '@angular/cdk/drag-drop';
|
import { DragDropModule } from '@angular/cdk/drag-drop';
|
||||||
import { ChooseMonsterDialogComponent } from './components/choose-monster-dialog/choose-monster-dialog.component';
|
import { ChooseMonsterDialogComponent } from './components/choose-monster-dialog/choose-monster-dialog.component';
|
||||||
|
import { CreateCharacterComponent } from './components/create-character/create-character.component';
|
||||||
|
import { PersonalizeTemplateComponent } from './components/personalize-template/personalize-template.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@ -90,6 +92,8 @@ import { ChooseMonsterDialogComponent } from './components/choose-monster-dialog
|
|||||||
MessageDialogComponent,
|
MessageDialogComponent,
|
||||||
GameMasterTurntrackerComponent,
|
GameMasterTurntrackerComponent,
|
||||||
ChooseMonsterDialogComponent,
|
ChooseMonsterDialogComponent,
|
||||||
|
CreateCharacterComponent,
|
||||||
|
PersonalizeTemplateComponent,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule.withServerTransition({ appId: 'ng-cli-universal' }),
|
BrowserModule.withServerTransition({ appId: 'ng-cli-universal' }),
|
||||||
@ -151,6 +155,8 @@ import { ChooseMonsterDialogComponent } from './components/choose-monster-dialog
|
|||||||
MessageDialogComponent,
|
MessageDialogComponent,
|
||||||
GameMasterTurntrackerComponent,
|
GameMasterTurntrackerComponent,
|
||||||
ChooseMonsterDialogComponent,
|
ChooseMonsterDialogComponent,
|
||||||
|
CreateCharacterComponent,
|
||||||
|
PersonalizeTemplateComponent,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class AppModule {}
|
export class AppModule {}
|
||||||
|
@ -0,0 +1,85 @@
|
|||||||
|
@import '../../../styles.css';
|
||||||
|
|
||||||
|
mat-form-field {
|
||||||
|
color: #df7c0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
min-width: 150px;
|
||||||
|
max-width: 500px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
margin: 5%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.container h1 {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-container {
|
||||||
|
padding: 10px;
|
||||||
|
margin: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.primary-text {
|
||||||
|
height: 100px;
|
||||||
|
color: #df7c0f;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 1%;
|
||||||
|
margin: 1%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-divider--custom-style {
|
||||||
|
background-color: #9e8b6e;
|
||||||
|
box-shadow: 0 1px 0 0 #d8d8d8;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 400px) {
|
||||||
|
.container {
|
||||||
|
margin-left: 0%;
|
||||||
|
width: 300px;
|
||||||
|
padding-top: 5%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 400px) {
|
||||||
|
.container {
|
||||||
|
margin-left: 0%;
|
||||||
|
width: 300px;
|
||||||
|
padding-top: 5%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 401px) {
|
||||||
|
.container {
|
||||||
|
margin-left: 1%;
|
||||||
|
width: 450px;
|
||||||
|
padding-top: 10%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.container {
|
||||||
|
width: 550px;
|
||||||
|
padding-top: 5%;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
<div class="container">
|
||||||
|
<mat-icon matSuffix class="arrow-back arrow-select" (click)="onArrowBackClick()">arrow_back</mat-icon>
|
||||||
|
<div class="primary-text header">Create character</div>
|
||||||
|
<div class="primary-text header">Step 1 - Choose one of template</div>
|
||||||
|
<mat-divider class="mat-divider--custom-style"></mat-divider>
|
||||||
|
<mat-list>
|
||||||
|
<mat-list-item *ngFor="let character of charactersTemplateList">
|
||||||
|
<mat-icon mat-list-icon>account_circle</mat-icon>
|
||||||
|
<div mat-line (click)="onCharacterClick(character.id)">{{character.name}}</div>
|
||||||
|
<mat-icon matSuffix class="arrow-forward arrow-select" (click)="onCharacterClick(character.id)">arrow_forward</mat-icon>
|
||||||
|
<div mat-line> {{character.class}}, {{character.race}}</div>
|
||||||
|
<mat-divider class="mat-divider--custom-style"></mat-divider>
|
||||||
|
</mat-list-item>
|
||||||
|
</mat-list>
|
||||||
|
</div>
|
@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { CreateCharacterComponent } from './create-character.component';
|
||||||
|
|
||||||
|
describe('CreateCharacterComponent', () => {
|
||||||
|
let component: CreateCharacterComponent;
|
||||||
|
let fixture: ComponentFixture<CreateCharacterComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ CreateCharacterComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(CreateCharacterComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,66 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { Store } from '@ngrx/store';
|
||||||
|
import { AppState } from '../../store/models/app-state.model';
|
||||||
|
import { first } from 'rxjs/operators';
|
||||||
|
import { ErrorResponse } from '../../../types/ErrorResponse';
|
||||||
|
import { HttpErrorResponse } from '@angular/common/http';
|
||||||
|
import { CharacterFromTemplatesViewModel } from '../../../types/viewmodels/character-viewmodels/CharacterFromTemplatesViewModel';
|
||||||
|
import { CharacterService } from '../../../services/character.service';
|
||||||
|
import {
|
||||||
|
ChooseTemplateToCreateCharacter,
|
||||||
|
ClearCharacterId,
|
||||||
|
} from '../../store/actions/player.action';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-create-character',
|
||||||
|
templateUrl: './create-character.component.html',
|
||||||
|
styleUrls: ['./create-character.component.css'],
|
||||||
|
})
|
||||||
|
export class CreateCharacterComponent implements OnInit {
|
||||||
|
charactersTemplateList: CharacterFromTemplatesViewModel[];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private router: Router,
|
||||||
|
private store: Store<AppState>,
|
||||||
|
private characterService: CharacterService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.getTemplateCharacters();
|
||||||
|
}
|
||||||
|
|
||||||
|
onArrowBackClick() {
|
||||||
|
this.store
|
||||||
|
.select((s) => s.playerStore.characterId)
|
||||||
|
.pipe(first())
|
||||||
|
.subscribe((characterId) => {
|
||||||
|
if (characterId !== null) {
|
||||||
|
this.store.dispatch(new ClearCharacterId());
|
||||||
|
}
|
||||||
|
this.router.navigate(['select-character']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onCharacterClick(characterId: number) {
|
||||||
|
this.store.dispatch(new ChooseTemplateToCreateCharacter({ characterId }));
|
||||||
|
this.router.navigate(['create-character-step-2']);
|
||||||
|
}
|
||||||
|
|
||||||
|
getTemplateCharacters() {
|
||||||
|
this.characterService
|
||||||
|
.getTemplateCharacters()
|
||||||
|
.pipe(first())
|
||||||
|
.subscribe(
|
||||||
|
(charactersTemplateList) => {
|
||||||
|
this.charactersTemplateList = charactersTemplateList;
|
||||||
|
},
|
||||||
|
(error: ErrorResponse | HttpErrorResponse) => {
|
||||||
|
if (error instanceof HttpErrorResponse) {
|
||||||
|
error = error.error as ErrorResponse;
|
||||||
|
}
|
||||||
|
console.error(error.message);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,77 @@
|
|||||||
|
@import '../../../styles.css';
|
||||||
|
|
||||||
|
mat-form-field {
|
||||||
|
color: #df7c0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
min-width: 150px;
|
||||||
|
max-width: 500px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
margin: 5%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.container h1 {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-container {
|
||||||
|
padding: 10px;
|
||||||
|
margin: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.primary-text {
|
||||||
|
height: 100px;
|
||||||
|
color: #df7c0f;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 1%;
|
||||||
|
margin: 1%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-divider--custom-style {
|
||||||
|
background-color: #9e8b6e;
|
||||||
|
box-shadow: 0 1px 0 0 #d8d8d8;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 400px) {
|
||||||
|
.container {
|
||||||
|
margin-left: 0%;
|
||||||
|
width: 300px;
|
||||||
|
padding-top: 5%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 401px) {
|
||||||
|
.container {
|
||||||
|
margin-left: 1%;
|
||||||
|
width: 450px;
|
||||||
|
padding-top: 10%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.container {
|
||||||
|
width: 550px;
|
||||||
|
padding-top: 5%;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
<div [formGroup]="personalizeTemplateFormGroup" class="container">
|
||||||
|
<mat-icon matSuffix class="arrow-back arrow-select" (click)="onArrowBackClick()">arrow_back</mat-icon>
|
||||||
|
<div class="primary-text header">Create character</div>
|
||||||
|
<div class="primary-text header">Step 2 - Personalize template</div>
|
||||||
|
<mat-form-field class="form-container">
|
||||||
|
<input
|
||||||
|
id="newName"
|
||||||
|
matInput
|
||||||
|
formControlName="newName"
|
||||||
|
placeholder="New character name"
|
||||||
|
type="text"
|
||||||
|
name="newName">
|
||||||
|
<mat-error *ngIf="personalizeTemplateFormGroup?.get('newName').hasError('required')">
|
||||||
|
If the field is left blank, your character will have the default name
|
||||||
|
</mat-error>
|
||||||
|
<mat-icon matSuffix>person</mat-icon>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-error *ngIf="apiError">
|
||||||
|
{{apiErrorMessage}}
|
||||||
|
</mat-error>
|
||||||
|
<button
|
||||||
|
id="create-character-submit-button"
|
||||||
|
mat-raised-button
|
||||||
|
class="btn-primary form-container"
|
||||||
|
(click)="onCreateCharacterButton()">
|
||||||
|
CONTINUE
|
||||||
|
</button>
|
||||||
|
</div>
|
@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { PersonalizeTemplateComponent } from './personalize-template.component';
|
||||||
|
|
||||||
|
describe('PersonalizeTemplateComponent', () => {
|
||||||
|
let component: PersonalizeTemplateComponent;
|
||||||
|
let fixture: ComponentFixture<PersonalizeTemplateComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ PersonalizeTemplateComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(PersonalizeTemplateComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,71 @@
|
|||||||
|
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
import { CharacterService } from '../../../services/character.service';
|
||||||
|
import { ErrorResponse } from '../../../types/ErrorResponse';
|
||||||
|
import { HttpErrorResponse } from '@angular/common/http';
|
||||||
|
import { AppState } from '../../store/models/app-state.model';
|
||||||
|
import { Store } from '@ngrx/store';
|
||||||
|
import { first } from 'rxjs/operators';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-personalize-template',
|
||||||
|
templateUrl: './personalize-template.component.html',
|
||||||
|
styleUrls: ['./personalize-template.component.css'],
|
||||||
|
})
|
||||||
|
export class PersonalizeTemplateComponent implements OnInit {
|
||||||
|
allSubscriptions = new Subscription();
|
||||||
|
apiError = false;
|
||||||
|
apiErrorMessage = '';
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private router: Router,
|
||||||
|
private formBuilder: FormBuilder,
|
||||||
|
private characterService: CharacterService,
|
||||||
|
private store: Store<AppState>
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public personalizeTemplateFormGroup: FormGroup = this.formBuilder.group({
|
||||||
|
newName: ['', [Validators.required]],
|
||||||
|
});
|
||||||
|
|
||||||
|
ngOnInit() {}
|
||||||
|
|
||||||
|
onCreateCharacterButton() {
|
||||||
|
this.store
|
||||||
|
.select((s) => {
|
||||||
|
return {
|
||||||
|
userId: s.appStore.userId,
|
||||||
|
characterId: s.playerStore.characterId,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.pipe(first())
|
||||||
|
.subscribe((result) => {
|
||||||
|
this.characterService
|
||||||
|
.createCharacterFromTemplate(
|
||||||
|
result.characterId,
|
||||||
|
result.userId,
|
||||||
|
this.personalizeTemplateFormGroup.value['newName']
|
||||||
|
)
|
||||||
|
.pipe(first())
|
||||||
|
.subscribe(
|
||||||
|
() => {
|
||||||
|
this.router.navigate(['select-character']);
|
||||||
|
},
|
||||||
|
(error: ErrorResponse | HttpErrorResponse) => {
|
||||||
|
console.error(error);
|
||||||
|
if (error instanceof HttpErrorResponse) {
|
||||||
|
error = error.error as ErrorResponse;
|
||||||
|
}
|
||||||
|
this.apiError = true;
|
||||||
|
this.apiErrorMessage = error.message;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onArrowBackClick() {
|
||||||
|
this.router.navigate(['create-character-step-1']);
|
||||||
|
}
|
||||||
|
}
|
@ -13,22 +13,20 @@ import { AppState } from '../../store/models/app-state.model';
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'app-player-weapons-table',
|
selector: 'app-player-weapons-table',
|
||||||
templateUrl: './player-weapons-table.component.html',
|
templateUrl: './player-weapons-table.component.html',
|
||||||
styleUrls: ['./player-weapons-table.component.css']
|
styleUrls: ['./player-weapons-table.component.css'],
|
||||||
})
|
})
|
||||||
export class PlayerWeaponsTableComponent implements OnInit {
|
export class PlayerWeaponsTableComponent implements OnInit {
|
||||||
weapons: WeaponViewModel[];
|
weapons: WeaponViewModel[];
|
||||||
displayedColumns: string[] = [
|
displayedColumns: string[] = ['name', 'weaponType', 'weight', 'cost'];
|
||||||
'name',
|
|
||||||
'weaponType',
|
|
||||||
'weight',
|
|
||||||
'cost',
|
|
||||||
];
|
|
||||||
dataSource: MatTableDataSource<WeaponViewModel>;
|
dataSource: MatTableDataSource<WeaponViewModel>;
|
||||||
|
|
||||||
@ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
|
@ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
|
||||||
@ViewChild(MatSort, { static: true }) sort: MatSort;
|
@ViewChild(MatSort, { static: true }) sort: MatSort;
|
||||||
|
|
||||||
constructor(private store: Store<AppState>, private equipmentService: EquipmentService) {}
|
constructor(
|
||||||
|
private store: Store<AppState>,
|
||||||
|
private equipmentService: EquipmentService
|
||||||
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.getCharacterWeapons();
|
this.getCharacterWeapons();
|
||||||
@ -40,7 +38,7 @@ export class PlayerWeaponsTableComponent implements OnInit {
|
|||||||
.pipe(first())
|
.pipe(first())
|
||||||
.subscribe((characterId) => {
|
.subscribe((characterId) => {
|
||||||
this.equipmentService
|
this.equipmentService
|
||||||
.getCharacterWeapons(1)
|
.getCharacterWeapons(characterId)
|
||||||
.pipe(first())
|
.pipe(first())
|
||||||
.subscribe(
|
.subscribe(
|
||||||
(result) => {
|
(result) => {
|
||||||
|
@ -64,6 +64,17 @@ input {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
|
.container {
|
||||||
|
width: 550px;
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
.form-container {
|
||||||
|
padding: 10px;
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1800px) {
|
||||||
.container {
|
.container {
|
||||||
width: 550px;
|
width: 550px;
|
||||||
padding-top: 5%;
|
padding-top: 5%;
|
||||||
|
@ -38,7 +38,8 @@
|
|||||||
required
|
required
|
||||||
placeholder="Confirm Password"
|
placeholder="Confirm Password"
|
||||||
type="password"
|
type="password"
|
||||||
name="confirmPassword"/>
|
name="confirmPassword"
|
||||||
|
(keyup.enter)="Register()"/>
|
||||||
<mat-icon matSuffix >lock</mat-icon>
|
<mat-icon matSuffix >lock</mat-icon>
|
||||||
<mat-error *ngIf="signUpFormGroup?.get('newAccount')['controls']?.confirmPassword?.hasError('required')">
|
<mat-error *ngIf="signUpFormGroup?.get('newAccount')['controls']?.confirmPassword?.hasError('required')">
|
||||||
Confirm your password
|
Confirm your password
|
||||||
|
@ -69,3 +69,8 @@ input {
|
|||||||
padding-top: 5%;
|
padding-top: 5%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mat-divider--custom-style {
|
||||||
|
background-color: #9e8b6e;
|
||||||
|
box-shadow: 0 1px 0 0 #d8d8d8;
|
||||||
|
}
|
||||||
|
@ -1,15 +1,28 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<mat-icon matSuffix class="arrow-back arrow-select" (click)="onArrowBackClick()">arrow_back</mat-icon>
|
<mat-icon matSuffix class="arrow-back arrow-select" (click)="onArrowBackClick()">arrow_back</mat-icon>
|
||||||
|
<div *ngIf="charactersList != null && charactersList.length > 0">
|
||||||
<div class="primary-text header">Select character</div>
|
<div class="primary-text header">Select character</div>
|
||||||
|
<mat-divider class="mat-divider--custom-style"></mat-divider>
|
||||||
<mat-list>
|
<mat-list>
|
||||||
<mat-divider></mat-divider>
|
|
||||||
<mat-list-item *ngFor="let character of charactersList">
|
<mat-list-item *ngFor="let character of charactersList">
|
||||||
<mat-icon mat-list-icon>account_circle</mat-icon>
|
<mat-icon mat-list-icon>account_circle</mat-icon>
|
||||||
<div mat-line>{{character.name}}</div>
|
<div mat-line (click)="onCharacterClick(character.id)">{{character.name}}</div>
|
||||||
<mat-icon matSuffix class="arrow-forward arrow-select" (click)="onCharacterClick(character.id)">arrow_forward</mat-icon>
|
<mat-icon matSuffix class="arrow-forward arrow-select" (click)="onCharacterClick(character.id)">arrow_forward</mat-icon>
|
||||||
<div mat-line> {{character.className}} level: {{character.level}}</div>
|
<div mat-line> {{character.className}} level: {{character.level}}</div>
|
||||||
<mat-divider></mat-divider>
|
<mat-divider class="mat-divider--custom-style"></mat-divider>
|
||||||
</mat-list-item>
|
</mat-list-item>
|
||||||
</mat-list>
|
</mat-list>
|
||||||
|
<div class="primary-text header">or</div>
|
||||||
|
</div>
|
||||||
|
<div class="primary-text header">Create character</div>
|
||||||
|
<mat-divider class="mat-divider--custom-style"></mat-divider>
|
||||||
|
<mat-list>
|
||||||
|
<mat-list-item>
|
||||||
|
<mat-icon mat-list-icon>account_circle</mat-icon>
|
||||||
|
<div mat-line>Create character</div>
|
||||||
|
<mat-icon matSuffix class="arrow-forward arrow-select" (click)="onCreateCharacter()">arrow_forward</mat-icon>
|
||||||
|
</mat-list-item>
|
||||||
|
</mat-list>
|
||||||
|
<mat-divider class="mat-divider--custom-style"></mat-divider>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1,44 +1,57 @@
|
|||||||
import {Component, OnInit} from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import {first} from 'rxjs/operators';
|
import { first } from 'rxjs/operators';
|
||||||
import {ClearUserId} from '../../store/actions/app.actions';
|
import { ClearUserId } from '../../store/actions/app.actions';
|
||||||
import {ErrorResponse} from '../../../types/ErrorResponse';
|
import { ErrorResponse } from '../../../types/ErrorResponse';
|
||||||
import {HttpErrorResponse} from '@angular/common/http';
|
import { HttpErrorResponse } from '@angular/common/http';
|
||||||
import {Store} from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
import {AppState} from '../../store/models/app-state.model';
|
import { AppState } from '../../store/models/app-state.model';
|
||||||
import {CharacterService} from '../../../services/character.service';
|
import { CharacterService } from '../../../services/character.service';
|
||||||
import {CharacterForLoginViewModel} from '../../../types/viewmodels/character-viewmodels/CharacterForLoginViewModel';
|
import { CharacterForLoginViewModel } from '../../../types/viewmodels/character-viewmodels/CharacterForLoginViewModel';
|
||||||
import {AddCharacterId} from '../../store/actions/player.action';
|
import { AddCharacterId } from '../../store/actions/player.action';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-select-character',
|
selector: 'app-select-character',
|
||||||
templateUrl: './select-character.component.html',
|
templateUrl: './select-character.component.html',
|
||||||
styleUrls: ['./select-character.component.css']
|
styleUrls: ['./select-character.component.css'],
|
||||||
})
|
})
|
||||||
export class SelectCharacterComponent implements OnInit {
|
export class SelectCharacterComponent implements OnInit {
|
||||||
charactersList: CharacterForLoginViewModel[];
|
charactersList: CharacterForLoginViewModel[];
|
||||||
|
|
||||||
constructor(private router: Router, private store: Store<AppState>, private characterService: CharacterService) {}
|
constructor(
|
||||||
|
private router: Router,
|
||||||
|
private store: Store<AppState>,
|
||||||
|
private characterService: CharacterService
|
||||||
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.getUserCharactersList();
|
this.getUserCharactersList();
|
||||||
}
|
}
|
||||||
|
|
||||||
getUserCharactersList() {
|
getUserCharactersList() {
|
||||||
this.store.select(s => s.appStore.userId).pipe(first()).subscribe((userId) => {
|
this.store
|
||||||
this.characterService.getUserCharactersList(userId).pipe(first()).subscribe((charactersList) => {
|
.select((s) => s.appStore.userId)
|
||||||
this.charactersList = charactersList;
|
.pipe(first())
|
||||||
}, (error: ErrorResponse | HttpErrorResponse) => {
|
.subscribe((userId) => {
|
||||||
if (error instanceof HttpErrorResponse) {
|
this.characterService
|
||||||
error = error.error as ErrorResponse;
|
.getUserCharactersList(userId)
|
||||||
}
|
.pipe(first())
|
||||||
console.error(error.message);
|
.subscribe(
|
||||||
} );
|
(charactersList) => {
|
||||||
});
|
this.charactersList = charactersList;
|
||||||
|
},
|
||||||
|
(error: ErrorResponse | HttpErrorResponse) => {
|
||||||
|
if (error instanceof HttpErrorResponse) {
|
||||||
|
error = error.error as ErrorResponse;
|
||||||
|
}
|
||||||
|
console.error(error.message);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onCharacterClick(characterId: number) {
|
onCharacterClick(characterId: number) {
|
||||||
this.store.dispatch(new AddCharacterId({characterId}));
|
this.store.dispatch(new AddCharacterId({ characterId }));
|
||||||
this.router.navigate(['player']);
|
this.router.navigate(['player']);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,4 +59,8 @@ export class SelectCharacterComponent implements OnInit {
|
|||||||
this.store.dispatch(new ClearUserId());
|
this.store.dispatch(new ClearUserId());
|
||||||
this.router.navigate(['login']);
|
this.router.navigate(['login']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onCreateCharacter() {
|
||||||
|
this.router.navigate(['create-character-step-1']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@
|
|||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
.container {
|
.container {
|
||||||
padding-top: 10%;
|
padding-top: 6%;
|
||||||
}
|
}
|
||||||
.button {
|
.button {
|
||||||
margin: 20px;
|
margin: 20px;
|
||||||
@ -59,3 +59,9 @@
|
|||||||
font-size: 40px;
|
font-size: 40px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1800px) {
|
||||||
|
.container {
|
||||||
|
padding-top: 10%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -64,6 +64,17 @@ input {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
|
.container {
|
||||||
|
width: 550px;
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
.form-container {
|
||||||
|
padding: 10px;
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1800px) {
|
||||||
.container {
|
.container {
|
||||||
width: 550px;
|
width: 550px;
|
||||||
padding-top: 5%;
|
padding-top: 5%;
|
||||||
|
@ -26,7 +26,9 @@
|
|||||||
required
|
required
|
||||||
placeholder="Password"
|
placeholder="Password"
|
||||||
type="password"
|
type="password"
|
||||||
name="password"/>
|
name="password"
|
||||||
|
(keyup.enter)="onLoginButtonClick()"
|
||||||
|
/>
|
||||||
<mat-icon matSuffix>lock</mat-icon>
|
<mat-icon matSuffix>lock</mat-icon>
|
||||||
<mat-error *ngIf="signInFormGroup?.get('signIn')['controls']?.password?.hasError('required')">
|
<mat-error *ngIf="signInFormGroup?.get('signIn')['controls']?.password?.hasError('required')">
|
||||||
Password is required
|
Password is required
|
||||||
|
@ -2,7 +2,9 @@ import {Action} from '@ngrx/store';
|
|||||||
|
|
||||||
export enum PlayerActionTypes {
|
export enum PlayerActionTypes {
|
||||||
ADD_CHARACTER_ID= '[PLAYER] Add character id',
|
ADD_CHARACTER_ID= '[PLAYER] Add character id',
|
||||||
CLEAR_CHARACTER_ID = '[PLAYER] Clear character id'
|
CLEAR_CHARACTER_ID = '[PLAYER] Clear character id',
|
||||||
|
CHOOSE_TEMPLATE_TO_CREATE_CHARACTER = '[PLAYER] Choose template to create character',
|
||||||
|
CREATED_CHARACTER_FROM_TEMPLATE = '[PLAYER] Created character from template'
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AddCharacterId implements Action {
|
export class AddCharacterId implements Action {
|
||||||
@ -21,4 +23,20 @@ export class ClearCharacterId implements Action {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PlayerAction = AddCharacterId | ClearCharacterId;
|
export class ChooseTemplateToCreateCharacter implements Action {
|
||||||
|
readonly type = PlayerActionTypes.CHOOSE_TEMPLATE_TO_CREATE_CHARACTER;
|
||||||
|
|
||||||
|
constructor(public payload: {characterId: number}) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CreateCharacterFromTemplate implements Action {
|
||||||
|
readonly type = PlayerActionTypes.CREATED_CHARACTER_FROM_TEMPLATE;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PlayerAction = AddCharacterId | ClearCharacterId | ChooseTemplateToCreateCharacter | CreateCharacterFromTemplate;
|
||||||
|
@ -9,6 +9,10 @@ export function PlayerReducer(state: PlayerStoreModel = initialState, action: Pl
|
|||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case PlayerActionTypes.ADD_CHARACTER_ID:
|
case PlayerActionTypes.ADD_CHARACTER_ID:
|
||||||
return {...state, characterId: action.payload.characterId};
|
return {...state, characterId: action.payload.characterId};
|
||||||
|
case PlayerActionTypes.CHOOSE_TEMPLATE_TO_CREATE_CHARACTER:
|
||||||
|
return {...state, characterId: action.payload.characterId};
|
||||||
|
case PlayerActionTypes.CREATED_CHARACTER_FROM_TEMPLATE:
|
||||||
|
return {...state};
|
||||||
case PlayerActionTypes.CLEAR_CHARACTER_ID:
|
case PlayerActionTypes.CLEAR_CHARACTER_ID:
|
||||||
return { characterId: null};
|
return { characterId: null};
|
||||||
default:
|
default:
|
||||||
|
@ -1,16 +1,18 @@
|
|||||||
import {Inject, Injectable} from '@angular/core';
|
import { Inject, Injectable } from '@angular/core';
|
||||||
import {HttpClient, HttpParams} from '@angular/common/http';
|
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||||
import {Observable, of, throwError} from 'rxjs';
|
import { Observable, of, throwError } from 'rxjs';
|
||||||
import {ErrorResponse} from '../types/ErrorResponse';
|
import { ErrorResponse } from '../types/ErrorResponse';
|
||||||
import {Either} from '../types/Either';
|
import { Either } from '../types/Either';
|
||||||
import {CharacterForLoginViewModel} from '../types/viewmodels/character-viewmodels/CharacterForLoginViewModel';
|
import { CharacterForLoginViewModel } from '../types/viewmodels/character-viewmodels/CharacterForLoginViewModel';
|
||||||
import {switchMap, retry} from 'rxjs/operators';
|
import { switchMap, retry } from 'rxjs/operators';
|
||||||
import {LoggedCharactersViewModel} from '../types/viewmodels/character-viewmodels/LoggedCharactersViewModel';
|
import { LoggedCharactersViewModel } from '../types/viewmodels/character-viewmodels/LoggedCharactersViewModel';
|
||||||
import {CharacterStatsViewModel} from "../types/viewmodels/character-viewmodels/CharacterStatsViewModel";
|
import { CharacterStatsViewModel } from '../types/viewmodels/character-viewmodels/CharacterStatsViewModel';
|
||||||
|
import { CharacterFromTemplatesViewModel } from '../types/viewmodels/character-viewmodels/CharacterFromTemplatesViewModel';
|
||||||
|
import { SuccessResponse } from '../types/SuccessResponse';
|
||||||
|
|
||||||
Injectable({
|
Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root',
|
||||||
})
|
});
|
||||||
export class CharacterService {
|
export class CharacterService {
|
||||||
private baseUrl = 'api/character/';
|
private baseUrl = 'api/character/';
|
||||||
constructor(private http: HttpClient, @Inject('BASE_URL') baseUrl: string) {
|
constructor(private http: HttpClient, @Inject('BASE_URL') baseUrl: string) {
|
||||||
@ -18,42 +20,101 @@ export class CharacterService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getLoggedCharacters(): Observable<LoggedCharactersViewModel[]> {
|
getLoggedCharacters(): Observable<LoggedCharactersViewModel[]> {
|
||||||
return this.http.get<Either<LoggedCharactersViewModel[], ErrorResponse>>(this.baseUrl + 'loggedCharacters').pipe(
|
return this.http
|
||||||
switchMap(response => {
|
.get<Either<LoggedCharactersViewModel[], ErrorResponse>>(
|
||||||
if (response.isLeft) {
|
this.baseUrl + 'loggedCharacters'
|
||||||
return of(response.left);
|
)
|
||||||
} else {
|
.pipe(
|
||||||
return throwError(response.right);
|
switchMap((response) => {
|
||||||
}
|
if (response.isLeft) {
|
||||||
}),
|
return of(response.left);
|
||||||
retry(3)
|
} else {
|
||||||
);
|
return throwError(response.right);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
retry(3)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getUserCharactersList(userId: number): Observable<CharacterForLoginViewModel[]> {
|
getUserCharactersList(
|
||||||
const params = new HttpParams().set('userId', userId.toString())
|
userId: number
|
||||||
return this.http.get<Either<CharacterForLoginViewModel[], ErrorResponse>>(this.baseUrl + 'userCharactersList', {params}).pipe(
|
): Observable<CharacterForLoginViewModel[]> {
|
||||||
switchMap(response => {
|
const params = new HttpParams().set('userId', userId.toString());
|
||||||
if (response.isLeft) {
|
return this.http
|
||||||
return of(response.left);
|
.get<Either<CharacterForLoginViewModel[], ErrorResponse>>(
|
||||||
} else {
|
this.baseUrl + 'userCharactersList',
|
||||||
return throwError(response.right);
|
{ params }
|
||||||
}
|
)
|
||||||
})
|
.pipe(
|
||||||
);
|
switchMap((response) => {
|
||||||
|
if (response.isLeft) {
|
||||||
|
return of(response.left);
|
||||||
|
} else {
|
||||||
|
return throwError(response.right);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getCharacterStats(characterId: number): Observable<CharacterStatsViewModel[]> {
|
getCharacterStats(
|
||||||
const params = new HttpParams().set('characterId', characterId.toString())
|
characterId: number
|
||||||
return this.http.get<Either<CharacterStatsViewModel[], ErrorResponse>>( this.baseUrl + 'characterStats', {params}).pipe(
|
): Observable<CharacterStatsViewModel[]> {
|
||||||
switchMap( response => {
|
const params = new HttpParams().set('characterId', characterId.toString());
|
||||||
if (response.isLeft) {
|
return this.http
|
||||||
return of(response.left);
|
.get<Either<CharacterStatsViewModel[], ErrorResponse>>(
|
||||||
} else {
|
this.baseUrl + 'characterStats',
|
||||||
return throwError(response.right);
|
{ params }
|
||||||
}
|
)
|
||||||
})
|
.pipe(
|
||||||
);
|
switchMap((response) => {
|
||||||
|
if (response.isLeft) {
|
||||||
|
return of(response.left);
|
||||||
|
} else {
|
||||||
|
return throwError(response.right);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getTemplateCharacters(): Observable<CharacterFromTemplatesViewModel[]> {
|
||||||
|
return this.http
|
||||||
|
.get<Either<CharacterFromTemplatesViewModel[], ErrorResponse>>(
|
||||||
|
this.baseUrl + 'getTemplateCharacters'
|
||||||
|
)
|
||||||
|
.pipe(
|
||||||
|
switchMap((response) => {
|
||||||
|
if (response.isLeft) {
|
||||||
|
return of(response.left);
|
||||||
|
} else {
|
||||||
|
return throwError(response.right);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
createCharacterFromTemplate(
|
||||||
|
characterId: number,
|
||||||
|
userId: number,
|
||||||
|
newName: string
|
||||||
|
): Observable<SuccessResponse> {
|
||||||
|
const params = new HttpParams()
|
||||||
|
.set('characterId', characterId.toString())
|
||||||
|
.set('userId', userId.toString())
|
||||||
|
.set('newName', newName);
|
||||||
|
return this.http
|
||||||
|
.post<Either<SuccessResponse, ErrorResponse>>(
|
||||||
|
this.baseUrl + 'createCharacterFromTemplate',
|
||||||
|
null,
|
||||||
|
{ params }
|
||||||
|
)
|
||||||
|
.pipe(
|
||||||
|
switchMap((response) => {
|
||||||
|
if (response.isLeft) {
|
||||||
|
return of(response.left);
|
||||||
|
} else {
|
||||||
|
return throwError(response.right);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,10 @@
|
|||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.arrow-forward:hover {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
/* icon in mat-form-field*/
|
/* icon in mat-form-field*/
|
||||||
::ng-deep mat-form-field {
|
::ng-deep mat-form-field {
|
||||||
color: #df7c0f !important;
|
color: #df7c0f !important;
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
export interface CharacterFromTemplatesViewModel {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
race: string;
|
||||||
|
class: number;
|
||||||
|
}
|
@ -116,7 +116,7 @@ namespace SessionCompanion.Controllers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>liste podstawowych informacji o postaciach z templatki</returns>
|
/// <returns>liste podstawowych informacji o postaciach z templatki</returns>
|
||||||
[HttpGet("getTemplateCharacters")]
|
[HttpGet("getTemplateCharacters")]
|
||||||
public async Task<Either<ErrorResponse, List<CharacterFromTemplatesSimpleViewModel>>> GetTemplateCharacters()
|
public async Task<Either<List<CharacterFromTemplatesSimpleViewModel>, ErrorResponse>> GetTemplateCharacters()
|
||||||
{
|
{
|
||||||
var races = _raceService.Get().ToList();
|
var races = _raceService.Get().ToList();
|
||||||
var classes = _classService.Get().ToList();
|
var classes = _classService.Get().ToList();
|
||||||
|
Loading…
Reference in New Issue
Block a user