Skip to content

Commit cd7ed8e

Browse files
bodnaraxkureck
authored andcommitted
feat(admin): hierarchical organizations
* created page for managing hierarchical organizations * created dialog to add member org. * created dialog to remove member org. * added icon for hierarchical org.
1 parent 061bbcd commit cd7ed8e

23 files changed

+688
-29
lines changed

apps/admin-gui/src/app/shared/side-menu/side-menu-item.service.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -350,13 +350,13 @@ export class SideMenuItemService {
350350
};
351351
}
352352

353-
parseVo(vo: Vo): SideMenuItem {
353+
parseVo(vo: Vo, isHierarchical = false): SideMenuItem {
354354
return {
355355
label: vo.name,
356356
baseLink: [`/organizations/${vo.id}`],
357357
links: this.getVoLinks(vo),
358358
colorClass: 'vo-item',
359-
icon: 'perun-vo',
359+
icon: isHierarchical ? 'perun-hierarchical-vo' : 'perun-vo',
360360
// labelClass: 'vo-text',
361361
activatedClass: 'dark-item-activated',
362362
linksClass: 'dark-item-links',
@@ -626,6 +626,15 @@ export class SideMenuItemService {
626626
});
627627
}
628628

629+
// Member organizations
630+
if (this.authResolver.isPerunAdmin()) {
631+
children.push({
632+
label: 'MENU_ITEMS.VO.MEMBER_ORGANIZATIONS',
633+
url: [`/organizations/${vo.id}/settings/memberOrganizations`],
634+
activatedRegex: '/organizations/\\d+/settings/memberOrganizations',
635+
});
636+
}
637+
629638
links.push({
630639
label: 'MENU_ITEMS.VO.SETTINGS',
631640
url: [`/organizations/${vo.id}/settings`],
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{{title | translate}}:
2+
<span *ngFor="let vo of vos.slice(0, 3); let i = index">
3+
<a
4+
class="vo-link pointer"
5+
[perunWebAppsForceRouterLink]="['/organizations/', vo.id]"
6+
queryParamsHandling="merge"
7+
>{{vo.name}}</a
8+
><span *ngIf="vos.length > i+1">, </span>
9+
</span>
10+
<span *ngIf="vos.length > 3">&#8230;</span>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.pointer {
2+
cursor: pointer;
3+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Component, Input } from '@angular/core';
2+
import { Vo } from '@perun-web-apps/perun/openapi';
3+
4+
@Component({
5+
selector: 'app-related-vos',
6+
templateUrl: './related-vos.component.html',
7+
styleUrls: ['./related-vos.component.scss'],
8+
})
9+
export class RelatedVosComponent {
10+
@Input() title: string;
11+
@Input() vos: Vo[] = [];
12+
}

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<mat-icon
77
matTooltip="{{ 'VO_DETAIL.ENTITY' | translate }}"
88
[ngStyle]="{'color':'black'}"
9-
svgIcon="perun-vo-black"
9+
[svgIcon]="enrichedVo.memberVos.length !== 0 ? 'perun-hierarchical-vo' :'perun-vo-black'"
1010
class="perun-icon perun-icon-detail">
1111
</mat-icon>
1212
<div class="page-title-block">
@@ -33,7 +33,15 @@
3333
</div>
3434
<span class="mt-1 entity-info">
3535
{{'VO_DETAIL.ENTITY' | translate}}, {{'VO_DETAIL.SHORT_NAME' | translate}}:
36-
{{vo.shortName}}
36+
{{vo.shortName + ((enrichedVo.memberVos.length + enrichedVo.parentVos.length) ? ',' : '')}}
37+
<app-related-vos
38+
*ngIf="enrichedVo.memberVos.length !== 0"
39+
[title]="'VO_DETAIL.RELATED_VOS.MEMBER_VOS'"
40+
[vos]="enrichedVo.memberVos"></app-related-vos>
41+
<app-related-vos
42+
*ngIf="enrichedVo.parentVos.length !== 0"
43+
[title]="'VO_DETAIL.RELATED_VOS.PARENT_VOS'"
44+
[vos]="enrichedVo.parentVos"></app-related-vos>
3745
</span>
3846
</div>
3947
</div>

apps/admin-gui/src/app/vos/pages/vo-detail-page/vo-detail-page.component.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { SideMenuService } from '../../../core/services/common/side-menu.service
33
import { ActivatedRoute, Router } from '@angular/router';
44
import { SideMenuItemService } from '../../../shared/side-menu/side-menu-item.service';
55
import { fadeIn } from '@perun-web-apps/perun/animations';
6-
import { Vo, VosManagerService } from '@perun-web-apps/perun/openapi';
6+
import { EnrichedVo, Vo, VosManagerService } from '@perun-web-apps/perun/openapi';
77
import {
88
addRecentlyVisited,
99
addRecentlyVisitedObject,
@@ -38,6 +38,7 @@ export class VoDetailPageComponent implements OnInit {
3838
) {}
3939

4040
vo: Vo;
41+
enrichedVo: EnrichedVo;
4142
editAuth: boolean;
4243
loading = false;
4344
removeAuth: boolean;
@@ -54,10 +55,11 @@ export class VoDetailPageComponent implements OnInit {
5455
this.route.params.subscribe((params) => {
5556
const voId = params['voId'];
5657

57-
this.voService.getVoById(voId).subscribe(
58-
(vo) => {
59-
this.vo = vo;
60-
this.entityStorageService.setEntity({ id: vo.id, beanName: vo.beanName });
58+
this.voService.getEnrichedVoById(voId).subscribe(
59+
(enrichedVo) => {
60+
this.vo = enrichedVo.vo;
61+
this.enrichedVo = enrichedVo;
62+
this.entityStorageService.setEntity({ id: this.vo.id, beanName: this.vo.beanName });
6163
this.editAuth = this.authResolver.isAuthorized('updateVo_Vo_policy', [this.vo]);
6264
this.removeAuth = this.authResolver.isAuthorized('deleteVo_Vo_policy', [this.vo]);
6365

@@ -94,7 +96,8 @@ export class VoDetailPageComponent implements OnInit {
9496
}
9597

9698
setMenuItems() {
97-
const sideMenuItem = this.sideMenuItemService.parseVo(this.vo);
99+
const isHierarchical = this.enrichedVo.memberVos.length !== 0;
100+
const sideMenuItem = this.sideMenuItemService.parseVo(this.vo, isHierarchical);
98101

99102
this.sideMenuService.setAccessMenuItems([sideMenuItem]);
100103
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<div class="{{theme}}">
2+
<h1 mat-dialog-title>
3+
{{'VO_DETAIL.SETTINGS.MEMBER_ORGANIZATIONS.ADD_MEMBER_ORGANIZATION.TITLE' | translate}}
4+
</h1>
5+
<mat-spinner *ngIf="loading" class="ml-auto mr-auto"></mat-spinner>
6+
<div *ngIf="!loading" mat-dialog-content class="dialog-container">
7+
<mat-stepper #stepper [linear]="true">
8+
<mat-step>
9+
<ng-template
10+
matStepLabel
11+
>{{'VO_DETAIL.SETTINGS.MEMBER_ORGANIZATIONS.ADD_MEMBER_ORGANIZATION.SELECTION_STEP' | translate}}</ng-template
12+
>
13+
<perun-web-apps-immediate-filter
14+
(filter)="voFilter = $event"
15+
[placeholder]="'VO_DETAIL.SETTINGS.MEMBER_ORGANIZATIONS.ADD_MEMBER_ORGANIZATION.FILTER_VOS'"></perun-web-apps-immediate-filter>
16+
<perun-web-apps-vos-list
17+
[vos]="vos"
18+
[selection]="voSelection"
19+
[displayedColumns]="displayedColumns"
20+
[filterValue]="voFilter"
21+
[disableRouting]="true"></perun-web-apps-vos-list>
22+
</mat-step>
23+
<mat-step>
24+
<ng-template
25+
matStepLabel
26+
>{{'VO_DETAIL.SETTINGS.MEMBER_ORGANIZATIONS.ADD_MEMBER_ORGANIZATION.CONFIRMATION_STEP' | translate}}</ng-template
27+
>
28+
<ng-template matStepContent>
29+
<span
30+
>{{'VO_DETAIL.SETTINGS.MEMBER_ORGANIZATIONS.ADD_MEMBER_ORGANIZATION.CONFIRM' | translate}}</span
31+
>
32+
<table mat-table [dataSource]="voSelection.selected" class="w-100">
33+
<ng-container matColumnDef="name">
34+
<th mat-header-cell *matHeaderCellDef></th>
35+
<td mat-cell *matCellDef="let vo">{{vo.name}}</td>
36+
</ng-container>
37+
<tr mat-header-row *matHeaderRowDef="columns"></tr>
38+
<tr mat-row *matRowDef="let vo; columns: columns;"></tr>
39+
</table>
40+
<app-alert
41+
[alert_type]="'warn'"
42+
>{{'VO_DETAIL.SETTINGS.MEMBER_ORGANIZATIONS.ADD_MEMBER_ORGANIZATION.WARNING' | translate}}</app-alert
43+
>
44+
</ng-template>
45+
</mat-step>
46+
</mat-stepper>
47+
</div>
48+
<div *ngIf="!loading" mat-dialog-actions>
49+
<button (click)="close()" mat-flat-button>
50+
{{'VO_DETAIL.SETTINGS.MEMBER_ORGANIZATIONS.ADD_MEMBER_ORGANIZATION.CANCEL' | translate}}
51+
</button>
52+
<div class="ml-auto">
53+
<button
54+
*ngIf="stepper !== undefined && stepper.selectedIndex !== 0"
55+
(click)="stepperPrevious()"
56+
mat-flat-button>
57+
{{'VO_DETAIL.SETTINGS.MEMBER_ORGANIZATIONS.ADD_MEMBER_ORGANIZATION.BACK' | translate}}
58+
</button>
59+
<button
60+
*ngIf="stepper?.selectedIndex !== 1"
61+
(click)="stepperNext()"
62+
[disabled]="voSelection.selected.length === 0"
63+
color="accent"
64+
mat-flat-button>
65+
{{'VO_DETAIL.SETTINGS.MEMBER_ORGANIZATIONS.ADD_MEMBER_ORGANIZATION.NEXT' | translate}}
66+
</button>
67+
<button
68+
*ngIf="stepper?.selectedIndex === 1"
69+
(click)="addMemberOrganization()"
70+
color="accent"
71+
mat-flat-button>
72+
{{'VO_DETAIL.SETTINGS.MEMBER_ORGANIZATIONS.ADD_MEMBER_ORGANIZATION.ADD' | translate}}
73+
</button>
74+
</div>
75+
</div>
76+
</div>

apps/admin-gui/src/app/vos/pages/vo-detail-page/vo-settings/vo-settings-member-organizations/add-member-organization-dialog/add-member-organization-dialog.component.scss

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { Component, OnInit, ViewChild } from '@angular/core';
2+
import { Vo, VosManagerService } from '@perun-web-apps/perun/openapi';
3+
import { MatDialogRef } from '@angular/material/dialog';
4+
import { SelectionModel } from '@angular/cdk/collections';
5+
import { MatStepper } from '@angular/material/stepper';
6+
import { EntityStorageService, NotificatorService } from '@perun-web-apps/perun/services';
7+
import { TranslateService } from '@ngx-translate/core';
8+
9+
@Component({
10+
selector: 'app-add-member-organization-dialog',
11+
templateUrl: './add-member-organization-dialog.component.html',
12+
styleUrls: ['./add-member-organization-dialog.component.scss'],
13+
})
14+
export class AddMemberOrganizationDialogComponent implements OnInit {
15+
@ViewChild('stepper') stepper: MatStepper;
16+
loading = false;
17+
theme = 'vo-theme';
18+
displayedColumns: string[] = ['checkbox', 'id', 'name', 'shortName'];
19+
columns: string[] = ['name'];
20+
voId: number;
21+
vos: Vo[] = [];
22+
voSelection: SelectionModel<Vo> = new SelectionModel<Vo>(false, []);
23+
voFilter = '';
24+
25+
constructor(
26+
private dialogRef: MatDialogRef<AddMemberOrganizationDialogComponent>,
27+
private vosService: VosManagerService,
28+
private entityStorage: EntityStorageService,
29+
private notificator: NotificatorService,
30+
private translator: TranslateService
31+
) {}
32+
33+
ngOnInit(): void {
34+
this.loading = true;
35+
this.voId = this.entityStorage.getEntity().id;
36+
this.vosService.getEnrichedVoById(this.voId).subscribe(
37+
(enrichedVo) => {
38+
this.vosService.getAllVos().subscribe(
39+
(vos) => {
40+
const memberVoIds: number[] = enrichedVo.memberVos.map((vo) => vo.id);
41+
this.vos = vos.filter((vo) => !memberVoIds.includes(vo.id));
42+
this.loading = false;
43+
},
44+
() => (this.loading = false)
45+
);
46+
},
47+
() => (this.loading = false)
48+
);
49+
}
50+
51+
close(): void {
52+
this.dialogRef.close(false);
53+
}
54+
55+
stepperNext(): void {
56+
this.stepper.next();
57+
}
58+
59+
stepperPrevious(): void {
60+
this.stepper.previous();
61+
}
62+
63+
addMemberOrganization(): void {
64+
this.loading = true;
65+
this.vosService.addMemberVo(this.voId, this.voSelection.selected[0].id).subscribe(
66+
() => {
67+
this.notificator.showSuccess(
68+
this.translator.instant(
69+
'VO_DETAIL.SETTINGS.MEMBER_ORGANIZATIONS.ADD_MEMBER_ORGANIZATION.SUCCESS'
70+
)
71+
);
72+
this.dialogRef.close(true);
73+
},
74+
() => (this.loading = false)
75+
);
76+
}
77+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<h1 class="page-subtitle">{{'VO_DETAIL.SETTINGS.MEMBER_ORGANIZATIONS.TITLE' | translate}}</h1>
2+
<perun-web-apps-refresh-button (click)="refresh()"></perun-web-apps-refresh-button>
3+
<button
4+
*ngIf="auth"
5+
(click)="addMemberOrganization()"
6+
mat-flat-button
7+
class="mr-2 action-button"
8+
color="accent">
9+
{{'VO_DETAIL.SETTINGS.MEMBER_ORGANIZATIONS.ADD' | translate}}
10+
</button>
11+
<button
12+
*ngIf="auth"
13+
(click)="removeMemberOrganization()"
14+
[disabled]="voSelection.selected.length === 0"
15+
mat-flat-button
16+
color="warn"
17+
class="mr-2">
18+
{{'VO_DETAIL.SETTINGS.MEMBER_ORGANIZATIONS.REMOVE' | translate}}
19+
</button>
20+
<perun-web-apps-immediate-filter
21+
(filter)="filterValue = $event"
22+
[placeholder]="'VO_DETAIL.SETTINGS.MEMBER_ORGANIZATIONS.FILTER'"></perun-web-apps-immediate-filter>
23+
<mat-spinner *ngIf="loading" class="ml-auto mr-auto"></mat-spinner>
24+
<perun-web-apps-vos-list
25+
*ngIf="!loading"
26+
[vos]="memberVos"
27+
[selection]="voSelection"
28+
[filterValue]="filterValue"
29+
[disableRouting]="!auth"
30+
[displayedColumns]="displayedColumns">
31+
</perun-web-apps-vos-list>

apps/admin-gui/src/app/vos/pages/vo-detail-page/vo-settings/vo-settings-member-organizations/vo-settings-member-organizations.component.scss

Whitespace-only changes.

0 commit comments

Comments
 (0)