Skip to content

Commit 842fcfe

Browse files
committed
feat(admin): application list column customization
* Vo/Group admins can now modify the information that's displayed on the 'Applications' page * update openapi
1 parent 4002659 commit 842fcfe

File tree

16 files changed

+584
-56
lines changed

16 files changed

+584
-56
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<h1 mat-dialog-title>{{'DIALOGS.APPLICATIONS_LIST_COLUMNS_CHANGE.TITLE' | translate}}</h1>
2+
<div class="{{theme}}">
3+
<mat-spinner *ngIf="loading" class="ml-auto mr-auto"></mat-spinner>
4+
<div *ngIf="!loading" mat-dialog-content>
5+
<perun-web-apps-alert alert_type="warn">
6+
{{'DIALOGS.APPLICATIONS_LIST_COLUMNS_CHANGE.WARN' | translate}}
7+
</perun-web-apps-alert>
8+
<mat-form-field class="w-100">
9+
<mat-label
10+
>{{'DIALOGS.APPLICATIONS_LIST_COLUMNS_CHANGE.FILTER_COLUMNS' | translate}}</mat-label
11+
>
12+
<mat-select
13+
(closed)="attribute.value = selectedColumns.value"
14+
[formControl]="selectedColumns"
15+
multiple>
16+
<mat-option class="{{theme}}" *ngFor="let column of columnOptions" [value]="column">
17+
{{column | applicationColumnSelectLabel}}
18+
</mat-option>
19+
</mat-select>
20+
</mat-form-field>
21+
<perun-web-apps-alert alert_type="info">
22+
{{'DIALOGS.APPLICATIONS_LIST_COLUMNS_CHANGE.DEFAULT_TEXT' | translate}}
23+
</perun-web-apps-alert>
24+
</div>
25+
<div mat-dialog-actions>
26+
<button (click)="cancel()" mat-flat-button>
27+
{{'DIALOGS.APPLICATIONS_LIST_COLUMNS_CHANGE.CANCEL' | translate}}
28+
</button>
29+
<button (click)="default()" class="ml-auto" mat-flat-button>
30+
{{'DIALOGS.APPLICATIONS_LIST_COLUMNS_CHANGE.DEFAULT' | translate}}
31+
</button>
32+
<button (click)="confirm()" [disabled]="loading" class="ml-2" color="accent" mat-flat-button>
33+
{{'DIALOGS.APPLICATIONS_LIST_COLUMNS_CHANGE.CONFIRM' | translate}}
34+
</button>
35+
</div>
36+
</div>

