diff --git a/apps/red-ui/src/app/app.module.ts b/apps/red-ui/src/app/app.module.ts index 6e2768349..8d6ee58f2 100644 --- a/apps/red-ui/src/app/app.module.ts +++ b/apps/red-ui/src/app/app.module.ts @@ -66,6 +66,8 @@ import { FileNotAvailableOverlayComponent } from './screens/file/file-not-availa import { ToastComponent } from './components/toast/toast.component'; import { FilterComponent } from './common/filter/filter.component'; import { AppInfoComponent } from './screens/app-info/app-info.component'; +import { SortingComponent } from './components/sorting/sorting.component'; +import { TableColNameComponent } from './components/table-col-name/table-col-name.component'; export function HttpLoaderFactory(httpClient: HttpClient) { return new TranslateHttpLoader(httpClient, '/assets/i18n/', '.json'); @@ -97,7 +99,9 @@ export function HttpLoaderFactory(httpClient: HttpClient) { ToastComponent, FileNotAvailableOverlayComponent, FilterComponent, - AppInfoComponent + AppInfoComponent, + SortingComponent, + TableColNameComponent ], imports: [ BrowserModule, diff --git a/apps/red-ui/src/app/components/simple-doughnut-chart/simple-doughnut-chart.component.ts b/apps/red-ui/src/app/components/simple-doughnut-chart/simple-doughnut-chart.component.ts index 3b6d55b34..dc761199b 100644 --- a/apps/red-ui/src/app/components/simple-doughnut-chart/simple-doughnut-chart.component.ts +++ b/apps/red-ui/src/app/components/simple-doughnut-chart/simple-doughnut-chart.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; +import { Component, Input, OnChanges, SimpleChanges } from '@angular/core'; import { Color } from '../../utils/types'; export class DoughnutChartConfig { diff --git a/apps/red-ui/src/app/components/sorting/sorting.component.html b/apps/red-ui/src/app/components/sorting/sorting.component.html new file mode 100644 index 000000000..9cee56903 --- /dev/null +++ b/apps/red-ui/src/app/components/sorting/sorting.component.html @@ -0,0 +1,11 @@ + + + + {{ option.label | translate }} + + + diff --git a/apps/red-ui/src/app/components/sorting/sorting.component.scss b/apps/red-ui/src/app/components/sorting/sorting.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/apps/red-ui/src/app/components/sorting/sorting.component.ts b/apps/red-ui/src/app/components/sorting/sorting.component.ts new file mode 100644 index 000000000..c49a538eb --- /dev/null +++ b/apps/red-ui/src/app/components/sorting/sorting.component.ts @@ -0,0 +1,86 @@ +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; + +export class SortingOption { + label?: string; + order: 'asc' | 'desc'; + column: string; +} + +const SORTING_OPTIONS: { [key: string]: SortingOption[] } = { + 'project-listing': [ + { label: 'sorting.recent.label', order: 'desc', column: 'projectDate' }, + { label: 'sorting.alphabetically.label', order: 'asc', column: 'project.projectName' } + ], + 'project-overview': [ + { label: 'sorting.recent.label', order: 'desc', column: 'added' }, + { label: 'sorting.alphabetically.label', order: 'asc', column: 'filename' }, + { label: 'sorting.number-of-pages.label', order: 'asc', column: 'numberOfPages' }, + { label: 'sorting.number-of-analyses.label', order: 'desc', column: 'numberOfAnalyses' } + ] +}; + +@Component({ + selector: 'redaction-sorting', + templateUrl: './sorting.component.html', + styleUrls: ['./sorting.component.scss'] +}) +export class SortingComponent implements OnInit { + @Input() + private type: 'project-overview' | 'project-listing'; + + @Output() + private optionChanged = new EventEmitter(); + + public sortingOptions: SortingOption[]; + public activeOption: SortingOption; + + constructor() {} + + public ngOnInit(): void {} + + private _addCustomOption(option: Partial) { + const customOption = { + label: 'sorting.custom.label', + column: option.column, + order: option.order + }; + this.sortingOptions.push(customOption); + this.activeOption = customOption; + } + + private _resetOptions() { + if (this.activeOption?.label !== 'sorting.custom.label') { + this.sortingOptions = [...SORTING_OPTIONS[this.type]]; + } + } + + public dropdownSelect() { + this._resetOptions(); + this.optionChanged.emit(this.activeOption); + } + + public setOption(option: { column: string; order: 'asc' | 'desc' }) { + if (!this.sortingOptions) { + this._resetOptions(); + } + const existingOption = this.sortingOptions.find( + (o) => o.column === option.column && o.order === option.order + ); + if (existingOption) { + this.activeOption = existingOption; + this._resetOptions(); + } else { + this._addCustomOption(option); + } + } + + public toggleSort(column: string) { + if (this.activeOption.column === column) { + const currentOrder = this.activeOption.order; + this.setOption({ column, order: currentOrder === 'asc' ? 'desc' : 'asc' }); + } else { + this.setOption({ column, order: 'asc' }); + } + this.optionChanged.emit(this.activeOption); + } +} diff --git a/apps/red-ui/src/app/components/table-col-name/table-col-name.component.html b/apps/red-ui/src/app/components/table-col-name/table-col-name.component.html new file mode 100644 index 000000000..b4807f83a --- /dev/null +++ b/apps/red-ui/src/app/components/table-col-name/table-col-name.component.html @@ -0,0 +1,7 @@ +
+ +
+ + +
+
diff --git a/apps/red-ui/src/app/components/table-col-name/table-col-name.component.scss b/apps/red-ui/src/app/components/table-col-name/table-col-name.component.scss new file mode 100644 index 000000000..db3f92bf2 --- /dev/null +++ b/apps/red-ui/src/app/components/table-col-name/table-col-name.component.scss @@ -0,0 +1,23 @@ +@import '../../../assets/styles/red-variables'; + +:host { + display: flex; + border-bottom: 1px solid $separator; + + > div { + align-items: center; + text-transform: uppercase; + display: flex; + font-weight: 600; + gap: 8px; + padding: 8px 16px; + } + + .sort-arrows-container { + mat-icon { + display: block; + width: 6px; + height: 11px; + } + } +} diff --git a/apps/red-ui/src/app/components/table-col-name/table-col-name.component.ts b/apps/red-ui/src/app/components/table-col-name/table-col-name.component.ts new file mode 100644 index 000000000..a127a3292 --- /dev/null +++ b/apps/red-ui/src/app/components/table-col-name/table-col-name.component.ts @@ -0,0 +1,34 @@ +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { SortingOption } from '../sorting/sorting.component'; + +@Component({ + selector: 'redaction-table-col-name', + templateUrl: './table-col-name.component.html', + styleUrls: ['./table-col-name.component.scss'] +}) +export class TableColNameComponent implements OnInit { + @Input() public activeSortingOption: SortingOption; + @Input() public column: string; + @Input() public label: string; + @Input() public withSort = false; + @Input() public class: string; + @Output() public toggleSort = new EventEmitter(); + + constructor() {} + + ngOnInit(): void {} + + public get arrowColor(): { up: string; down: string } { + const up = + this.activeSortingOption.order === 'desc' && + this.activeSortingOption.column === this.column + ? 'primary' + : 'accent'; + const down = + this.activeSortingOption.order === 'asc' && + this.activeSortingOption.column === this.column + ? 'primary' + : 'accent'; + return { up, down }; + } +} diff --git a/apps/red-ui/src/app/screens/project-listing-screen/project-listing-screen.component.html b/apps/red-ui/src/app/screens/project-listing-screen/project-listing-screen.component.html index 61c600699..6938c8a87 100644 --- a/apps/red-ui/src/app/screens/project-listing-screen/project-listing-screen.component.html +++ b/apps/red-ui/src/app/screens/project-listing-screen/project-listing-screen.component.html @@ -74,37 +74,34 @@ [class.active]="bulkSelectActive" (click)="toggleBulkSelect()" > - - - - {{ option.label | translate }} - - - + +
-
+
-
- -
-
- -
-
- -
+ + + + +
{ @@ -112,7 +108,7 @@ export class ProjectListingScreenComponent implements OnInit { }); } - downloadRedactionReport($event: MouseEvent, project: Project) { + public downloadRedactionReport($event: MouseEvent, project: Project) { $event.preventDefault(); this.appStateService.downloadRedactionReport(project); } @@ -125,6 +121,10 @@ export class ProjectListingScreenComponent implements OnInit { this.bulkSelectActive = !this.bulkSelectActive; } + public sortingOptionChanged(option: SortingOption) { + this.sortingOption = option; + } + public getProjectStatusConfig(pw: ProjectWrapper) { const obj = pw.files.reduce((acc, file) => { const status = file.status; @@ -141,7 +141,7 @@ export class ProjectListingScreenComponent implements OnInit { .map((status) => ({ length: obj[status], color: status })); } - reanalyseProject($event: MouseEvent, project: Project) { + public reanalyseProject($event: MouseEvent, project: Project) { $event.preventDefault(); this.appStateService.reanalyseProject(project); } @@ -199,7 +199,7 @@ export class ProjectListingScreenComponent implements OnInit { }); } - filtersChanged() { + public filtersChanged() { this._filterProjects(); } diff --git a/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.html b/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.html index 75acce1d5..8ac32aad1 100644 --- a/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.html +++ b/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.html @@ -80,64 +80,50 @@ [class.active]="bulkSelectActive" (click)="toggleBulkSelect()" >
- - - - {{ option.label | translate }} - - - +
-
+
-
- -
+ -
- -
- - -
-
+ -
- -
+ -
- -
+ -
- -
+
div { height: 80px; - border-bottom: 1px solid rgba(226, 228, 233, 0.9); + border-bottom: 1px solid $separator; padding: 0 16px; }