Improved spinner logic and table design #130

This commit is contained in:
Sven Heidemann 2023-02-18 12:42:51 +01:00
parent 7d67b08ce6
commit 43b6df2ba3
8 changed files with 438 additions and 387 deletions

View File

@ -1,4 +1,5 @@
<main [class]="themeName"> <main [class]="themeName">
<ng-container *ngIf="isLoggedIn; else login"> <ng-container *ngIf="isLoggedIn; else login">
<app-header></app-header> <app-header></app-header>

View File

@ -1,4 +1,4 @@
<ng-container *ngIf="spinnerService.showSpinnerState"> <ng-container *ngIf="showSpinnerState">
<div class="spinner-component-wrapper"> <div class="spinner-component-wrapper">
<div class="spinner-wrapper"> <div class="spinner-wrapper">
<p-progressSpinner styleClass="custom-spinner" animationDuration=".8s"></p-progressSpinner> <p-progressSpinner styleClass="custom-spinner" animationDuration=".8s"></p-progressSpinner>

View File

@ -8,9 +8,15 @@ import { SpinnerService } from 'src/app/services/spinner/spinner.service';
}) })
export class SpinnerComponent implements OnInit { export class SpinnerComponent implements OnInit {
showSpinnerState: boolean = false;
constructor( constructor(
public spinnerService: SpinnerService public spinnerService: SpinnerService
) { } ) {
this.spinnerService.showSpinnerState$.subscribe(value => {
this.showSpinnerState = value;
});
}
ngOnInit(): void { ngOnInit(): void {
} }

View File

@ -22,7 +22,7 @@ export class Queries {
} }
`; `;
static levelQuery= ` static levelQuery = `
query LevelsList($filter: LevelFilter, $page: Page, $sort: Sort) { query LevelsList($filter: LevelFilter, $page: Page, $sort: Sort) {
levelCount levelCount
levels(filter: $filter, page: $page, sort: $sort) { levels(filter: $filter, page: $page, sort: $sort) {
@ -39,7 +39,7 @@ export class Queries {
} }
`; `;
static usersQuery= ` static usersQuery = `
query UsersList($filter: UserFilter, $page: Page, $sort: Sort) { query UsersList($filter: UserFilter, $page: Page, $sort: Sort) {
userCount userCount
users(filter: $filter, page: $page, sort: $sort) { users(filter: $filter, page: $page, sort: $sort) {
@ -75,6 +75,9 @@ export class Queries {
id id
gameServer gameServer
} }
createdAt
modifiedAt
} }
} }
`; `;

View File

@ -26,7 +26,7 @@
<ng-template pTemplate="header"> <ng-template pTemplate="header">
<tr> <tr>
<th class="table-header-small" pSortableColumn="id"> <th pSortableColumn="id">
<div class="table-header-label"> <div class="table-header-label">
<div class="table-header-text">{{'view.server.members.headers.id' | translate}}</div> <div class="table-header-text">{{'view.server.members.headers.id' | translate}}</div>
<p-sortIcon field="id" class="table-header-icon"></p-sortIcon> <p-sortIcon field="id" class="table-header-icon"></p-sortIcon>
@ -61,33 +61,46 @@
</div> </div>
</th> </th>
<th class="table-header-actions" pSortableColumn="leftServer"> <th pSortableColumn="leftServer">
<div class="table-header-label"> <div class="table-header-label">
<div class="table-header-text">{{'view.server.members.headers.left_server' | translate}}</div> <div class="table-header-text">{{'view.server.members.headers.left_server' | translate}}</div>
<p-sortIcon field="leftServer" class="table-header-icon"></p-sortIcon> <p-sortIcon field="leftServer" class="table-header-icon"></p-sortIcon>
</div> </div>
</th> </th>
<th class="table-header-small-dropdown" pSortableColumn="level.name"> <th pSortableColumn="level.name">
<div class="table-header-label"> <div class="table-header-label">
<div class="table-header-text">{{'view.server.members.headers.level' | translate}}</div> <div class="table-header-text">{{'view.server.members.headers.level' | translate}}</div>
<p-sortIcon field="level.name" class="table-header-icon"></p-sortIcon> <p-sortIcon field="level.name" class="table-header-icon"></p-sortIcon>
</div> </div>
</th> </th>
<th class="table-header-actions"> <th>
<div class="table-header-label">
<div class="table-header-text">{{'common.created_at' | translate}}</div>
</div>
</th>
<th>
<div class="table-header-label">
<div class="table-header-text">{{'common.modified_at' | translate}}</div>
</div>
</th>
<th>
<div class="table-header-label"> <div class="table-header-label">
<div class="table-header-text">{{'view.server.members.headers.actions' | translate}}</div> <div class="table-header-text">{{'view.server.members.headers.actions' | translate}}</div>
</div> </div>
</th> </th>
</tr> </tr>
<tr> <tr>
<th> <th class="table-header-small">
<form [formGroup]="filterForm"> <form [formGroup]="filterForm">
<input type="text" pInputText formControlName="id" placeholder="{{'view.server.members.headers.id' | translate}}"> <input type="text" pInputText formControlName="id" placeholder="{{'view.server.members.headers.id' | translate}}">
</form> </form>
</th> </th>
<th> <th class="table-header-medium">
<form [formGroup]="filterForm"> <form [formGroup]="filterForm">
<input type="text" pInputText formControlName="discordId" placeholder="{{'view.server.members.headers.discord_id' | translate}}"> <input type="text" pInputText formControlName="discordId" placeholder="{{'view.server.members.headers.discord_id' | translate}}">
</form> </form>
@ -99,17 +112,19 @@
</th> </th>
<th></th> <th></th>
<th></th> <th></th>
<th> <th class="table-header-small-dropdown">
<form [formGroup]="filterForm"> <form [formGroup]="filterForm">
<p-dropdown formControlName="leftServer" [options]="leftServerOptions" placeholder="{{'view.server.members.headers.left_server' | translate}}"></p-dropdown> <p-dropdown formControlName="leftServer" [options]="leftServerOptions" placeholder="{{'view.server.members.headers.left_server' | translate}}"></p-dropdown>
</form> </form>
</th> </th>
<th> <th class="table-header-small-dropdown">
<form [formGroup]="filterForm"> <form [formGroup]="filterForm">
<p-dropdown formControlName="level" [options]="levels" placeholder="{{'view.server.members.headers.level' | translate}}"></p-dropdown> <p-dropdown formControlName="level" [options]="levels" placeholder="{{'view.server.members.headers.level' | translate}}"></p-dropdown>
</form> </form>
</th> </th>
<th></th> <th></th>
<th></th>
<th class="table-header-actions"></th>
</tr> </tr>
</ng-template> </ng-template>
@ -185,6 +200,26 @@
</ng-template> </ng-template>
</p-cellEditor> </p-cellEditor>
</td> </td>
<td>
<p-cellEditor>
<ng-template pTemplate="input">
{{member.createdAt | date:'dd.MM.yy HH:mm'}}
</ng-template>
<ng-template pTemplate="output">
{{member.createdAt | date:'dd.MM.yy HH:mm'}}
</ng-template>
</p-cellEditor>
</td>
<td>
<p-cellEditor>
<ng-template pTemplate="input">
{{member.modifiedAt | date:'dd.MM.yy HH:mm'}}
</ng-template>
<ng-template pTemplate="output">
{{member.modifiedAt | date:'dd.MM.yy HH:mm'}}
</ng-template>
</p-cellEditor>
</td>
<td> <td>
<div class="btn-wrapper"> <div class="btn-wrapper">
<button *ngIf="!editing" pButton pInitEditableRow class="btn icon-btn" icon="pi pi-pencil" <button *ngIf="!editing" pButton pInitEditableRow class="btn icon-btn" icon="pi pi-pencil"

View File

@ -35,18 +35,17 @@ export class ProfileComponent implements OnInit {
async ngOnInit() { async ngOnInit() {
this.data.getServerFromRoute(this.route); this.data.getServerFromRoute(this.route);
this.spinner.showSpinner();
if (!this.route.snapshot.params["memberId"]) { if (!this.route.snapshot.params["memberId"]) {
this.spinner.hideSpinner();
this.router.navigate(["/dashboard"]); this.router.navigate(["/dashboard"]);
return; return;
} }
let authUser = await this.auth.getLoggedInUser(); let authUser = await this.auth.getLoggedInUser();
this.spinner.showSpinner();
let user: UserDTO | null = authUser?.users?.find(u => u.server == this.sidebar.server$.value?.id) ?? null; let user: UserDTO | null = authUser?.users?.find(u => u.server == this.sidebar.server$.value?.id) ?? null;
if (!user || user?.id != this.route.snapshot.params["memberId"] && !user?.isModerator) { if (!user || user?.id != this.route.snapshot.params["memberId"] && !user?.isModerator) {
this.toast.error(this.translate.instant("view.server.profile.permission_denied"), this.translate.instant("view.server.profile.permission_denied_d")); this.toast.error(this.translate.instant("view.server.profile.permission_denied"), this.translate.instant("view.server.profile.permission_denied_d"));
this.spinner.hideSpinner();
this.router.navigate(["/server", this.sidebar.server$.value?.id]); this.router.navigate(["/server", this.sidebar.server$.value?.id]);
return; return;
} }

View File

@ -1,22 +1,23 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { BehaviorSubject } from "rxjs";
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class SpinnerService { export class SpinnerService {
showSpinnerState = false; showSpinnerState$ = new BehaviorSubject<boolean>(false);
constructor() { } constructor() { }
showSpinner() { showSpinner() {
this.showSpinnerState = true; this.showSpinnerState$.next(true);
} }
hideSpinner() { hideSpinner() {
this.showSpinnerState = false; this.showSpinnerState$.next(false);
} }
toggleSpinner() { toggleSpinner() {
this.showSpinnerState = !this.showSpinnerState; this.showSpinnerState$.next(!this.showSpinnerState$.value);
} }
} }

View File

@ -7,465 +7,471 @@
html, html,
body { body {
height: 100%; height: 100%;
padding: 0; padding: 0;
margin: 0; margin: 0;
font-size: 1rem; font-size: 1rem;
} }
main { main {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
min-height: 100vh; min-height: 100vh;
} }
h1 { h1 {
margin: 0; margin: 0;
font-size: 1.75rem; font-size: 1.75rem;
} }
h2 { h2 {
margin: 0; margin: 0;
font-size: 1.5rem; font-size: 1.5rem;
} }
h3 { h3 {
margin: 0; margin: 0;
font-size: 1.25rem; font-size: 1.25rem;
} }
header { header {
height: $headerHeight; height: $headerHeight;
display: flex;
flex-direction: row;
.logo-button-wrapper {
display: flex; display: flex;
flex-direction: row; align-items: center;
justify-content: center;
.logo-button-wrapper { .p-button.p-button-text {
display: flex; border: none;
align-items: center;
justify-content: center;
.p-button.p-button-text {
border: none;
}
} }
}
.logo { .logo {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
.header-menu { .header-menu {
text-align: right; text-align: right;
flex: 1; flex: 1;
justify-content: flex-end; justify-content: flex-end;
margin: 0 5px 0 0; margin: 0 5px 0 0;
} }
.p-menu-overlay { .p-menu-overlay {
width: 180px !important; width: 180px !important;
} }
} }
.auth-header { .auth-header {
.p-menu-overlay { .p-menu-overlay {
width: 180px !important; width: 180px !important;
} }
} }
.app { .app {
height: 100%;
display: flex;
flex: 1;
.sidebar {
height: 100%; height: 100%;
display: flex; }
flex: 1;
.sidebar { h1 {
height: 100%; // table views with filters are scrollable with 10 items, when 30px margin
} margin-bottom: 20px;
}
h1 { .component-wrapper {
// table views with filters are scrollable with 10 items, when 30px margin width: 100%;
margin-bottom: 20px;
}
.component-wrapper { .component {
width: 100%; width: 100%;
height: 100%;
padding: 15px;
.component { .content-wrapper {
width: 100%; margin-bottom: 30px;
height: 100%; border-radius: 5px;
padding: 15px;
.content-wrapper { .content-header {
margin-bottom: 30px; padding: 10px;
border-radius: 5px;
.content-header { h2 {
padding: 10px; font-size: 20px;
}
h2 {
font-size: 20px;
}
}
.content {
width: 100%;
display: flex;
flex-direction: column;
padding: 15px;
form {
width: 100%;
}
.content-row {
display: flex;
flex-direction: row;
flex: 1;
margin: 5px 0;
}
.content-column {
display: flex;
flex: 1;
}
.content-data-name {
display: flex;
flex: 1;
align-items: center;
font-size: 18px;
}
.content-data-value {
display: flex;
flex: 1;
align-items: center;
font-size: 18px;
}
.content-divider {
margin: 5px 0;
}
.content-input-field {
width: 50% !important;
margin: 0 !important;
}
.input-field {
width: 100%;
input,
.p-password {
width: 100%;
}
}
.input-field-info-text {
margin: 15px 0;
width: 100%;
}
.login-form-submit {
.login-form-submit-btn {
width: 100%;
}
}
.login-form-sub-button-wrapper {
display: flex;
flex-direction: column;
.login-form-sub-btn {
margin-top: 15px;
width: 100%;
}
.login-form-sub-btn-wrapper {
width: 100%;
}
}
.table-caption {
display: flex;
.table-caption-text {
flex: 1;
font-weight: 400;
}
.table-caption-search {
}
}
.table-header-label {
display: flex;
.table-header-text {
flex: 1;
}
.table-header-icon {
}
}
.table-header-small {
width: 25px;
}
.table-header-actions {
width: 100px;
}
.table-header-small-dropdown {
width: 150px;
}
.server-list-wrapper {
display: flex;
flex-direction: column;
gap: 10px;
.server-filter {
}
.server-count {
}
.server-list {
display: flex;
flex-direction: column;
gap: 15px;
.server {
display: flex;
gap: 15px;
padding: 20px;
.logo {
overflow: hidden;
img {
width: 4rem;
height: 4rem;
object-fit: contain;
}
}
.info {
display: flex;
flex-direction: column;
gap: 10px;
.name {
margin: 0;
justify-content: center;
align-items: center;
}
.data {
}
}
}
}
}
}
}
} }
.content {
width: 100%;
display: flex;
flex-direction: column;
padding: 15px;
form {
input {
width: 100%;
}
}
.content-row {
display: flex;
flex-direction: row;
flex: 1;
margin: 5px 0;
}
.content-column {
display: flex;
flex: 1;
}
.content-data-name {
display: flex;
flex: 1;
align-items: center;
font-size: 18px;
}
.content-data-value {
display: flex;
flex: 1;
align-items: center;
font-size: 18px;
}
.content-divider {
margin: 5px 0;
}
.content-input-field {
width: 50% !important;
margin: 0 !important;
}
.input-field {
width: 100%;
input,
.p-password {
width: 100%;
}
}
.input-field-info-text {
margin: 15px 0;
width: 100%;
}
.login-form-submit {
.login-form-submit-btn {
width: 100%;
}
}
.login-form-sub-button-wrapper {
display: flex;
flex-direction: column;
.login-form-sub-btn {
margin-top: 15px;
width: 100%;
}
.login-form-sub-btn-wrapper {
width: 100%;
}
}
.table-caption {
display: flex;
.table-caption-text {
flex: 1;
font-weight: 400;
}
.table-caption-search {
}
}
.table-header-label {
display: flex;
.table-header-text {
flex: 1;
}
.table-header-icon {
}
}
.table-header-small {
width: 75px;
}
.table-header-medium {
width: 150px;
}
.table-header-actions {
width: 100px;
}
.table-header-small-dropdown {
width: 150px;
}
.server-list-wrapper {
display: flex;
flex-direction: column;
gap: 10px;
.server-filter {
}
.server-count {
}
.server-list {
display: flex;
flex-direction: column;
gap: 15px;
.server {
display: flex;
gap: 15px;
padding: 20px;
.logo {
overflow: hidden;
img {
width: 4rem;
height: 4rem;
object-fit: contain;
}
}
.info {
display: flex;
flex-direction: column;
gap: 10px;
.name {
margin: 0;
justify-content: center;
align-items: center;
}
.data {
}
}
}
}
}
}
}
} }
}
} }
.p-dialog-header { .p-dialog-header {
padding: 20px 20px 20px 20px !important; padding: 20px 20px 20px 20px !important;
} }
.p-dialog-content { .p-dialog-content {
padding: 20px !important; padding: 20px !important;
} }
.p-dialog-footer { .p-dialog-footer {
padding: 0 20px 20px 20px !important; padding: 0 20px 20px 20px !important;
} }
.p-dialog-content { .p-dialog-content {
.content-row { .content-row {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
flex: 1; flex: 1;
margin: 1.5px 0; margin: 1.5px 0;
} }
.content-column { .content-column {
display: flex; display: flex;
flex: 1; flex: 1;
} }
.content-data-name { .content-data-name {
display: flex; display: flex;
flex: 1; flex: 1;
align-items: center; align-items: center;
font-size: 18px; font-size: 18px;
} }
.content-data-value { .content-data-value {
display: flex; display: flex;
flex: 1; flex: 1;
align-items: center; align-items: center;
font-size: 18px; font-size: 18px;
} }
.content-divider { .content-divider {
margin: 5px 0; margin: 5px 0;
} }
.content-input-field { .content-input-field {
width: 50% !important; width: 50% !important;
margin: 0 !important; margin: 0 !important;
} }
} }
footer { footer {
width: 100%; width: 100%;
height: $footerHeight; height: $footerHeight;
padding: 0 10px; padding: 0 10px;
display: flex;
align-items: center;
justify-content: center;
.left {
width: 50%;
display: flex; display: flex;
align-items: center;
justify-content: center;
.left { // .frontend-version {
width: 50%; // }
display: flex; .version-divider {
margin: 0 5px;
// .frontend-version {
// }
.version-divider {
margin: 0 5px;
}
// .backend-version {
// }
} }
.right { // .backend-version {
width: 50%; // }
text-align: right; }
}
.right {
width: 50%;
text-align: right;
}
} }
.login-wrapper { .login-wrapper {
width: 100vw; width: 100vw;
height: 100vh; height: 100vh;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
gap: 15px;
.login-form-wrapper,
.auth-header {
width: 350px;
height: 450px;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
flex-direction: column;
gap: 15px;
.login-form-wrapper, border-radius: 25px;
.auth-header {
width: 350px;
height: 450px;
display: flex; .login-form {
justify-content: center; width: 80%;
align-items: center; height: 80%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
border-radius: 25px; h1 {
text-align: center;
}
.login-form { .input-field-info-text {
width: 80%; margin: 15px 0;
height: 80%; width: 100%;
display: flex; }
flex-direction: column;
justify-content: center;
align-items: center;
h1 { .login-form-submit {
text-align: center; .login-form-submit-btn {
} width: 100%;
.input-field-info-text {
margin: 15px 0;
width: 100%;
}
.login-form-submit {
.login-form-submit-btn {
width: 100%;
}
}
.login-form-sub-button-wrapper {
display: flex;
flex-direction: column;
.login-form-sub-btn {
margin-top: 15px;
width: 100%;
}
.login-form-sub-btn-wrapper {
width: 100%;
}
}
} }
} }
.register-form-wrapper { .login-form-sub-button-wrapper {
height: 600px; display: flex;
} flex-direction: column;
.register-confirm-form-wrapper { .login-form-sub-btn {
height: 250px; margin-top: 15px;
} width: 100%;
}
.auth-header { .login-form-sub-btn-wrapper {
width: 350px; width: 100%;
height: 75px; }
}
} }
}
.register-form-wrapper {
height: 600px;
}
.register-confirm-form-wrapper {
height: 250px;
}
.auth-header {
width: 350px;
height: 75px;
}
} }
.input-field { .input-field {
margin: 15px 0; margin: 15px 0;
input, input,
.p-password { .p-password {
height: 40px; height: 40px;
width: 100%; width: 100%;
font-size: 18px; font-size: 18px;
} }
} }
.btn { .btn {
border: 0; border: 0;
} }
.spinner-component-wrapper { .spinner-component-wrapper {
position: absolute; position: absolute;
top: 0; top: 0;
width: 100vw; width: 100vw;
height: 100vh; height: 100vh;
display: flex;
flex-direction: row;
justify-content: center;
.spinner-wrapper {
display: flex; display: flex;
flex-direction: row;
justify-content: center; justify-content: center;
align-items: center;
.spinner-wrapper { }
display: flex;
justify-content: center;
align-items: center;
}
} }