Skip to content

Commit 88e8aff

Browse files
It is now possible to add permission to application on creation (#138)
1 parent 874c661 commit 88e8aff

File tree

6 files changed

+118
-22
lines changed

6 files changed

+118
-22
lines changed

src/app/admin/permission/permission-edit/permission-edit.component.html

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,15 @@
6767
<mat-form-field>
6868
<label class="form-label" for="name">{{'PERMISSION.EDIT.APPS' | translate}}</label>
6969
<div class="col-12">
70-
<mat-select [formControl]="applicationMultiCtrl" [multiple]="true"
71-
panelClass="overflow-x-hidden" [(value)]="permission.applicationIds" id="applicationIds"
72-
name="applicationIds" #multiSelect [compareWith]="compare">
70+
<mat-select
71+
id="applicationIds"
72+
[formControl]="applicationMultiCtrl"
73+
[multiple]="true"
74+
panelClass="overflow-x-hidden"
75+
[(value)]="permission.applicationIds"
76+
name="applicationIds"
77+
#multiSelect
78+
[compareWith]="compare">
7379
<app-mat-select-search [formControl]="applicationMultiFilterCtrl"></app-mat-select-search>
7480
<mat-option *ngFor="let app of filteredApplicationsMulti | async" [value]="app.id">
7581
{{app.name}}

src/app/applications/application.model.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export class Application {
3030
public hardware?: string;
3131
public controlledProperties?: ControlledProperty[];
3232
public deviceTypes?: ApplicationDeviceType[];
33+
public permissionIds: number[];
3334
}
3435

3536
export class ApplicationRequest {
@@ -48,6 +49,7 @@ export class ApplicationRequest {
4849
public hardware?: string;
4950
public controlledProperties?: ControlledPropertyTypes[];
5051
public deviceTypes?: ApplicationDeviceTypeUnion[];
52+
public permissionIds: number[];
5153
}
5254

5355
export interface ApplicationData {

src/app/shared/components/forms/form-body-application/form-body-application.component.html

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,27 @@
2424
[ngClass]="{'is-invalid' : formFailedSubmit && errorFields.includes('description'), 'is-valid' : formFailedSubmit && !errorFields.includes('description')}"></textarea>
2525
</div>
2626

27+
<div class="form-group mt-3 col-12">
28+
<label class="form-label" for="userGroup">{{'QUESTION.APPLICATION.ADD-PERMISSION' | translate}}</label>
29+
<mat-select
30+
id="userGroup"
31+
class="form-control"
32+
[formControl]="permissionMultiCtrl"
33+
[multiple]="true"
34+
panelClass="overflow-x-hidden"
35+
[(value)]="application.permissionIds"
36+
[compareWith]="compare">
37+
<app-mat-select-search [formControl]="permissionMultiFilterCtrl"></app-mat-select-search>
38+
<mat-option *ngFor="let permission of filteredPermissionsMulti | async" [value]="permission.id">
39+
{{permission.name}}
40+
</mat-option>
41+
</mat-select>
42+
<mat-hint>{{'QUESTION.APPLICATION.PERMISSION-HINT' | translate}}</mat-hint>
43+
</div>
44+
2745
<div class="form-group mt-3">
28-
<label class="form-label" for="application.status">{{'APPLICATION.METADATA-FIELD.STATUS' | translate}}</label>
29-
<mat-select class="form-control" name="application.statusOptions" [(ngModel)]="application.status"
46+
<label class="form-label" for="application.statusOptions">{{'APPLICATION.METADATA-FIELD.STATUS' | translate}}</label>
47+
<mat-select class="form-control" id="application.statusOptions" name="application.statusOptions" [(ngModel)]="application.status"
3048
[ngClass]="{'is-invalid' : formFailedSubmit && errorFields.includes('application.status'), 'is-valid' : formFailedSubmit && !errorFields.includes('application.status')}">
3149
<mat-option *ngFor="let statusOption of statuses" [value]="statusOption.value">
3250
{{statusOption.label}}
@@ -37,7 +55,7 @@
3755
<div class="form-group mt-3">
3856
<label class="form-label" for="application.startDate">{{'APPLICATION.METADATA-FIELD.START-DATE' |
3957
translate}}</label>
40-
<mat-form-field appearance="fill">
58+
<mat-form-field appearance="fill" id="application.startDate">
4159
<mat-label>{{'QUESTION.APPLICATION.START-DATE-PLACEHOLDER' | translate}}</mat-label>
4260
<input matInput [max]="serializedEndDate.value" [matDatepicker]="startDatePicker"
4361
[formControl]="serializedStartDate"
@@ -50,7 +68,7 @@
5068

5169
<div class="form-group mt-3">
5270
<label class="form-label" for="application.endDate">{{'APPLICATION.METADATA-FIELD.END-DATE' | translate}}</label>
53-
<mat-form-field appearance="fill">
71+
<mat-form-field appearance="fill" id="application.endDate">
5472
<mat-label>{{'QUESTION.APPLICATION.END-DATE-PLACEHOLDER' | translate}}</mat-label>
5573
<input matInput [min]="serializedStartDate.value" [matDatepicker]="endDatePicker"
5674
[formControl]="serializedEndDate"
@@ -62,23 +80,23 @@
6280
</div>
6381

6482
<div class="form-group mt-3">
65-
<label class="form-label" for="application.category">{{'APPLICATION.METADATA-FIELD.CATEGORY' | translate}}</label>
83+
<label class="form-label" for="category">{{'APPLICATION.METADATA-FIELD.CATEGORY' | translate}}</label>
6684
<input type="text" class="form-control" id="category" name="category"
6785
[placeholder]="'QUESTION.APPLICATION.CATEGORY-PLACEHOLDER' | translate" maxlength="100"
6886
[(ngModel)]="application.category"
6987
[ngClass]="{'is-invalid' : formFailedSubmit && errorFields.includes('application.category'), 'is-valid' : formFailedSubmit && !errorFields.includes('application.category')}">
7088
</div>
7189

7290
<div class="form-group mt-3">
73-
<label class="form-label" for="application.owner">{{'APPLICATION.METADATA-FIELD.OWNER' | translate}}</label>
91+
<label class="form-label" for="owner">{{'APPLICATION.METADATA-FIELD.OWNER' | translate}}</label>
7492
<input type="text" class="form-control" id="owner" name="owner"
7593
[placeholder]="'QUESTION.APPLICATION.OWNER-PLACEHOLDER' | translate" maxlength="100"
7694
[(ngModel)]="application.owner"
7795
[ngClass]="{'is-invalid' : formFailedSubmit && errorFields.includes('application.owner'), 'is-valid' : formFailedSubmit && !errorFields.includes('application.owner')}">
7896
</div>
7997

8098
<div class="form-group mt-3">
81-
<label class="form-label" for="application.contactPerson">{{'APPLICATION.METADATA-FIELD.CONTACT-PERSON' |
99+
<label class="form-label" for="contactPerson">{{'APPLICATION.METADATA-FIELD.CONTACT-PERSON' |
82100
translate}}</label>
83101
<input type="text" class="form-control" id="contactPerson" name="contactPerson"
84102
[placeholder]="'QUESTION.APPLICATION.CONTACT-PERSON-PLACEHOLDER' | translate" maxlength="100"
@@ -87,7 +105,7 @@
87105
</div>
88106

89107
<div class="form-group mt-3">
90-
<label class="form-label" for="application.contactEmail">{{'APPLICATION.METADATA-FIELD.CONTACT-EMAIL' |
108+
<label class="form-label" for="contactEmail">{{'APPLICATION.METADATA-FIELD.CONTACT-EMAIL' |
91109
translate}}</label>
92110
<input type="email" class="form-control" id="contactEmail" name="contactEmail"
93111
[placeholder]="'QUESTION.APPLICATION.CONTACT-EMAIL-PLACEHOLDER' | translate" maxlength="50" [email]="true"
@@ -96,7 +114,7 @@
96114
</div>
97115

98116
<div class="form-group mt-3">
99-
<label class="form-label" for="application.contactPhone">{{'APPLICATION.METADATA-FIELD.CONTACT-PHONE' |
117+
<label class="form-label" for="contactPhone">{{'APPLICATION.METADATA-FIELD.CONTACT-PHONE' |
100118
translate}}</label>
101119
<input type="text" class="form-control" id="contactPhone" name="contactPhone" [formControl]="phoneCtrl"
102120
[placeholder]="'QUESTION.APPLICATION.CONTACT-PHONE-PLACEHOLDER' | translate" maxlength="12"
@@ -126,7 +144,7 @@
126144
<label class="form-label" for="application.controlledProperty">{{'APPLICATION.METADATA-FIELD.CONTROLLED-PROPERTY'
127145
|
128146
translate}}</label>
129-
<mat-select class="form-control" name="application.controlledProperty" multiple="true"
147+
<mat-select class="form-control" name="application.controlledProperty" id="application.controlledProperty" multiple="true"
130148
[(ngModel)]="application.controlledProperties"
131149
[placeholder]="'QUESTION.APPLICATION.CONTROLLED-PROPERTY-PLACEHOLDER' | translate"
132150
[ngClass]="{'is-invalid' : formFailedSubmit && errorFields.includes('application.controlledProperty'), 'is-valid' : formFailedSubmit && !errorFields.includes('application.controlledProperty')}">
@@ -139,7 +157,7 @@
139157
<div class="form-group mt-3">
140158
<label class="form-label" for="application.deviceType">{{'APPLICATION.METADATA-FIELD.DEVICE-TYPE' |
141159
translate}}</label>
142-
<mat-select class="form-control" name="application.deviceType" multiple="true"
160+
<mat-select class="form-control" name="application.deviceType" id="application.deviceType" multiple="true"
143161
[(ngModel)]="application.deviceTypes" [placeholder]="'QUESTION.APPLICATION.DEVICE-TYPE-PLACEHOLDER' | translate"
144162
[ngClass]="{'is-invalid' : formFailedSubmit && errorFields.includes('application.deviceType'), 'is-valid' : formFailedSubmit && !errorFields.includes('application.deviceType')}">
145163
<mat-option *ngFor="let deviceType of deviceTypes" [value]="deviceType.value">

src/app/shared/components/forms/form-body-application/form-body-application.component.ts

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Component, OnInit, Input, OnDestroy } from '@angular/core';
22
import { ActivatedRoute, Router } from '@angular/router';
3-
import { Subscription } from 'rxjs';
3+
import { ReplaySubject, Subject, Subscription } from 'rxjs';
44
import { TranslateService } from '@ngx-translate/core';
55
import { RestService } from '@shared/services/rest.service';
66
import { HttpErrorResponse } from '@angular/common/http';
@@ -21,6 +21,10 @@ import {
2121
ApplicationDeviceTypeEntries,
2222
} from '@shared/enums/device-type';
2323
import { isPhoneNumberValid } from '@shared/validators/phone-number.validator';
24+
import { PermissionResponse } from '@app/admin/permission/permission.model';
25+
import { takeUntil } from 'rxjs/operators';
26+
import { PermissionService } from '@app/admin/permission/permission.service';
27+
import { MeService } from '@shared/services/me.service';
2428

2529
export class User {
2630
public name: string;
@@ -58,13 +62,25 @@ export class FormBodyApplicationComponent implements OnInit, OnDestroy {
5862
controlledProperties = Object.values(ControlledPropertyTypes);
5963
deviceTypes: DropdownOption[] = [];
6064

65+
permissionsSubscription: Subscription;
66+
public permissions: PermissionResponse[];
67+
public permissionMultiCtrl: UntypedFormControl = new UntypedFormControl();
68+
public permissionMultiFilterCtrl: UntypedFormControl = new UntypedFormControl();
69+
public filteredPermissionsMulti: ReplaySubject<
70+
PermissionResponse[]
71+
> = new ReplaySubject<PermissionResponse[]>(1);
72+
73+
private _onDestroy = new Subject<void>();
74+
6175
constructor(
6276
private restService: RestService,
6377
private applicationService: ApplicationService,
6478
private route: ActivatedRoute,
6579
public translate: TranslateService,
6680
private router: Router,
67-
private sharedVariableService: SharedVariableService
81+
private permissionService: PermissionService,
82+
private sharedVariableService: SharedVariableService,
83+
private meService: MeService
6884
) {
6985
this.fillDefaultMetadata();
7086
this.phoneCtrl = new UntypedFormControl(this.application.contactPhone, [
@@ -78,6 +94,7 @@ export class FormBodyApplicationComponent implements OnInit, OnDestroy {
7894
if (this.id) {
7995
this.getApplication(this.id);
8096
}
97+
this.getPermissions(this.sharedVariableService.getUserInfo().user.id);
8198

8299
const statusTranslationPrefix = 'APPLICATION.STATUS.';
83100
const statusTranslationKeys = ApplicationStatusEntries.map(
@@ -111,6 +128,28 @@ export class FormBodyApplicationComponent implements OnInit, OnDestroy {
111128
}));
112129
this.deviceTypes.push(...deviceTypeOptions);
113130
});
131+
132+
this.permissionMultiFilterCtrl.valueChanges
133+
.pipe(takeUntil(this._onDestroy))
134+
.subscribe(() => {
135+
this.filterPermissionsMulti();
136+
});
137+
}
138+
139+
filterPermissionsMulti() {
140+
if (!this.permissions) {
141+
return;
142+
}
143+
let search = this.permissionMultiFilterCtrl.value;
144+
if (!search) {
145+
this.filteredPermissionsMulti.next(this.permissions.slice());
146+
return;
147+
}
148+
149+
search = search.toLowerCase();
150+
this.filteredPermissionsMulti.next(
151+
this.permissions.filter((p) => p.name.toLowerCase().indexOf(search) > -1)
152+
);
114153
}
115154

116155
fillDefaultMetadata() {
@@ -152,6 +191,8 @@ export class FormBodyApplicationComponent implements OnInit, OnDestroy {
152191
this.application.deviceTypes = application.deviceTypes.map(
153192
(deviceType) => deviceType.type
154193
);
194+
this.application.permissionIds = application.permissionIds;
195+
this.permissionMultiCtrl.setValue(this.application.permissionIds);
155196

156197
this.fillDefaultMetadata();
157198
});
@@ -256,10 +297,37 @@ export class FormBodyApplicationComponent implements OnInit, OnDestroy {
256297
this.router.navigateByUrl('/applications');
257298
}
258299

300+
public compare(o1: any, o2: any): boolean {
301+
return o1 === o2;
302+
}
303+
304+
getPermissions(userId: number) {
305+
this.permissionsSubscription = this.permissionService
306+
.getPermissions(
307+
1000,
308+
0,
309+
undefined,
310+
undefined,
311+
this.meService.hasGlobalAdmin() ? undefined : userId
312+
)
313+
.subscribe((res) => {
314+
this.permissions = res.data.sort((a, b) =>
315+
a.name.localeCompare(b.name, 'da-DK', { numeric: true })
316+
);
317+
this.filteredPermissionsMulti.next(this.permissions.slice());
318+
if (!this.id) {
319+
this.application.permissionIds = [this.permissions[0].id];
320+
this.permissionMultiCtrl.setValue(this.application.permissionIds);
321+
}
322+
});
323+
}
324+
259325
ngOnDestroy() {
260326
// prevent memory leak by unsubscribing
261327
if (this.applicationsSubscription) {
262328
this.applicationsSubscription.unsubscribe();
263329
}
330+
this._onDestroy.next();
331+
this._onDestroy.complete();
264332
}
265333
}

src/app/shared/components/forms/form.module.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { FormBodyApplicationComponent } from './form-body-application/form-body-
99
import { FormHeaderComponent } from './form-header/form-header.component';
1010
import { FormKeyValuePairComponent } from './form-key-value/form-key-value-pair/form-key-value-pair.component';
1111
import { FormKeyValueListComponent } from './form-key-value/form-key-value-list/form-key-value-list.component';
12-
12+
import { MatSelectSearchModule } from '@shared/components/mat-select-search/mat-select-search.module';
1313

1414
@NgModule({
1515
declarations: [
@@ -25,15 +25,15 @@ import { FormKeyValueListComponent } from './form-key-value/form-key-value-list/
2525
FormsModule,
2626
ReactiveFormsModule,
2727
TranslateModule,
28-
NGMaterialModule
28+
NGMaterialModule,
29+
MatSelectSearchModule,
2930
],
3031
exports: [
3132
FormHeaderComponent,
3233
FormBodyApplicationComponent,
3334
FormKeyValuePairComponent,
3435
FormKeyValueListComponent,
3536
],
36-
providers: [
37-
]
37+
providers: [],
3838
})
39-
export class FormModule { }
39+
export class FormModule {}

src/assets/i18n/da.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -742,7 +742,9 @@
742742
"PERSONAL-DATA-PLACEHOLDER": "Markér, hvis data i applikationen indeholder personoplysninger. Berører ikke funktionalitet i OS2iot",
743743
"HARDWARE-PLACEHOLDER": "Angiv hvad der bruges af sensorer/hardware, samt model",
744744
"CONTROLLED-PROPERTY-PLACEHOLDER": "Angiv hvilke datatyper der opsamles",
745-
"DEVICE-TYPE-PLACEHOLDER": "Angiv hvilke forbindelsesteknologier, der benyttes"
745+
"DEVICE-TYPE-PLACEHOLDER": "Angiv hvilke forbindelsesteknologier, der benyttes",
746+
"PERMISSION-HINT": "Husk at alle med adgang til brugergruppen vil kunne se applikationen",
747+
"ADD-PERMISSION": "Tilføj til brugergruppe"
746748
},
747749
"MQTT": {
748750
"TITLE": "MQTT specifik opsætning",

0 commit comments

Comments
 (0)