apps/admin-gui/src/app/shared/components/dialogs/applications-list-columns-change-dialog/applications-list-columns-change-dialog.component.scss

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
import { Component, Inject, OnInit } from '@angular/core';
2+
import {
3+
Attribute,
4+
AttributeDefinition,
5+
AttributesManagerService,
6+
} from '@perun-web-apps/perun/openapi';
7+
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
8+
import { NotificatorService, PerunTranslateService } from '@perun-web-apps/perun/services';
9+
import { FormControl } from '@angular/forms';
10+
11+
export interface ApplicationsListColumnsChangeDialogData {
12+
theme: string;
13+
voId: number;
14+
groupId?: number;
15+
}
16+
@Component({
17+
selector: 'app-applications-list-columns-change-dialog',
18+
templateUrl: './applications-list-columns-change-dialog.component.html',
19+
styleUrls: ['./applications-list-columns-change-dialog.component.scss'],
20+
})
21+
export class ApplicationsListColumnsChangeDialogComponent implements OnInit {
22+
loading = false;
23+
theme: string;
24+
simpleColumns = ['createdAt', 'type', 'state', 'createdBy', 'modifiedBy'];
25+
columnOptions = ['createdAt', 'type', 'state', 'createdBy', 'modifiedBy'];
26+
selectedColumns = new FormControl<string[]>([]);
27+
attribute: Attribute;
28+
29+
constructor(
30+
private dialogRef: MatDialogRef<ApplicationsListColumnsChangeDialogComponent>,
31+
private attributesManager: AttributesManagerService,
32+
@Inject(MAT_DIALOG_DATA) private data: ApplicationsListColumnsChangeDialogData,
33+
private translate: PerunTranslateService,
34+
private notificator: NotificatorService
35+
) {}
36+
37+
ngOnInit(): void {
38+
this.theme = this.data.theme;
39+
if (this.data.groupId) {
40+
this.attributesManager
41+
.getIdpAttributeDefinitions()
42+
.subscribe((attrDefs: AttributeDefinition[]) => {
43+
attrDefs.forEach((attr) => {
44+
this.columnOptions.push(attr.friendlyName);
45+
});
46+
this.attributesManager
47+
.getGroupAttributeByName(
48+
this.data.groupId,
49+
'urn:perun:group:attribute-def:def:applicationViewPreferences'
50+
)
51+
.subscribe((attribute) => {
52+
this.attribute = attribute;
53+
const configuredColumns: string[] = attribute.value as string[];
54+
if (configuredColumns !== null && configuredColumns.length > 0) {
55+
this.selectedColumns.setValue(configuredColumns);
56+
} else {
57+
this.selectedColumns.setValue(this.simpleColumns);
58+
}
59+
});
60+
});
61+
} else {
62+
this.attributesManager
63+
.getIdpAttributeDefinitions()
64+
.subscribe((attrDefs: AttributeDefinition[]) => {
65+
attrDefs.forEach((attr) => {
66+
this.columnOptions.push(attr.friendlyName);
67+
});
68+
this.attributesManager
69+
.getVoAttributeByName(
70+
this.data.voId,
71+
'urn:perun:vo:attribute-def:def:applicationViewPreferences'
72+
)
73+
.subscribe((attribute) => {
74+
this.attribute = attribute;
75+
const configuredColumns: string[] = attribute.value as string[];
76+
if (configuredColumns !== null && configuredColumns.length > 0) {
77+
this.selectedColumns.setValue(configuredColumns);
78+
} else {
79+
this.selectedColumns.setValue(this.simpleColumns);
80+
}
81+
});
82+
});
83+
}
84+
}
85+
86+
confirm(): void {
87+
if (this.data.groupId) {
88+
this.changeGroupAttribute();
89+
} else {
90+
this.changeVoAttribute();
91+
}
92+
}
93+
cancel(): void {
94+
this.dialogRef.close(false);
95+
}
96+
default(): void {
97+
this.attribute.value = [];
98+
this.confirm();
99+
}
100+
101+
private changeVoAttribute(): void {
102+
this.loading = true;
103+
this.attributesManager
104+
.setVoAttribute({
105+
vo: this.data.voId,
106+
attribute: this.attribute,
107+
})
108+
.subscribe({
109+
next: () => {
110+
this.loading = false;
111+
this.dialogRef.close(true);
112+
this.notificator.showSuccess(
113+
this.translate.instant('DIALOGS.APPLICATIONS_LIST_COLUMNS_CHANGE.SUCCESS')
114+
);
115+
},
116+
error: () => {
117+
this.loading = false;
118+
},
119+
});
120+
}
121+
122+
private changeGroupAttribute(): void {
123+
this.loading = true;
124+
this.attributesManager
125+
.setGroupAttribute({
126+
group: this.data.groupId,
127+
attribute: this.attribute,
128+
})
129+
.subscribe({
130+
next: () => {
131+
this.loading = false;
132+
this.dialogRef.close(true);
133+
this.notificator.showSuccess(
134+
this.translate.instant('DIALOGS.APPLICATIONS_LIST_COLUMNS_CHANGE.SUCCESS')
135+
);
136+
},
137+
error: () => {
138+
this.loading = false;
139+
},
140+
});
141+
}
142+
}

apps/admin-gui/src/app/shared/shared.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ import { UpdateVoBanDialogComponent } from './components/dialogs/update-vo-ban-d
186186
import { UpdateResourceBanDialogComponent } from './components/dialogs/update-resource-ban-dialog/update-resource-ban-dialog.component';
187187
import { UpdateFacilityBanDialogComponent } from './components/dialogs/update-facility-ban-dialog/update-facility-ban-dialog.component';
188188
import { CopyMembersDialogComponent } from './components/dialogs/copy-members-dialog/copy-members-dialog-component';
189+
import { ApplicationsListColumnsChangeDialogComponent } from './components/dialogs/applications-list-columns-change-dialog/applications-list-columns-change-dialog.component';
189190

190191
@NgModule({
191192
imports: [
@@ -462,6 +463,7 @@ import { CopyMembersDialogComponent } from './components/dialogs/copy-members-di
462463
UpdateResourceBanDialogComponent,
463464
UpdateFacilityBanDialogComponent,
464465
CopyMembersDialogComponent,
466+
ApplicationsListColumnsChangeDialogComponent,
465467
],
466468
providers: [AnyToStringPipe, ExtSourceTypePipe, ConsentRelatedAttributePipe],
467469
})

apps/admin-gui/src/app/vos/components/applications-dynamic-list/applications-dynamic-list.component.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,12 @@
113113
<td *matCellDef="let application" mat-cell>{{application.fedInfo}}</td>
114114
</ng-container>
115115

116+
<ng-container *ngFor="let i = index; let col of fedColumns;" matColumnDef="{{col}}">
117+
<th *matHeaderCellDef mat-header-cell>{{col}}</th>
118+
<td *matCellDef="let application" mat-cell>
119+
{{getFedValue(application.fedInfo, col)}}
120+
</td>
121+
</ng-container>
116122
<ng-container *ngFor="let i = index; let col of parsedColumns;" matColumnDef="{{col}}">
117123
<th *matHeaderCellDef mat-header-cell>{{col}}</th>
118124
<td *matCellDef="let application" mat-cell>

apps/admin-gui/src/app/vos/components/applications-dynamic-list/applications-dynamic-list.component.ts

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
ApplicationFormItemData,
55
ApplicationsOrderColumn,
66
AppState,
7+
AttributesManagerService,
78
Group,
89
Member,
910
RichApplication,
@@ -42,6 +43,8 @@ export class ApplicationsDynamicListComponent implements OnInit, OnChanges, Afte
4243

