Add chart to repository details

Signed-off-by: trivernis <trivernis@protonmail.com>
pull/17/head
trivernis 3 years ago
parent bf8e70f846
commit 39978ff34c
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

@ -40,7 +40,9 @@
"src/styles.scss", "src/styles.scss",
"src/material-theme-correction.scss" "src/material-theme-correction.scss"
], ],
"scripts": [] "scripts": [
"./node_modules/chart.js/dist/chart.js"
]
}, },
"configurations": { "configurations": {
"production": { "production": {
@ -114,7 +116,9 @@
"src/styles.scss", "src/styles.scss",
"src/material-theme-correction.scss" "src/material-theme-correction.scss"
], ],
"scripts": [] "scripts": [
"./node_modules/chart.js/dist/chart.js"
]
} }
}, },
"lint": { "lint": {

@ -28,6 +28,7 @@
"@ng-icons/feather-icons": "^13.2.1", "@ng-icons/feather-icons": "^13.2.1",
"@ng-icons/material-icons": "^13.2.1", "@ng-icons/material-icons": "^13.2.1",
"@tauri-apps/api": "^1.0.0-beta.8", "@tauri-apps/api": "^1.0.0-beta.8",
"chart.js": "^3.7.1",
"primeicons": "^5.0.0", "primeicons": "^5.0.0",
"primeng": "^13.0.4", "primeng": "^13.0.4",
"rxjs": "~7.5.2", "rxjs": "~7.5.2",
@ -59,4 +60,4 @@
"karma-jasmine-html-reporter": "~1.7.0", "karma-jasmine-html-reporter": "~1.7.0",
"typescript": "~4.5.4" "typescript": "~4.5.4"
} }
} }

