Als Nutzer möchte ich Datenänderungen nach verfolgen können #246 #248
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "kdb-web",
|
||||
"version": "1.0.dev247",
|
||||
"version": "1.0.dev246",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"update-version": "ts-node-esm update-version.ts",
|
||||
@ -50,4 +50,4 @@
|
||||
"tslib": "^2.4.1",
|
||||
"typescript": "~4.9.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,3 +2,16 @@ export interface Data {
|
||||
createdAt?: string;
|
||||
modifiedAt?: string;
|
||||
}
|
||||
|
||||
export interface DataWithHistory {
|
||||
createdAt?: string;
|
||||
modifiedAt?: string;
|
||||
history?: History[];
|
||||
}
|
||||
|
||||
export interface History {
|
||||
deleted?: boolean;
|
||||
dateFrom?: string;
|
||||
dateTo?: string;
|
||||
[x: string | number | symbol]: unknown;
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { Data } from "./data.model";
|
||||
import { DataWithHistory } from "./data.model";
|
||||
import { Level, LevelFilter } from "./level.model";
|
||||
import { Server, ServerFilter } from "./server.model";
|
||||
import { UserJoinedServer } from "./user_joined_server.model";
|
||||
import { UserJoinedVoiceChannel } from "./user_joined_voice_channel.model";
|
||||
import { UserJoinedGameServer } from "./user_joined_game_server.model";
|
||||
|
||||
export interface User extends Data {
|
||||
export interface User extends DataWithHistory {
|
||||
id?: number;
|
||||
discordId?: number;
|
||||
name?: string;
|
||||
@ -23,6 +23,17 @@ export interface User extends Data {
|
||||
|
||||
userJoinedGameServerCount?: number;
|
||||
userJoinedGameServers?: UserJoinedGameServer[];
|
||||
|
||||
// history?: UserHistory[];
|
||||
}
|
||||
|
||||
export interface UserHistory extends History {
|
||||
id?: number;
|
||||
discordId?: number;
|
||||
xp?: number;
|
||||
level?: number;
|
||||
server?: number;
|
||||
leftServer?: boolean;
|
||||
}
|
||||
|
||||
export interface UserFilter {
|
||||
|
@ -115,6 +115,17 @@ export class Queries {
|
||||
|
||||
createdAt
|
||||
modifiedAt
|
||||
|
||||
history {
|
||||
id
|
||||
discordId
|
||||
xp
|
||||
server
|
||||
leftServer
|
||||
deleted
|
||||
dateFrom
|
||||
dateTo
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,21 @@
|
||||
<button pButton class="btn icon-btn" icon="pi pi-history" (click)="openHistory()"></button>
|
||||
|
||||
<p-sidebar styleClass="history p-sidebar-md" [(visible)]="showSidebar" position="right" [baseZIndex]="10000">
|
||||
<h1>{{translationKey | translate}} {{'common.history.header' | translate}}</h1>
|
||||
|
||||
<div class="entry-list">
|
||||
<div class="entry" *ngFor="let entry of history">
|
||||
<div class="attribute" *ngFor="let item of entry | keyvalue">
|
||||
<div class="key">
|
||||
{{getAttributeTranslationKey(item.key) | translate}}
|
||||
</div>
|
||||
<div class="seperator">
|
||||
->
|
||||
</div>
|
||||
<div class="value">
|
||||
{{item.value}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</p-sidebar>
|
@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { HistoryBtnComponent } from './history-btn.component';
|
||||
|
||||
describe('HistoryBtnComponent', () => {
|
||||
let component: HistoryBtnComponent;
|
||||
let fixture: ComponentFixture<HistoryBtnComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ HistoryBtnComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(HistoryBtnComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
edraft marked this conversation as resolved
|
@ -0,0 +1,31 @@
|
||||
import { Component, Input, OnInit } from "@angular/core";
|
||||
import { History } from "../../../../models/data/data.model";
|
||||
|
||||
@Component({
|
||||
selector: "app-history-btn",
|
||||
templateUrl: "./history-btn.component.html",
|
||||
styleUrls: ["./history-btn.component.scss"]
|
||||
})
|
||||
export class HistoryBtnComponent implements OnInit {
|
||||
|
||||
@Input() history: History[] = [];
|
||||
@Input() translationKey: string = "";
|
||||
|
||||
showSidebar = false;
|
||||
edraft marked this conversation as resolved
Jonas
commented
Darf showSidebar keinen Typen bekommen? :( public showSidebar: boolean = false; Je nachdem was showSidebar tut, wäre hier vielleicht ein Store als "Single-Source-Of-Truth" sinnvoll, damit wir hier ein Observable haben. Darf showSidebar keinen Typen bekommen? :(
public showSidebar: boolean = false;
Je nachdem was showSidebar tut, wäre hier vielleicht ein Store als "Single-Source-Of-Truth" sinnvoll, damit wir hier ein Observable haben.
So kann sich egal wo dieser boolische Wert anpassen und DOM-Elemente anschließened gerendert werden oder eben nicht.
Ist aber wie gesagt abhängig vom Use-Case.
edraft
commented
Generell hast du recht. Wäre für das was die Komponente macht jedoch OP. Generell hast du recht. Wäre für das was die Komponente macht jedoch OP.
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
openHistory(): void {
|
||||
console.log("history", this.history);
|
||||
this.showSidebar = true;
|
||||
}
|
||||
|
||||
getAttributeTranslationKey(key: string) {
|
||||
return `common.history.${key}`;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1 @@
|
||||
<p>history works!</p>
|
@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { HistoryComponent } from './history.component';
|
||||
|
||||
describe('HistoryComponent', () => {
|
||||
let component: HistoryComponent;
|
||||
let fixture: ComponentFixture<HistoryComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ HistoryComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(HistoryComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,10 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-history',
|
||||
templateUrl: './history.component.html',
|
||||
styleUrls: ['./history.component.scss']
|
||||
})
|
||||
export class HistoryComponent {
|
||||
|
||||
}
|
@ -1,28 +1,30 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { ButtonModule } from 'primeng/button';
|
||||
import { CheckboxModule } from 'primeng/checkbox';
|
||||
import { ConfirmDialogModule } from 'primeng/confirmdialog';
|
||||
import { DialogModule } from 'primeng/dialog';
|
||||
import { DropdownModule } from 'primeng/dropdown';
|
||||
import { DynamicDialogModule } from 'primeng/dynamicdialog';
|
||||
import { InputTextModule } from 'primeng/inputtext';
|
||||
import { MenuModule } from 'primeng/menu';
|
||||
import { PasswordModule } from 'primeng/password';
|
||||
import { ProgressSpinnerModule } from 'primeng/progressspinner';
|
||||
import { TableModule } from 'primeng/table';
|
||||
import { ToastModule } from 'primeng/toast';
|
||||
import { AuthRolePipe } from './pipes/auth-role.pipe';
|
||||
import { IpAddressPipe } from './pipes/ip-address.pipe';
|
||||
import { BoolPipe } from './pipes/bool.pipe';
|
||||
import { PanelMenuModule } from 'primeng/panelmenu';
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { HttpClientModule } from "@angular/common/http";
|
||||
import { NgModule } from "@angular/core";
|
||||
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
|
||||
import { TranslateModule } from "@ngx-translate/core";
|
||||
import { ButtonModule } from "primeng/button";
|
||||
import { CheckboxModule } from "primeng/checkbox";
|
||||
import { ConfirmDialogModule } from "primeng/confirmdialog";
|
||||
import { DialogModule } from "primeng/dialog";
|
||||
import { DropdownModule } from "primeng/dropdown";
|
||||
import { DynamicDialogModule } from "primeng/dynamicdialog";
|
||||
import { InputTextModule } from "primeng/inputtext";
|
||||
import { MenuModule } from "primeng/menu";
|
||||
import { PasswordModule } from "primeng/password";
|
||||
import { ProgressSpinnerModule } from "primeng/progressspinner";
|
||||
import { TableModule } from "primeng/table";
|
||||
import { ToastModule } from "primeng/toast";
|
||||
import { AuthRolePipe } from "./pipes/auth-role.pipe";
|
||||
import { IpAddressPipe } from "./pipes/ip-address.pipe";
|
||||
import { BoolPipe } from "./pipes/bool.pipe";
|
||||
import { PanelMenuModule } from "primeng/panelmenu";
|
||||
import { PanelModule } from "primeng/panel";
|
||||
import { InputNumberModule } from 'primeng/inputnumber';
|
||||
import { InputNumberModule } from "primeng/inputnumber";
|
||||
import { ImageModule } from "primeng/image";
|
||||
|
||||
import { SidebarModule } from "primeng/sidebar";
|
||||
import { HistoryComponent } from "./components/history/history.component";
|
||||
import { HistoryBtnComponent } from './components/history-btn/history-btn.component';
|
||||
|
||||
|
||||
@NgModule({
|
||||
@ -30,6 +32,8 @@ import { ImageModule } from "primeng/image";
|
||||
AuthRolePipe,
|
||||
IpAddressPipe,
|
||||
BoolPipe,
|
||||
HistoryComponent,
|
||||
HistoryBtnComponent,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
@ -52,7 +56,8 @@ import { ImageModule } from "primeng/image";
|
||||
PanelMenuModule,
|
||||
PanelModule,
|
||||
InputNumberModule,
|
||||
ImageModule
|
||||
ImageModule,
|
||||
SidebarModule,
|
||||
],
|
||||
exports: [
|
||||
ButtonModule,
|
||||
@ -77,7 +82,9 @@ import { ImageModule } from "primeng/image";
|
||||
IpAddressPipe,
|
||||
BoolPipe,
|
||||
InputNumberModule,
|
||||
ImageModule
|
||||
ImageModule,
|
||||
SidebarModule,
|
||||
HistoryBtnComponent
|
||||
]
|
||||
})
|
||||
export class SharedModule { }
|
||||
|
@ -224,6 +224,7 @@
|
||||
</td>
|
||||
<td>
|
||||
<div class="btn-wrapper">
|
||||
<app-history-btn [history]="member.history" translationKey="view.server.members.header"></app-history-btn>
|
||||
<button *ngIf="!editing" pButton pInitEditableRow class="btn icon-btn" icon="pi pi-pencil"
|
||||
(click)="onRowEditInit(dt, member, ri)"></button>
|
||||
<button *ngIf="!editing" pButton pInitEditableRow class="btn icon-btn" icon="pi pi-user"
|
||||
|
@ -3,7 +3,7 @@
|
||||
"WebVersion": {
|
||||
"Major": "1",
|
||||
"Minor": "0",
|
||||
"Micro": "dev247"
|
||||
"Micro": "dev246"
|
||||
},
|
||||
"Themes": [
|
||||
{
|
||||
@ -23,4 +23,4 @@
|
||||
"Name": "sh-edraft-dark-theme"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -134,7 +134,18 @@
|
||||
"modified_at": "Bearbeitet am",
|
||||
"no_entries_found": "Keine Einträge gefunden",
|
||||
"of": "von",
|
||||
"reset_filters": "Filter zurücksetzen"
|
||||
"reset_filters": "Filter zurücksetzen",
|
||||
"history": {
|
||||
"header": "Historie",
|
||||
"id": "Id",
|
||||
"discordId": "Discord Id",
|
||||
"deleted": "Gelöscht",
|
||||
"dateFrom": "Von",
|
||||
"dateTo": "Bis",
|
||||
"server": "Server",
|
||||
"leftServer": "Gegangen",
|
||||
"xp": "XP"
|
||||
}
|
||||
},
|
||||
"dialog": {
|
||||
"abort": "Abbrechen",
|
||||
|
@ -5,13 +5,39 @@
|
||||
@import "./styles/primeng-fixes.scss";
|
||||
@import "./styles/constants.scss";
|
||||
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
height: 100vh;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
font-size: 1rem;
|
||||
|
||||
$scrollbarColor: #272727;
|
||||
$scrollbarBackgroundColor: #888;
|
||||
|
||||
/* width */
|
||||
::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
/* Track */
|
||||
::-webkit-scrollbar-track {
|
||||
background-color: $scrollbarBackgroundColor;
|
||||
}
|
||||
|
||||
/* Handle */
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: $scrollbarColor;
|
||||
border: 3px solid $scrollbarBackgroundColor;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
/* Handle on hover */
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: $scrollbarColor;
|
||||
}
|
||||
}
|
||||
|
||||
main {
|
||||
@ -332,6 +358,43 @@ header {
|
||||
}
|
||||
}
|
||||
|
||||
.history {
|
||||
width: 300px;
|
||||
|
||||
.entry-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
gap: 15px;
|
||||
|
||||
.entry {
|
||||
padding-bottom: 10px;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
gap: 5px;
|
||||
|
||||
.attribute {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
|
||||
.key {
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
.seperator {
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
.value {
|
||||
width: 60%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.p-dialog-header {
|
||||
padding: 20px 20px 20px 20px !important;
|
||||
}
|
||||
|
@ -175,6 +175,17 @@
|
||||
}
|
||||
}
|
||||
|
||||
.history {
|
||||
background-color: $primaryBackgroundColor;
|
||||
color: $primaryTextColor;
|
||||
|
||||
.entry-list {
|
||||
.entry {
|
||||
border-bottom: 1px solid $primaryHeaderColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.p-dialog-header {
|
||||
background-color: $secondaryBackgroundColor !important;
|
||||
color: $primaryTextColor !important;
|
||||
|
@ -175,6 +175,17 @@
|
||||
}
|
||||
}
|
||||
|
||||
.history {
|
||||
background-color: $primaryBackgroundColor;
|
||||
color: $primaryTextColor;
|
||||
|
||||
.entry-list {
|
||||
.entry {
|
||||
border-bottom: 1px solid $primaryHeaderColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.p-dialog-header {
|
||||
background-color: $secondaryBackgroundColor !important;
|
||||
color: $primaryTextColor !important;
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
background-color: $primaryBackgroundColor;
|
||||
|
||||
|
||||
h1,
|
||||
h2 {
|
||||
color: $primaryHeaderColor;
|
||||
@ -175,6 +176,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
.history {
|
||||
background-color: $primaryBackgroundColor;
|
||||
color: $primaryTextColor;
|
||||
|
||||
.entry-list {
|
||||
.entry {
|
||||
border-bottom: 1px solid $primaryHeaderColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.p-dialog-header {
|
||||
background-color: $primaryBackgroundColor !important;
|
||||
color: $primaryTextColor !important;
|
||||
|
@ -175,6 +175,17 @@
|
||||
}
|
||||
}
|
||||
|
||||
.history {
|
||||
background-color: $primaryBackgroundColor;
|
||||
color: $primaryTextColor;
|
||||
|
||||
.entry-list {
|
||||
.entry {
|
||||
border-bottom: 1px solid $primaryHeaderColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.p-dialog-header {
|
||||
background-color: $secondaryBackgroundColor !important;
|
||||
color: $primaryTextColor !important;
|
||||
|
Loading…
Reference in New Issue
Block a user
Tests schreiben üben wir aber nochmal ^^