Release v1.9.3 RC2 (#58)

release/v1.9.7 v1.9.3
hardi 2019-02-27 23:13:23 +01:00 committed by Gogs
parent df6abd39aa
commit 35e8b14a34
37 changed files with 624 additions and 155 deletions

View File

@ -14,7 +14,7 @@
</mat-sidenav> </mat-sidenav>
</mat-sidenav-container> </mat-sidenav-container>
<div (click)="sidenav.close()"> <div class="app-content-container" (click)="sidenav.close()">
<router-outlet></router-outlet> <router-outlet></router-outlet>
<div id="left"> <div id="left">
<router-outlet name="left"></router-outlet> <router-outlet name="left"></router-outlet>

View File

@ -1,9 +1,9 @@
@import "style/load-indicator.scss"; @import "style/load-indicator.scss";
mat-sidenav-container, mat-sidenav-content, mat-sidenav { mat-sidenav-container, mat-sidenav-content, mat-sidenav {
height: 100vh; height: calc(100vh - 50px);
min-height: fit-content;
display: inline; display: inline;
background: #fdfdfd;
} }
mat-sidenav { mat-sidenav {
@ -12,6 +12,12 @@ mat-sidenav {
width: 250px; width: 250px;
} }
.app-content-container {
overflow-x: auto;
position: relative;
min-height: calc(100vh - 50px)
}
#scrollTopBtn { #scrollTopBtn {
position: fixed; position: fixed;
bottom: 20px; bottom: 20px;
@ -22,4 +28,14 @@ mat-sidenav {
&:hover { &:hover {
background: #101010; background: #101010;
} }
@media all and (max-width: 959px) {
bottom: 0;
left: 0;
width: 100vw;
border-radius: 3px 3px 0 0;
background: #101010;
min-height: 34px;
height: 3vw;
}
} }

View File

@ -44,6 +44,7 @@ export const RouteConfig = {
export const BaseConfig = { export const BaseConfig = {
responsive: { responsive: {
breakpointSmallPx: 599,
breakpointPx: 959, breakpointPx: 959,
}, },
i18n: { i18n: {

View File

@ -3,6 +3,21 @@
padding: 6px 0; padding: 6px 0;
} }
@media all and (max-width: 599px) {
.squad-layout {
padding: 1vh 1vw;
}
img {
right: 3vw;
max-width: 27%;
}
div.name-cell {
margin-left: 2vw;
}
}
.squad-layout > div { .squad-layout > div {
clear: both; clear: both;
} }
@ -24,7 +39,7 @@
} }
.squad-member-count { .squad-member-count {
float:left; float: left;
color: whitesmoke; color: whitesmoke;
margin-left: 42px margin-left: 42px
} }
@ -46,7 +61,10 @@
img { img {
position: absolute; position: absolute;
margin-top: 8px; margin-top: 8px;
margin-left: 25px; margin-left: 2%;
@media all and (max-width: 959px) {
margin-left: 0;
}
} }
.title { .title {
@ -57,7 +75,7 @@ img {
.name-cell { .name-cell {
display: inherit; display: inherit;
margin-left: 200px; margin-left: 38%;
} }

View File

@ -1,8 +1,14 @@
<div style="width: 1100px; margin:auto; position: relative;"> <div class="army-content-container">
<h1>{{'public.army.headline' | translate}}</h1> <h1>{{'public.army.headline' | translate}}</h1>
<div class="pull-left army-column"> <div class="pull-left army-column" *ngIf="!isSmallLayout || singleViewSelected === fraction.BLUFOR">
<h3 class="army-head" [style.color]="fraction.COLOR_BLUFOR">{{fraction.BLUFOR}}</h3> <h3 class="army-head" [style.color]="fraction.COLOR_BLUFOR">
{{fraction.BLUFOR}}
<button mat-icon-button class="switch-btn-blufor"
(click)="singleViewSwitch(fraction.OPFOR)">
<mat-icon svgIcon="chevron-right"></mat-icon>
</button>
</h3>
<cc-army-squad *ngFor="let squad of army[0].squads" <cc-army-squad *ngFor="let squad of army[0].squads"
[squad]="squad" [squad]="squad"
squadFraction="BLUFOR" squadFraction="BLUFOR"
@ -11,8 +17,14 @@
<div class="member-count">{{'public.army.members' | translate}} {{army[0].memberCount}}</div> <div class="member-count">{{'public.army.members' | translate}} {{army[0].memberCount}}</div>
</div> </div>
<div class="pull-right army-column"> <div class="pull-right army-column" *ngIf="!isSmallLayout || singleViewSelected === fraction.OPFOR">
<h3 class="army-head" [style.color]="fraction.COLOR_OPFOR">{{fraction.OPFOR}}</h3> <h3 class="army-head" [style.color]="fraction.COLOR_OPFOR">
<button mat-icon-button class="switch-btn-opfor"
(click)="singleViewSwitch(fraction.BLUFOR)">
<mat-icon svgIcon="chevron-right"></mat-icon>
</button>
{{fraction.OPFOR}}
</h3>
<cc-army-squad *ngFor="let squad of army[1].squads" <cc-army-squad *ngFor="let squad of army[1].squads"
[squad]="squad" [squad]="squad"
squadFraction="OPFOR" squadFraction="OPFOR"

View File

@ -1,5 +1,63 @@
@import url('../style/background-image.scss'); @import url('../style/background-image.scss');
.army-content-container {
width: 90%;
max-width: 1100px;
margin: auto;
position: relative;
}
@media all and (max-width: 599px) {
h1 {
font-size: 26px;
margin: 0.5em 0;
}
.army-content-container {
width: 100%;
}
div.army-column {
width: 100%!important;
h3 {
background: #222;
font-size: 18px;
margin: 0.2em 0;
padding: 1.5vh 0;
button {
position: absolute;
margin-top: -10px;
display: inline-block;
&.switch-btn-blufor {
right: 5vw;
}
&.switch-btn-opfor {
transform: rotate(180deg);
left: 5vw;
}
mat-icon {
width: 2.5em;
height: 2.5em;
}
}
}
}
}
@media all and (max-width: 959px) {
div.army-column {
width: 49%;
}
.army-content-container {
width: 98%;
}
}
.army-column { .army-column {
width: 45%; width: 45%;
} }
@ -8,6 +66,10 @@ h1 {
text-align: center; text-align: center;
} }
h3 button {
display: none;
}
.army-head { .army-head {
font-weight: bolder; font-weight: bolder;
text-align: center text-align: center

View File

@ -3,6 +3,8 @@ import {Army} from '../models/model-interfaces';
import {ArmyService} from '../services/army-service/army.service'; import {ArmyService} from '../services/army-service/army.service';
import {ActivatedRoute, Router} from '@angular/router'; import {ActivatedRoute, Router} from '@angular/router';
import {Fraction} from '../utils/fraction.enum'; import {Fraction} from '../utils/fraction.enum';
import {BaseConfig} from '../app.config';
import {Observable} from 'rxjs';
@Component({ @Component({
@ -12,9 +14,13 @@ import {Fraction} from '../utils/fraction.enum';
}) })
export class ArmyComponent implements OnInit { export class ArmyComponent implements OnInit {
readonly fraction = Fraction;
army: Army[] = [{}, {}]; army: Army[] = [{}, {}];
readonly fraction = Fraction; singleViewSelected = this.fraction.BLUFOR;
isSmallLayout;
constructor(private router: Router, constructor(private router: Router,
private route: ActivatedRoute, private route: ActivatedRoute,
@ -25,9 +31,18 @@ export class ArmyComponent implements OnInit {
this.armyService.getArmies().subscribe(army => { this.armyService.getArmies().subscribe(army => {
this.army = army; this.army = army;
}); });
this.isSmallLayout = (window.innerWidth <= BaseConfig.responsive.breakpointSmallPx);
Observable.fromEvent(window, 'resize').subscribe(event => {
this.isSmallLayout = event.target['innerWidth'] <= BaseConfig.responsive.breakpointSmallPx;
});
}; };
select(memberId) { select(memberId) {
this.router.navigate(['member', memberId], {relativeTo: this.route}); this.router.navigate(['member', memberId], {relativeTo: this.route});
} }
singleViewSwitch(switchFraction: Fraction) {
this.singleViewSelected = switchFraction;
}
} }

View File

@ -1,5 +1,5 @@
<mat-toolbar color="primary"> <mat-toolbar color="primary">
<div fxHide.gt-sm style="position:absolute;"> <div fxHide.gt-sm>
<button mat-icon-button (click)="onToggleSidenav()"> <button mat-icon-button (click)="onToggleSidenav()">
<mat-icon svgIcon="menu"></mat-icon> <mat-icon svgIcon="menu"></mat-icon>
</button> </button>

View File

@ -1,11 +1,12 @@
.form-signin { .form-signin {
max-width: 330px; width: 100%;
padding: 15px; padding: 15px;
margin: 0 auto; margin: 0 auto;
} }
.form-signin > .row { .form-signin > .row {
width: 400px; max-width: 400px;
margin: auto;
} }
.form-signin-heading { .form-signin-heading {

View File

@ -8,11 +8,29 @@ h1 {
.decoration-overview-container { .decoration-overview-container {
position: relative; position: relative;
z-index: 0; z-index: 0;
margin: auto 140px 30px; margin: auto 4vw 0;
}
@media all and (max-width: 959px) { @media all and (max-width: 959px) {
.decoration-overview-container {
margin: auto; margin: auto;
} }
h1 {
font-size: 26px;
margin: 0.5em 0;
}
:host {
margin-bottom: -50vh;
}
}
:host /deep/ .mat-tab-group {
@media all and (max-width: 959px) {
float: left;
width: 100vw;
}
} }
:host /deep/ .mat-tab-header { :host /deep/ .mat-tab-header {
@ -41,6 +59,10 @@ h1 {
height: calc(100vh - 241px); height: calc(100vh - 241px);
min-width: 560px; min-width: 560px;
padding: 1em 1.5em; padding: 1em 1.5em;
@media all and (max-width: 959px) {
padding: 0;
}
} }
.fraction-side-bar { .fraction-side-bar {
@ -50,26 +72,44 @@ h1 {
height: calc(100vh - 192px); height: calc(100vh - 192px);
background: #222222;; background: #222222;;
box-shadow: #666666 2px 2px 8px; box-shadow: #666666 2px 2px 8px;
}
.fraction-side-bar > div { & > div:hover {
text-align: center; color: #ffffff;
padding: 12px 25px; }
margin-bottom: 15px;
font-size: 16px;
color: #666;
cursor: pointer;
border-top: 1px solid #666666;
border-bottom: 1px solid #666666;
}
.fraction-side-bar > div:first-child { @media all and (max-width: 959px) {
margin-top: 100%; width: 100%;
} height: 50px
}
.fraction-side-bar > div.active { > div {
background-color: #080808; text-align: center;
color: white; padding: 12px 25px;
margin-bottom: 15px;
font-size: 16px;
color: #666;
cursor: pointer;
border-top: 1px solid #666666;
border-bottom: 1px solid #666666;
@media all and (max-width: 959px) {
width: 33%;
float: left;
}
&.active {
background-color: #080808;
color: white;
}
&:first-child {
margin-top: 100%;
@media all and (max-width: 959px) {
margin-top: 0;
}
}
}
} }
/* SCROLL BAR */ /* SCROLL BAR */

View File

@ -1,6 +1,5 @@
import {Component, OnDestroy, OnInit} from '@angular/core'; import {Component, OnDestroy, OnInit} from '@angular/core';
import {ActivatedRoute, Router} from '@angular/router'; import {ActivatedRoute, Router} from '@angular/router';
import {Fraction} from '../../utils/fraction.enum'; import {Fraction} from '../../utils/fraction.enum';
import {Decoration} from '../../models/model-interfaces'; import {Decoration} from '../../models/model-interfaces';
import {DecorationService} from '../../services/army-management/decoration.service'; import {DecorationService} from '../../services/army-management/decoration.service';

View File

@ -7,6 +7,11 @@
overflow: hidden; overflow: hidden;
float: left; float: left;
cursor: pointer; cursor: pointer;
@media all and (max-width: 959px) {
width: 45vw;
margin: 1vh 2vw;
}
} }
.decoration-card:hover { .decoration-card:hover {
@ -47,8 +52,9 @@ img.img-medal {
} }
img.img-ribbon { img.img-ribbon {
padding-top: 10%; padding-top: 20px;
width: 55%; width: 60%;
max-width: 130px;
} }
.gradient { .gradient {

View File

@ -1,4 +1,4 @@
<div style="width: 1000px; margin:auto; position: relative;"> <div class="rank-overview-container">
<h1>{{'public.ranks.headline' | translate}}</h1> <h1>{{'public.ranks.headline' | translate}}</h1>
<div class="column-container pull-left"> <div class="column-container pull-left">

View File

@ -1,5 +1,51 @@
@import url('../../style/background-image.scss'); @import url('../../style/background-image.scss');
.rank-overview-container {
width: 1000px;
margin: auto;
position: relative;
}
@media all and (max-width: 959px) {
.rank-overview-container {
width: 100%;
}
div.column-container {
width: 49%;
}
h1 {
font-size: 26px;
margin: 0.5em 0;
}
h3 {
margin-top: 0;
font-size: 18px;
}
.mat-column-picture {
max-width: 4vw;
}
.mat-column-name {
max-width: 7.5vw;
overflow: hidden;
}
td.mat-cell:first-child {
padding-left: 1vw;
}
td > img {
height: 13vh !important;
max-height: 120px;
max-width: 100%;
padding: 4px;
}
}
table { table {
width: 100%; width: 100%;
} }

View File

@ -1,23 +1,24 @@
<div class="scroll-btn" <div *ngIf="!isSmallLayout">
*ngIf="isLeftScrollVisible" <div class="scroll-btn"
(mouseenter)="setRepeater(-10)" *ngIf="isLeftScrollVisible"
(mouseleave)="clearRepeater()"> (mouseenter)="setRepeater(-10)"
<mat-icon svgIcon="chevron-left"></mat-icon> (mouseleave)="clearRepeater()">
</div> <mat-icon svgIcon="chevron-left"></mat-icon>
<div class="campaign-horizontal-list" #horizontalList>
<div class="campaign-entry"
[ngClass]="{active : selectedCampaignId === 'all'}"
(click)="select({_id:'all'})">
{{'stats.campaign.title.all.time.overview' | translate}}
</div> </div>
<div class="campaign-entry"
*ngFor="let campaign of campaigns$ | async" <div class="campaign-horizontal-list" #horizontalList>
[ngClass]="{active : campaign._id === selectedCampaignId}" <div class="campaign-entry"
(click)="select(campaign)"> [ngClass]="{active : selectedCampaignId === 'all'}"
{{campaign.title}} (click)="select({_id:'all'})">
<span style="display:inline-block;" {{'stats.campaign.title.all.time.overview' | translate}}
*ngIf="loginService.hasPermission(3)"> </div>
<div class="campaign-entry"
*ngFor="let campaign of campaigns$ | async"
[ngClass]="{active : campaign._id === selectedCampaignId}"
(click)="select(campaign)">
{{campaign.title}}
<span style="display:inline-block;"
*ngIf="loginService.hasPermission(3)">
<button mat-icon-button <button mat-icon-button
[matMenuTriggerFor]="menu" [matMenuTriggerFor]="menu"
(click)="$event.stopPropagation()" (click)="$event.stopPropagation()"
@ -35,13 +36,34 @@
</button> </button>
</mat-menu> </mat-menu>
</span> </span>
</div>
</div>
<div class="scroll-btn scroll-btn-right"
*ngIf="isRightScrollVisible"
(mouseenter)="setRepeater(10)"
(mouseleave)="clearRepeater()">
<mat-icon svgIcon="chevron-right"></mat-icon>
</div> </div>
</div> </div>
<div class="scroll-btn scroll-btn-right"
*ngIf="isRightScrollVisible"
(mouseenter)="setRepeater(10)"
(mouseleave)="clearRepeater()">
<mat-icon svgIcon="chevron-right"></mat-icon>
</div>
<div *ngIf="isSmallLayout" class="campaign-select-small">
<mat-list-item [matMenuTriggerFor]="menuCampaign">
<div matline class="select-menu-text">
<span *ngIf="selectedCampaignId === 'all'">{{'stats.campaign.title.all.time.overview' | translate}}</span>
<span *ngIf="selectedCampaignId !== 'all' && selectedCampaign">{{selectedCampaign.title}}</span>
<span class="caret"></span>
</div>
</mat-list-item>
<mat-menu #menuCampaign="matMenu">
<button mat-menu-item (click)="select({_id:'all'})">
{{'stats.campaign.title.all.time.overview' | translate}}
</button>
<mat-divider></mat-divider>
<button mat-menu-item *ngFor="let campaign of campaigns$ | async"
(click)="select(campaign)">
{{campaign.title}}
</button>
</mat-menu>
</div>

View File

@ -6,7 +6,7 @@
} }
.scroll-btn-right { .scroll-btn-right {
top: 50px; top: 0;
left: calc(100vw - 50px); left: calc(100vw - 50px);
} }
@ -47,3 +47,21 @@
.campaign-entry:hover { .campaign-entry:hover {
border-bottom: 3px solid #ffd740; border-bottom: 3px solid #ffd740;
} }
.campaign-select-small {
background: #424242;
height: 3em;
color: #9d9d9d;
text-align: center;
border-bottom: 1px solid #9d9d9d;
.select-menu-text {
font-size: 16px;
}
}
:host /deep/ div.mat-list-item-content {
height: 100%;
padding: 10px;
cursor: pointer;
}

View File

@ -1,12 +1,4 @@
import { import {Component, ElementRef, EventEmitter, Input, OnChanges, OnInit, Output, ViewChild} from '@angular/core';
Component,
ElementRef,
EventEmitter,
Input,
OnInit,
Output,
ViewChild
} from '@angular/core';
import {Campaign} from '../../../models/model-interfaces'; import {Campaign} from '../../../models/model-interfaces';
import {LoginService} from '../../../services/app-user-service/login-service'; import {LoginService} from '../../../services/app-user-service/login-service';
import {Observable} from 'rxjs'; import {Observable} from 'rxjs';
@ -17,12 +9,14 @@ import {CampaignService} from '../../../services/logs/campaign.service';
templateUrl: './campaign-navigation.component.html', templateUrl: './campaign-navigation.component.html',
styleUrls: ['./campaign-navigation.component.scss'] styleUrls: ['./campaign-navigation.component.scss']
}) })
export class CampaignNavigationComponent implements OnInit { export class CampaignNavigationComponent implements OnInit, OnChanges {
campaigns$: Observable<Campaign[]>; campaigns$: Observable<Campaign[]>;
@Input() selectedCampaignId; @Input() selectedCampaignId;
@Input() isSmallLayout;
@Output() campaignSelect = new EventEmitter(); @Output() campaignSelect = new EventEmitter();
@Output() campaignEdit = new EventEmitter(); @Output() campaignEdit = new EventEmitter();
@ -31,6 +25,8 @@ export class CampaignNavigationComponent implements OnInit {
@ViewChild('horizontalList', {read: ElementRef}) public panel: ElementRef<any>; @ViewChild('horizontalList', {read: ElementRef}) public panel: ElementRef<any>;
selectedCampaign;
isLeftScrollVisible = false; isLeftScrollVisible = false;
isRightScrollVisible = true; isRightScrollVisible = true;
@ -48,8 +44,18 @@ export class CampaignNavigationComponent implements OnInit {
}); });
} }
ngOnChanges() {
this.campaigns$.subscribe(campaigns => {
const campaignIdx = campaigns.findIndex(c => c._id === this.selectedCampaignId);
if (campaignIdx !== -1) {
this.selectedCampaign = campaigns[campaignIdx];
}
});
}
select(campaign) { select(campaign) {
if (campaign && campaign._id) { if (campaign && campaign._id) {
this.selectedCampaign = campaign;
this.selectedCampaignId = campaign._id; this.selectedCampaignId = campaign._id;
this.campaignSelect.emit(campaign); this.campaignSelect.emit(campaign);
} }

View File

@ -14,6 +14,7 @@ h2 {
.search-field { .search-field {
width: 40%; width: 40%;
min-width: $highscore-table-width;
margin: 50px 0 50px 8%; margin: 50px 0 50px 8%;
} }

View File

@ -1,5 +1,6 @@
<div class="slide-chart-container" style="height: 100px;padding-top: 20px; margin-top: 10px;"> <div class="slide-chart-container" style="height: 100px;padding-top: 20px; margin-top: 10px;">
<mat-button-toggle-group #viewToggle="matButtonToggleGroup" <mat-button-toggle-group class="chart-select-group"
#viewToggle="matButtonToggleGroup"
[value]="this.id !== 'all' ? '0' : '1'" [value]="this.id !== 'all' ? '0' : '1'"
(change)="goToSlide(viewToggle.value)"> (change)="goToSlide(viewToggle.value)">
<mat-button-toggle *ngIf="id != 'all'" value="0"> <mat-button-toggle *ngIf="id != 'all'" value="0">

View File

@ -6,6 +6,26 @@
padding-left: 5%; padding-left: 5%;
} }
@media all and (max-width: 959px) {
.slide-chart-container {
width: 100%;
min-width: unset;
margin-top: 105px;
margin-bottom: 35px;
}
.chart-select-group {
display: inline-grid;
width: 70%;
text-align: center;
margin-left: 10%;
}
.chart-select-group /deep/ button.mat-button-toggle-button {
width: 100%;
}
}
mat-button-toggle.mat-button-toggle-checked { mat-button-toggle.mat-button-toggle-checked {
background: #ffffff; background: #ffffff;
} }

View File

@ -1,18 +1,22 @@
<campaign-navigation <div>
[selectedCampaignId]="selectedCampaign._id" <campaign-navigation
(campaignSelect)="switchCampaign($event)" [selectedCampaignId]="selectedCampaign._id"
(campaignEdit)="editCampaign($event)" [isSmallLayout]="isSmallLayout"
(campaignDelete)="deleteCampaign($event)"> (campaignSelect)="switchCampaign($event)"
</campaign-navigation> (campaignEdit)="editCampaign($event)"
(campaignDelete)="deleteCampaign($event)">
</campaign-navigation>
</div>
<div class="side-bar" <div [ngClass]="{collapsed: collapsed}"
[ngClass]="{collapsed: collapsed}" [style.display]="selectedCampaign._id === 'all' ? 'none' : 'block'"
[style.display]="selectedCampaign._id === 'all' ? 'none' : 'block'"> class="side-bar">
<war-list <war-list
[collapsed]="collapsed" [collapsed]="collapsed"
[campaign]="selectedCampaign"> [campaign]="selectedCampaign"
[isSmallLayout]="isSmallLayout">
</war-list> </war-list>
<div class="button-container" *ngIf="collapseBtnVisible"> <div *ngIf="!isSmallLayout" class="button-container">
<button mat-icon-button <button mat-icon-button
class="sidebar-toggle-btn" class="sidebar-toggle-btn"
(click)="toggleCollapse()"> (click)="toggleCollapse()">

View File

@ -1,6 +1,10 @@
.side-bar { .side-bar {
width: 20%; width: 20%;
float: left; float: left;
@media all and (max-width: 959px) {
width: 100%;
}
} }
.collapsed { .collapsed {
@ -10,13 +14,13 @@
.button-container { .button-container {
position: relative; position: relative;
z-index: 100; z-index: 100;
}
.button-container > button { & > button {
position: fixed; position: fixed;
top: calc(100vh - 35px); top: calc(100vh - 35px);
left: -10px; left: -10px;
background: #424242; background: #424242;
border: 1px solid #dadada; border: 1px solid #dadada;
box-shadow: #ffd740 0 0 3px; box-shadow: #ffd740 0 0 3px;
}
} }

View File

@ -21,7 +21,7 @@ export class StatisticComponent implements OnInit {
collapsed = false; collapsed = false;
collapseBtnVisible = true; isSmallLayout = false;
constructor(private campaignService: CampaignService, constructor(private campaignService: CampaignService,
private warService: WarService, private warService: WarService,
@ -40,13 +40,9 @@ export class StatisticComponent implements OnInit {
this.resolveCampaignFromUrl(); this.resolveCampaignFromUrl();
}); });
this.isSmallLayout = window.innerWidth <= BaseConfig.responsive.breakpointPx;
Observable.fromEvent(window, 'resize').subscribe(event => { Observable.fromEvent(window, 'resize').subscribe(event => {
if (event.target['innerWidth'] <= BaseConfig.responsive.breakpointPx) { this.isSmallLayout = event.target['innerWidth'] <= BaseConfig.responsive.breakpointPx;
this.collapsed = true;
this.collapseBtnVisible = false;
} else {
this.collapseBtnVisible = true;
}
}); });
} }

View File

@ -7,10 +7,10 @@ import {NgxChartsModule} from '@swimlane/ngx-charts';
import {CampaignService} from '../services/logs/campaign.service'; import {CampaignService} from '../services/logs/campaign.service';
import {PlayerService} from '../services/logs/player.service'; import {PlayerService} from '../services/logs/player.service';
import {LogsService} from '../services/logs/logs.service'; import {LogsService} from '../services/logs/logs.service';
import {MatButtonToggleModule, MatMenuModule, MatSortModule, MatTableModule} from '@angular/material'; import {MatButtonToggleModule, MatListModule, MatMenuModule, MatSortModule, MatTableModule} from '@angular/material';
import {HttpClient} from '@angular/common/http'; import {HttpClient} from '@angular/common/http';
import {TranslateHttpLoader} from '@ngx-translate/http-loader'; import {TranslateHttpLoader} from '@ngx-translate/http-loader';
import {TranslateModule, TranslateLoader} from '@ngx-translate/core'; import {TranslateLoader, TranslateModule} from '@ngx-translate/core';
export function createTranslateLoader(http: HttpClient) { export function createTranslateLoader(http: HttpClient) {
return new TranslateHttpLoader(http, './assets/i18n/statistics/', '.json'); return new TranslateHttpLoader(http, './assets/i18n/statistics/', '.json');
@ -27,6 +27,7 @@ export function createTranslateLoader(http: HttpClient) {
MatTableModule, MatTableModule,
MatSortModule, MatSortModule,
MatMenuModule, MatMenuModule,
MatListModule,
statsRouterModule, statsRouterModule,
TranslateModule.forChild({ TranslateModule.forChild({

View File

@ -38,3 +38,18 @@
padding: 15px; padding: 15px;
float: left; float: left;
} }
@media all and (max-width: 959px) {
.chart-container {
width: 150%;
min-width: 0;
height: 70vh
}
.chart-select-group {
background: #dadada;
max-width: fit-content;
width: 200%;
flex-wrap: wrap;
}
}

View File

@ -20,6 +20,8 @@ export class FractionStatsComponent implements OnInit, OnChanges {
@Input() logData: any; @Input() logData: any;
@Input() isSmallLayout: boolean;
startDateObj: Date; startDateObj: Date;
initialized: any; initialized: any;

View File

@ -33,8 +33,24 @@
.mat-column-vehicleHeavy, .mat-column-vehicleAir, .mat-column-travelDistance, .mat-column-driverDistance, .mat-column-vehicleHeavy, .mat-column-vehicleAir, .mat-column-travelDistance, .mat-column-driverDistance,
.mat-column-death, .mat-column-respawn, .mat-column-interact { .mat-column-death, .mat-column-respawn, .mat-column-interact {
flex: 0 0 62px; flex: 0 0 62px;
@media all and (max-width: 959px) {
flex: 0 0 50px;
}
} }
:host /deep/ .mat-table .mat-icon { :host /deep/ .mat-table .mat-icon {
color: #666666; color: #666666;
} }
@media all and (max-width: 959px) {
.mat-column-name {
flex: 0 0 160px;
word-wrap: break-word;
white-space: pre-wrap;
}
.mat-header-row, .scoreboard-table {
width: 768px;
}
}

View File

@ -20,6 +20,8 @@ export class ScoreboardComponent implements OnChanges {
@Input() fractionFilterSelected: string; @Input() fractionFilterSelected: string;
@Input() isSmallLayout: boolean;
@Output() playerTabSwitch = new EventEmitter(); @Output() playerTabSwitch = new EventEmitter();
tableHead = PlayerUtils.attributeDisplayNames; tableHead = PlayerUtils.attributeDisplayNames;
@ -47,8 +49,16 @@ export class ScoreboardComponent implements OnChanges {
} }
ngOnChanges(changes: SimpleChanges) { ngOnChanges(changes: SimpleChanges) {
if (changes.war) { if (changes['isSmallLayout'] && changes['isSmallLayout'].previousValue !== changes['isSmallLayout'].currentValue) {
const colIdxFraction = this.displayedColumns.findIndex(c => c === 'fraction');
if (this.isSmallLayout) {
this.displayedColumns.splice(colIdxFraction, 1);
} else if (colIdxFraction === -1) {
this.displayedColumns.splice(1, 0, 'fraction');
}
}
if (changes.war) {
changes.war.currentValue.players changes.war.currentValue.players
.filter(player => !player.initalized) .filter(player => !player.initalized)
.forEach(player => { .forEach(player => {

View File

@ -38,3 +38,12 @@
padding: 15px; padding: 15px;
float: left; float: left;
} }
@media all and (max-width: 959px) {
.chart-container {
width: 200%;
margin: 1%;
min-width: 0;
height: 80vh
}
}

View File

@ -9,7 +9,7 @@
style="font-weight: bold; margin-left: 10px;">{{war.ptOpfor}} {{fraction.OPFOR}}</span> style="font-weight: bold; margin-left: 10px;">{{war.ptOpfor}} {{fraction.OPFOR}}</span>
</div> </div>
<div class="pull-left head-field " style="padding-left: 100px;"> <div class="pull-left head-field-pie-chart">
<h4 class="pull-left" style="margin-bottom: 0;">{{'stats.scoreboard.participants' | translate}}</h4> <h4 class="pull-left" style="margin-bottom: 0;">{{'stats.scoreboard.participants' | translate}}</h4>
<ngx-charts-pie-chart <ngx-charts-pie-chart
class="pull-left" class="pull-left"
@ -24,7 +24,7 @@
</ngx-charts-pie-chart> </ngx-charts-pie-chart>
</div> </div>
<div class="pull-left " style="padding-left: 100px; padding-top:15px"> <div class="btn-clean-log">
<a class="btn btn-default" style="margin: 20px" target="_blank" href="resource/logs/{{war._id}}/clean.log"> <a class="btn btn-default" style="margin: 20px" target="_blank" href="resource/logs/{{war._id}}/clean.log">
{{'stats.scoreboard.show.logs' | translate}} {{'stats.scoreboard.show.logs' | translate}}
</a> </a>
@ -83,7 +83,9 @@
{{fraction.OPFOR}} {{fraction.OPFOR}}
</label> </label>
</form> </form>
<span class="btn btn-default pull-left tab-control" (click)="scoreBoardComponent.exportCSV()"> <span class="btn btn-default pull-left tab-control"
*ngIf="!isSmallLayout"
(click)="scoreBoardComponent.exportCSV()">
{{'stats.scoreboard.download.csv' | translate}} {{'stats.scoreboard.download.csv' | translate}}
</span> </span>
</div> </div>
@ -97,12 +99,14 @@
#scoreboard #scoreboard
[war]="war" [war]="war"
[fractionFilterSelected]="fractionFilterSelected" [fractionFilterSelected]="fractionFilterSelected"
[isSmallLayout]="isSmallLayout"
(playerTabSwitch)="switchToPlayerTab($event)"> (playerTabSwitch)="switchToPlayerTab($event)">
</cc-scoreboard> </cc-scoreboard>
<war-detail-fraction <war-detail-fraction
*ngIf="tab === 1 && logData" *ngIf="tab === 1 && logData"
[war]="war" [war]="war"
[logData]="logData"> [logData]="logData"
[isSmallLayout]="isSmallLayout">
</war-detail-fraction> </war-detail-fraction>
<campaign-player-detail <campaign-player-detail
*ngIf="tab === 2 && singlePlayerView === 1" *ngIf="tab === 2 && singlePlayerView === 1"

View File

@ -1,6 +1,10 @@
@import url('../../../style/list-entry.scss'); @import url('../../../style/list-entry.scss');
@import url('../../../style/hide-scrollbar.scss'); @import url('../../../style/hide-scrollbar.scss');
.war-header {
padding-left: 1.5vw;
}
.war-header-container { .war-header-container {
width: 920px; width: 920px;
min-height: 165px; min-height: 165px;
@ -14,6 +18,11 @@
margin-bottom: 10px; margin-bottom: 10px;
} }
.head-field-pie-chart {
@extend .head-field;
padding-left: 100px;
}
form.tab-control { form.tab-control {
padding: 10px; padding: 10px;
} }
@ -80,3 +89,33 @@ span.tab-control {
cursor: pointer !important; cursor: pointer !important;
color: #666666 !important; color: #666666 !important;
} }
.btn-clean-log {
padding-left: 100px;
padding-top: 15px;
float: left;
}
@media all and (max-width: 959px) {
.war-header {
position: relative;
overflow: auto;
margin-bottom: 34px; /*do not cover anything with back to top bar*/
}
.war-header-container, .nav-tabs {
width: 768px;
}
.head-field-pie-chart {
padding-left: 0;
}
.nav-tabs > li:last-child {
margin-left: 25px;
}
div.btn-clean-log {
padding-left: 57px;
}
}

View File

@ -6,6 +6,8 @@ import {ChartUtils} from '../../../utils/chart-utils';
import {Fraction} from '../../../utils/fraction.enum'; import {Fraction} from '../../../utils/fraction.enum';
import {LogsService} from '../../../services/logs/logs.service'; import {LogsService} from '../../../services/logs/logs.service';
import {ScoreboardComponent} from '../scoreboard/scoreboard.component'; import {ScoreboardComponent} from '../scoreboard/scoreboard.component';
import {BaseConfig} from '../../../app.config';
import {Observable} from 'rxjs';
@Component({ @Component({
@ -37,6 +39,8 @@ export class WarHeaderComponent implements OnInit {
fractionFilterSelected: string; fractionFilterSelected: string;
isSmallLayout = false;
playerChart: any[] = []; playerChart: any[] = [];
colorScheme = { colorScheme = {
@ -64,6 +68,11 @@ export class WarHeaderComponent implements OnInit {
ChartUtils.getSingleDataArray(Fraction.OPFOR, war.playersOpfor, Fraction.BLUFOR, war.playersBlufor); ChartUtils.getSingleDataArray(Fraction.OPFOR, war.playersOpfor, Fraction.BLUFOR, war.playersBlufor);
Object.assign(this, [this.playerChart]); Object.assign(this, [this.playerChart]);
}); });
this.isSmallLayout = window.innerWidth <= BaseConfig.responsive.breakpointPx;
Observable.fromEvent(window, 'resize').subscribe(event => {
this.isSmallLayout = event.target['innerWidth'] <= BaseConfig.responsive.breakpointPx;
});
} }
switchTab(index: number) { switchTab(index: number) {

View File

@ -1,53 +1,79 @@
<div class="war-list-header" *ngIf="!collapsed && loginService.hasPermission(3)"> <div *ngIf="!isSmallLayout">
<button mat-stroked-button (click)="selectNewCampaign()"> <div class="war-list-header" *ngIf="!collapsed && loginService.hasPermission(3)">
{{'stats.sidebar.campaign.add' | translate}} <button mat-stroked-button (click)="selectNewCampaign()">
</button> {{'stats.sidebar.campaign.add' | translate}}
<button mat-stroked-button (click)="selectNewWar()"> </button>
{{'stats.sidebar.battle.add' | translate}} <button mat-stroked-button (click)="selectNewWar()">
</button> {{'stats.sidebar.battle.add' | translate}}
</div> </button>
<div class="list-header"
[ngClass]="{selected : selectedWarId == campaign._id}" (click)="selectOverview(campaign._id)">
<div class="select-indicator-container">
<div class="select-indicator"></div>
</div> </div>
<mat-icon [matTooltip]="collapsed ? toolTipTranslation['stats.sidebar.overview'] : ''"
matTooltipPosition="right"
svgIcon="stats-chart"></mat-icon>
<span *ngIf="!collapsed">{{'stats.sidebar.overview' | translate}}</span>
</div>
<div class="list-header" <div class="list-header"
[ngClass]="{selected : selectedWarId == campaign._id + highscore}" (click)="selectHighscore(campaign._id)"> [ngClass]="{selected : selectedWarId == campaign._id}" (click)="selectOverview(campaign._id)">
<div class="select-indicator-container"> <div class="select-indicator-container">
<div class="select-indicator"></div> <div class="select-indicator"></div>
</div>
<mat-icon [matTooltip]="collapsed ? toolTipTranslation['stats.sidebar.overview'] : ''"
matTooltipPosition="right"
svgIcon="stats-chart"></mat-icon>
<span *ngIf="!collapsed">{{'stats.sidebar.overview' | translate}}</span>
</div> </div>
<mat-icon [matTooltip]="collapsed ? toolTipTranslation['stats.sidebar.highscore'] : ''"
matTooltipPosition="right"
svgIcon="highscore"></mat-icon>
<span *ngIf="!collapsed">{{'stats.sidebar.highscore' | translate}}</span>
</div>
<div class="list-header-battles"> <div class="list-header"
<mat-icon [matTooltip]="collapsed ? toolTipTranslation['stats.sidebar.battles'] : ''" [ngClass]="{selected : selectedWarId == campaign._id + highscore}" (click)="selectHighscore(campaign._id)">
matTooltipPosition="right" <div class="select-indicator-container">
svgIcon="battle"></mat-icon> <div class="select-indicator"></div>
<span *ngIf="!collapsed">{{'stats.sidebar.battles' | translate}}</span> </div>
</div> <mat-icon [matTooltip]="collapsed ? toolTipTranslation['stats.sidebar.highscore'] : ''"
matTooltipPosition="right"
<div class="battle-list"> svgIcon="highscore"></mat-icon>
<div *ngFor="let war of campaign.wars$ | async"> <span *ngIf="!collapsed">{{'stats.sidebar.highscore' | translate}}</span>
<cc-war-item
[war]="war"
(warEdit)="editWar($event)"
(warDelete)="deleteWar(war)"
(warSelected)="selectWar($event)"
[collapsed]="collapsed"
[selected]="war._id == selectedWarId">
</cc-war-item>
</div> </div>
<div class="fill-vertical-border"
[ngClass]="{collapsed: collapsed}"> <div class="list-header-battles">
<mat-icon [matTooltip]="collapsed ? toolTipTranslation['stats.sidebar.battles'] : ''"
matTooltipPosition="right"
svgIcon="battle"></mat-icon>
<span *ngIf="!collapsed">{{'stats.sidebar.battles' | translate}}</span>
</div>
<div class="battle-list">
<div *ngFor="let war of campaign.wars$ | async">
<cc-war-item
[war]="war"
(warEdit)="editWar($event)"
(warDelete)="deleteWar(war)"
(warSelected)="selectWar($event)"
[collapsed]="collapsed"
[selected]="war._id == selectedWarId">
</cc-war-item>
</div>
<div class="fill-vertical-border"
[ngClass]="{collapsed: collapsed}">
</div>
</div> </div>
</div> </div>
<div class="war-select-small" *ngIf="isSmallLayout">
<mat-list-item [matMenuTriggerFor]="menuWars">
<div matline class="select-menu-text">
<span *ngIf="selectedWarId === (campaign._id + highscore)">{{'stats.sidebar.highscore' | translate}}</span>
<span *ngIf="selectedWarId === campaign._id">{{'stats.sidebar.overview' | translate}}</span>
<span *ngIf="selectedWar">{{selectedWar.title}}</span>
<span class="caret"></span>
</div>
</mat-list-item>
<mat-menu #menuWars="matMenu">
<button mat-menu-item (click)="selectOverview(campaign._id)">
{{'stats.sidebar.overview' | translate}}
</button>
<button mat-menu-item (click)="selectHighscore(campaign._id)">
{{'stats.sidebar.highscore' | translate}}
</button>
<mat-divider></mat-divider>
<button mat-menu-item *ngFor="let war of campaign.wars$ | async"
(click)="selectWar(war._id)">
{{war.title}}
</button>
</mat-menu>
</div>

View File

@ -90,8 +90,32 @@
position: fixed; position: fixed;
border-right: 1px solid #dadada; border-right: 1px solid #dadada;
height: 100vh; height: 100vh;
top: 0;
z-index: -1;
@media all and (max-width: 959px) {
display: none;
}
} }
.war-select-small {
background: #424242;
height: 3em;
color: #9d9d9d;
text-align: center;
.select-menu-text {
font-size: 16px;
}
}
:host /deep/ div.mat-list-item-content {
height: 100%;
padding: 10px;
cursor: pointer;
}
/* Table Scrollbar BEGIN */ /* Table Scrollbar BEGIN */
.battle-list::-webkit-scrollbar { .battle-list::-webkit-scrollbar {

View File

@ -18,8 +18,12 @@ export class WarListComponent implements OnChanges {
@Input() collapsed: boolean; @Input() collapsed: boolean;
@Input() isSmallLayout: boolean;
selectedWarId: string | number; selectedWarId: string | number;
selectedWar: War;
changeCount = 0; changeCount = 0;
toolTipTranslation = {}; toolTipTranslation = {};
@ -65,15 +69,30 @@ export class WarListComponent implements OnChanges {
} else if (changes.campaign) { } else if (changes.campaign) {
this.selectOverview(this.campaign._id); this.selectOverview(this.campaign._id);
} }
this.fetchSelectedWar(this.selectedWarId);
}
fetchSelectedWar(newSelect) {
this.campaign.wars$.subscribe(wars => {
const warIdx = wars.findIndex(w => w._id === newSelect);
if (warIdx !== -1) {
this.selectedWar = wars[warIdx];
} else {
this.selectedWar = null;
}
});
} }
selectNewCampaign() { selectNewCampaign() {
this.selectedWarId = null; this.selectedWarId = null;
this.selectedWar = null;
this.router.navigate([{outlets: {'right': ['campaign']}}], {relativeTo: this.route}); this.router.navigate([{outlets: {'right': ['campaign']}}], {relativeTo: this.route});
} }
selectWar(warId) { selectWar(warId) {
if (this.selectedWarId !== warId) { if (this.selectedWarId !== warId) {
this.fetchSelectedWar(warId);
this.selectedWarId = warId; this.selectedWarId = warId;
this.router.navigate([{outlets: {'right': ['war', warId]}}], {relativeTo: this.route}); this.router.navigate([{outlets: {'right': ['war', warId]}}], {relativeTo: this.route});
} }
@ -82,6 +101,7 @@ export class WarListComponent implements OnChanges {
selectOverview(campaignId) { selectOverview(campaignId) {
if (this.selectedWarId !== campaignId) { if (this.selectedWarId !== campaignId) {
this.selectedWarId = campaignId; this.selectedWarId = campaignId;
this.selectedWar = null;
this.router.navigate([{outlets: {'right': ['overview', campaignId]}}], {relativeTo: this.route}); this.router.navigate([{outlets: {'right': ['overview', campaignId]}}], {relativeTo: this.route});
} }
} }
@ -89,6 +109,7 @@ export class WarListComponent implements OnChanges {
selectHighscore(campaignId) { selectHighscore(campaignId) {
if (this.selectedWarId !== campaignId + this.highscore) { if (this.selectedWarId !== campaignId + this.highscore) {
this.selectedWarId = campaignId + this.highscore; this.selectedWarId = campaignId + this.highscore;
this.selectedWar = null;
this.router.navigate([{outlets: {'right': ['highscore', campaignId]}}], {relativeTo: this.route}); this.router.navigate([{outlets: {'right': ['highscore', campaignId]}}], {relativeTo: this.route});
} }
} }

View File

@ -1,7 +1,7 @@
:host { :host {
display: flow-root; display: flow-root;
height: 100%; height: 100%;
min-height: 100vh; min-height: calc(100vh - 50px);
width: 100%; width: 100%;
min-width: fit-content; min-width: fit-content;
padding-bottom: 23px; padding-bottom: 23px;

View File

@ -62,6 +62,11 @@ form {
#right { #right {
display: flow-root; display: flow-root;
@media all and (max-width: 959px) {
display: inline-block;
width: 100%;
}
} }
/* MATERIAL */ /* MATERIAL */