@ -89,7 +89,7 @@ import {AboutDialogComponent} from "./repositories-tab/repository-overview/about
MatInputModule, MatInputModule,
TagModule, TagModule,
RepositoryModule, RepositoryModule,
MatToolbarModule, MatToolbarModule
] ]
}) })
export class CoreModule { export class CoreModule {

@ -1,6 +1,4 @@
<div *ngIf="!this.selectedRepository"> <app-repository-overview *ngIf="!this.selectedRepository"></app-repository-overview>
<app-repository-overview></app-repository-overview> <app-repository-details-view *ngIf="this.selectedRepository"
</div> [repository]="this.selectedRepository"></app-repository-details-view>
<div *ngIf="this.selectedRepository" class="repo-details">
<app-repository-details-view [repository]="this.selectedRepository"></app-repository-details-view>
</div>

@ -4,45 +4,56 @@
</button> </button>
</mat-toolbar> </mat-toolbar>
<div class="details-content" fxLayout="row"> <div class="details-content" fxLayout="row">
<div class="repository-metadata" fxFlex="100%"> <div class="repository-metadata" fxFlex="50%">
<h1>Stats</h1> <div class="stats-container">
<app-metadata-entry *ngIf="repository.path" attributeName="Path">{{repository.path}}</app-metadata-entry> <h1>Stats</h1>
<app-metadata-entry *ngIf="repository.address" <app-metadata-entry *ngIf="repository.path" attributeName="Path">{{repository.path}}</app-metadata-entry>
attributeName="Address">{{repository.address}}</app-metadata-entry> <app-metadata-entry *ngIf="repository.address"
<app-metadata-entry attributeName="File Count"> attributeName="Address">{{repository.address}}</app-metadata-entry>
<mat-progress-bar *ngIf="!metadata"></mat-progress-bar> <app-metadata-entry attributeName="File Count">
{{metadata ? metadata!.file_count.toString() : ''}} <mat-progress-bar *ngIf="!metadata"></mat-progress-bar>
</app-metadata-entry> {{metadata ? metadata!.file_count.toString() : ''}}
<app-metadata-entry attributeName="Tag Count"> </app-metadata-entry>
<mat-progress-bar *ngIf="!metadata"></mat-progress-bar> <app-metadata-entry attributeName="Tag Count">
{{metadata ? metadata!.tag_count.toString() : ''}} <mat-progress-bar *ngIf="!metadata"></mat-progress-bar>
</app-metadata-entry> {{metadata ? metadata!.tag_count.toString() : ''}}
<app-metadata-entry attributeName="Namespace Count"> </app-metadata-entry>
<mat-progress-bar *ngIf="!metadata"></mat-progress-bar> <app-metadata-entry attributeName="Namespace Count">
{{metadata ? metadata!.namespace_count.toString() : ''}} <mat-progress-bar *ngIf="!metadata"></mat-progress-bar>
</app-metadata-entry> {{metadata ? metadata!.namespace_count.toString() : ''}}
<app-metadata-entry attributeName="Mapping Count"> </app-metadata-entry>
<mat-progress-bar *ngIf="!metadata"></mat-progress-bar> <app-metadata-entry attributeName="Mapping Count">
{{metadata ? metadata!.mapping_count.toString() : ''}} <mat-progress-bar *ngIf="!metadata"></mat-progress-bar>
</app-metadata-entry> {{metadata ? metadata!.mapping_count.toString() : ''}}
<app-metadata-entry attributeName="Total Size"> </app-metadata-entry>
<mat-progress-bar *ngIf="(this.totalSize | async) === undefined" mode="indeterminate"></mat-progress-bar> <app-metadata-entry attributeName="Total Size">
{{this.totalSize | async}} <mat-progress-bar *ngIf="(this.totalSize | async) === undefined"
</app-metadata-entry> mode="indeterminate"></mat-progress-bar>
<app-metadata-entry attributeName="File Folder Size"> {{this.totalSize | async}}
<mat-progress-bar *ngIf="(this.fileFolderSize | async) === undefined" </app-metadata-entry>
mode="indeterminate"></mat-progress-bar> <app-metadata-entry attributeName="File Folder Size">
{{this.fileFolderSize | async}} <mat-progress-bar *ngIf="(this.fileFolderSize | async) === undefined"
</app-metadata-entry> mode="indeterminate"></mat-progress-bar>
<app-metadata-entry attributeName="Thumbnail Folder Size"> {{this.fileFolderSize | async}}
<mat-progress-bar *ngIf="(this.thumbFolderSize | async) === undefined" </app-metadata-entry>
mode="indeterminate"></mat-progress-bar> <app-metadata-entry attributeName="Thumbnail Folder Size">
{{this.thumbFolderSize | async}} <mat-progress-bar *ngIf="(this.thumbFolderSize | async) === undefined"
</app-metadata-entry> mode="indeterminate"></mat-progress-bar>
<app-metadata-entry attributeName="Database File Size"> {{this.thumbFolderSize | async}}
<mat-progress-bar *ngIf="(this.databaseFileSize | async) === undefined" </app-metadata-entry>
mode="indeterminate"></mat-progress-bar> <app-metadata-entry attributeName="Database File Size">
{{this.databaseFileSize | async}} <mat-progress-bar *ngIf="(this.databaseFileSize | async) === undefined"
</app-metadata-entry> mode="indeterminate"></mat-progress-bar>
{{this.databaseFileSize | async}}
</app-metadata-entry>
</div>
</div>
<div class="repository-charts" fxFlex="50%">
<app-chart *ngIf="this.chartData"
[datasets]="this.chartData"
[labels]="this.chartLabels"
chartType="doughnut"
class="size-chart"
title="Sizes"></app-chart>
</div> </div>
</div> </div>

@ -1,3 +1,10 @@
@import "src/colors";
:host {
height: 100%;
width: 100%;
}
.repository-name { .repository-name {
text-align: center; text-align: center;
align-self: center; align-self: center;
@ -12,16 +19,38 @@
padding: 1em 1em 1em 3em; padding: 1em 1em 1em 3em;
overflow-y: auto; overflow-y: auto;
user-select: none; user-select: none;
margin-left: 20%; margin-top: 4em;
margin-right: 20%; margin-left: 2em;
display: flex;
app-metadata-entry { app-metadata-entry {
margin-bottom: 0.5em; margin-bottom: 0.5em;
display: block; display: block;
} }
.stats-container {
margin-left: auto;
margin-right: auto;
}
}
.repository-charts {
margin-top: 4em;
margin-right: 2em;
height: 100%;
display: block;
} }
.details-content { .details-content {
height: calc(100% - 64px); height: calc(100% - 64px);
overflow: hidden; overflow: hidden;
width: 75%;
margin: auto;
background: $background-lighter-05;
}
.size-chart {
min-height: 50%;
max-width: 500px;
margin: auto;
} }

@ -5,6 +5,7 @@ import {RepositoryMetadata} from "../../../../models/RepositoryMetadata";
import {BehaviorSubject} from "rxjs"; import {BehaviorSubject} from "rxjs";
import {MatDialog} from "@angular/material/dialog"; import {MatDialog} from "@angular/material/dialog";
import {BusyDialogComponent} from "../../../shared/app-common/busy-dialog/busy-dialog.component"; import {BusyDialogComponent} from "../../../shared/app-common/busy-dialog/busy-dialog.component";
import {Dataset} from "../../../shared/app-common/chart/chart.component";
@Component({ @Component({
selector: "app-repository-details-view", selector: "app-repository-details-view",
@ -19,9 +20,14 @@ export class RepositoryDetailsViewComponent implements OnInit, OnChanges, OnDest
public fileFolderSize = new BehaviorSubject<string | undefined>(undefined); public fileFolderSize = new BehaviorSubject<string | undefined>(undefined);
public thumbFolderSize = new BehaviorSubject<string | undefined>(undefined); public thumbFolderSize = new BehaviorSubject<string | undefined>(undefined);
public databaseFileSize = new BehaviorSubject<string | undefined>(undefined); public databaseFileSize = new BehaviorSubject<string | undefined>(undefined);
public chartData?: Dataset[];
public chartLabels = ["Files", "Thumbnails", "Database"];
private refreshMetadataInterval?: number; private refreshMetadataInterval?: number;
constructor(private repoService: RepositoryService, public dialog: MatDialog) { constructor(
private repoService: RepositoryService,
public dialog: MatDialog
) {
} }
public async ngOnInit() { public async ngOnInit() {
@ -63,6 +69,11 @@ export class RepositoryDetailsViewComponent implements OnInit, OnChanges, OnDest
this.thumbFolderSize.next(this.formatByteSize(thumbSize.size)); this.thumbFolderSize.next(this.formatByteSize(thumbSize.size));
const databaseSize = await this.repoService.getSize("DatabaseFile"); const databaseSize = await this.repoService.getSize("DatabaseFile");
this.databaseFileSize.next(this.formatByteSize(databaseSize.size)); this.databaseFileSize.next(this.formatByteSize(databaseSize.size));
this.chartData = [
{
data: [fileSize.size, thumbSize.size, databaseSize.size],
},
];
} }
public formatByteSize(size: number): string { public formatByteSize(size: number): string {

@ -1,10 +1,17 @@
:host {
height: 100%;
width: 100%;
display: block;
overflow: hidden;
}
.repository-container { .repository-container {
padding: 1em; padding: 1em;
margin: auto; margin: auto;
display: block; display: block;
width: calc(600px - 2em); width: calc(600px - 2em);
float: left; float: left;
app-repository-card { app-repository-card {
display: block; display: block;
position: relative; position: relative;
@ -31,7 +38,8 @@
flex-wrap: wrap; flex-wrap: wrap;
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
height: calc(100% - 5em); max-height: calc(100% - 2em);
padding-bottom: 2em;
} }

@ -26,6 +26,8 @@ import {FlapButtonComponent} from "./flap-button/flap-button.component";
import {MiddleCenteredComponent} from "./middle-centered/middle-centered.component"; import {MiddleCenteredComponent} from "./middle-centered/middle-centered.component";
import {FormatBytesPipe} from "./pipes/format-bytes.pipe"; import {FormatBytesPipe} from "./pipes/format-bytes.pipe";
import {ExternalUrlComponent} from "./external-url/external-url.component"; import {ExternalUrlComponent} from "./external-url/external-url.component";
import {ChartComponent} from "./chart/chart.component";
import {ChartModule} from "primeng/chart";
@NgModule({ @NgModule({
@ -46,6 +48,7 @@ import {ExternalUrlComponent} from "./external-url/external-url.component";
MiddleCenteredComponent, MiddleCenteredComponent,
FormatBytesPipe, FormatBytesPipe,
ExternalUrlComponent, ExternalUrlComponent,
ChartComponent,
], ],
exports: [ exports: [
ConfirmDialogComponent, ConfirmDialogComponent,
@ -63,6 +66,7 @@ import {ExternalUrlComponent} from "./external-url/external-url.component";
MiddleCenteredComponent, MiddleCenteredComponent,
FormatBytesPipe, FormatBytesPipe,
ExternalUrlComponent, ExternalUrlComponent,
ChartComponent,
], ],
imports: [ imports: [
CommonModule, CommonModule,
@ -74,7 +78,8 @@ import {ExternalUrlComponent} from "./external-url/external-url.component";
MatProgressBarModule, MatProgressBarModule,
MatSidenavModule, MatSidenavModule,
FlexLayoutModule, FlexLayoutModule,
MatRippleModule MatRippleModule,
ChartModule
] ]
}) })
export class AppCommonModule { export class AppCommonModule {

@ -0,0 +1,2 @@
<h2 *ngIf="this.title" class="title">{{this.title}}</h2>
<p-chart [data]="data" [options]="this.options" [type]="this.chartType ?? ''"></p-chart>

@ -0,0 +1,15 @@
:host {
height: 100%;
width: 100%;
display: block;
}
.title {
width: 100%;
text-align: center;
}
p-chart {
height: 100%;
width: 100%;
}

@ -0,0 +1,25 @@
import {ComponentFixture, TestBed} from "@angular/core/testing";
import {ChartComponent} from "./chart.component";
describe("ChartComponent", () => {
let component: ChartComponent;
let fixture: ComponentFixture<ChartComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ChartComponent]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(ChartComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it("should create", () => {
expect(component).toBeTruthy();
});
});

@ -0,0 +1,86 @@
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, SimpleChanges} from "@angular/core";
import {ChartOptions} from "chart.js";
export type ChartType = "doughnut";
export type Dataset = {
label?: string,
data: number[],
};
@Component({
selector: "app-chart",
templateUrl: "./chart.component.html",
styleUrls: ["./chart.component.scss"],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChartComponent implements OnChanges {
@Input() chartType?: ChartType;
@Input() title?: string;
@Input() labels: string[] = [];
@Input() datasets: Dataset[] = [];
public data: any = {};
public options: ChartOptions = {
responsive: true,
elements: {
arc: {
borderWidth: 0
}
},
plugins: {
legend: {
labels: {
color: "#FFF",
boxHeight: 20,
font: {
size: 16,
}
},
},
tooltip: {
titleFont: {
size: 16
},
bodyFont: {
size: 14
}
}
}
};
private readonly colors = [
"#771e86",
"#4650b5",
"#0073d0",
"#0091d6",
"#00aacb",
"#00c0b7"
];
constructor(private changeDetector: ChangeDetectorRef) {
}
public ngOnChanges(changes: SimpleChanges): void {
if (changes["labels"] || changes["dataset"]) {
this.generateData();
this.changeDetector.markForCheck();
}
if (changes["chartType"]) {
this.changeDetector.markForCheck();
}
}
private generateData() {
this.data = {
labels: this.labels,
datasets: this.datasets.map(set => {
return {
label: set.label,
data: set.data,
backgroundColor: this.colors,
hoverBackgroundColor: this.colors,
};
})
};
}
}

@ -2776,6 +2776,11 @@ chardet@^0.7.0:
resolved "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz" resolved "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz"
integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
chart.js@^3.7.1:
version "3.7.1"
resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-3.7.1.tgz#0516f690c6a8680c6c707e31a4c1807a6f400ada"
integrity sha512-8knRegQLFnPQAheZV8MjxIXc5gQEfDFD897BJgv/klO/vtIyFFmgMXrNfgrXpbTr/XbTturxRgxIXx/Y+ASJBA==
"chokidar@>=3.0.0 <4.0.0", chokidar@^3.0.0, chokidar@^3.5.1, chokidar@^3.5.2: "chokidar@>=3.0.0 <4.0.0", chokidar@^3.0.0, chokidar@^3.5.1, chokidar@^3.5.2:
version "3.5.3" version "3.5.3"
resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz" resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz"

Loading…
Cancel
Save