4344
@Input()
4445
displayedColumns: string[] = [];
46+
@Input()
47+
fedColumns: string[] = [];
4548

4649
@Input()
4750
tableId: string;
@@ -72,20 +75,20 @@ export class ApplicationsDynamicListComponent implements OnInit, OnChanges, Afte
7275

7376
@Input()
7477
dateFrom: Date = this.yearAgo();
78+
@Input()
79+
fedAttrNames: string[] = [];
7580

7681
@Input()
7782
refreshTable = false;
78-
7983
parsedColumns: string[] = [];
80-
8184
dataSource: DynamicDataSource<Application>;
82-
8385
pageSizeOptions = TABLE_ITEMS_COUNT_OPTIONS;
8486

8587
constructor(
8688
private authResolver: GuiAuthResolver,
8789
private tableConfigService: TableConfigService,
8890
private dynamicPaginatingService: DynamicPaginatingService,
91+
private attributesManagerService: AttributesManagerService,
8992
private dialog: MatDialog
9093
) {}
9194

@@ -345,4 +348,24 @@ export class ApplicationsDynamicListComponent implements OnInit, OnChanges, Afte
345348
}
346349
return filter[0].value ?? filter[0].prefilledValue;
347350
}
351+
352+
getFedValue(fedInfo: string, colName: string): string {
353+
// looking for values between {,FED_INFO_ATTR_NAME:}
354+
const regexOtherValues = new RegExp(this.fedAttrNames.map((v) => ',' + v + ':').join('|'));
355+
if (fedInfo === null || fedInfo.length === 0) {
356+
return '';
357+
}
358+
359+
let values: string[] = [];
360+
if (fedInfo.startsWith(colName + ':')) {
361+
values = fedInfo.split(colName + ':');
362+
} else {
363+
values = fedInfo.split(',' + colName + ':');
364+
}
365+
if (values.length < 2) {
366+
return '';
367+
}
368+
values = values[1].split(regexOtherValues);
369+
return values[0].endsWith(',') ? values[0].slice(0, -1) : values[0];
370+
}
348371
}

apps/admin-gui/src/app/vos/pages/group-detail-page/group-applications/group-applications.component.html

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,10 @@ <h1 class="page-subtitle">{{'VO_DETAIL.APPLICATION.TITLE' | translate}}</h1>
22
<div>
33
<perun-web-apps-refresh-button
44
(refresh)="this.refresh=!this.refresh"></perun-web-apps-refresh-button>
5-
<button
6-
(click)="this.showAllDetails = true"
7-
*ngIf="!showAllDetails"
8-
class="mr-2"
9-
mat-stroked-button>
5+
<button (click)="this.showDetails(true)" *ngIf="!showAllDetails" class="mr-2" mat-stroked-button>
106
{{'VO_DETAIL.APPLICATION.SHOW_ALL_DETAILS' | translate}}
117
</button>
12-
<button
13-
(click)="this.showAllDetails = false"
14-
*ngIf="showAllDetails"
15-
class="mr-2"
16-
mat-stroked-button>
8+
<button (click)="this.showDetails(false)" *ngIf="showAllDetails" class="mr-2" mat-stroked-button>
179
{{'VO_DETAIL.APPLICATION.SHOW_LESS_DETAILS' | translate}}
1810
</button>
1911
<mat-form-field class="w-25 mr-2">
@@ -44,12 +36,27 @@ <h1 class="page-subtitle">{{'VO_DETAIL.APPLICATION.TITLE' | translate}}</h1>
4436
[placeholder]="'VO_DETAIL.APPLICATION.FILTER'"></perun-web-apps-debounce-filter>
4537
<perun-web-apps-date-range (datePicker)="select()" [endDate]="endDate" [startDate]="startDate">
4638
</perun-web-apps-date-range>
39+
<span
40+
[matTooltipDisabled]="!showAllDetails"
41+
matTooltip="{{'VO_DETAIL.APPLICATION.COLUMNS_TOOLTIP' | translate}}"
42+
matTooltipPosition="above">
43+
<button
44+
mat-stroked-button
45+
*ngIf="columnsAuth"
46+
[disabled]="showAllDetails"
47+
(click)="this.setColumns()">
48+
{{'VO_DETAIL.APPLICATION.SET_COLUMN_SETTINGS' | translate}}
49+
</button>
50+
</span>
4751
</div>
48-
<div>
52+
<mat-spinner *ngIf="loading" class="ml-auto mr-auto"></mat-spinner>
53+
<div *ngIf="!loading">
4954
<app-applications-dynamic-list
5055
[tableId]="showAllDetails ? detailTableId : tableId"
5156
[searchString]="filterValue"
52-
[displayedColumns]="showAllDetails ? detailedDisplayedColumns : displayedColumns"
57+
[displayedColumns]="currentColumns"
58+
[fedColumns]="configuredFedColumns"
59+
[fedAttrNames]="fedAttrNames"
5360
[states]="currentStates"
5461
[dateFrom]="startDate.value"
5562
[dateTo]="endDate.value"

0 commit comments

Comments
 (0)