Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/IOT-1583: Move application #167

Merged
merged 8 commits into from
Sep 13, 2024
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<div class="application-change-organization-dialog">
<h1 mat-dialog-title>{{ "APPLICATION.CHANGE-ORGANIZATION.TITLE" | translate }}</h1>
<div mat-dialog-content>
<label class="form-label" for="userGroup">{{
"APPLICATION.CHANGE-ORGANIZATION.CHOOSE-ORGANIZATION" | translate
}}</label>
<mat-select
id="userGroup"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Du har det samme id flere gange

class="form-control"
panelClass="overflow-x-hidden"
[(value)]="application.organizationId"
[compareWith]="compare"
(selectionChange)="onOrganizationChange()"
>
<mat-option *ngFor="let organization of filteredOrganizations | async" [value]="organization.id">
{{ organization.name }}
</mat-option>
</mat-select>
<label class="form-label" for="userGroup">{{
"APPLICATION.CHANGE-ORGANIZATION.CHOOSE-USER-GROUPS" | translate
}}</label>
<mat-select
id="userGroup"
class="form-control"
[multiple]="true"
panelClass="overflow-x-hidden"
[(value)]="application.permissionIds"
[compareWith]="compare"
>
<mat-option *ngFor="let permission of filteredPermissionsMulti | async" [value]="permission.id">
{{ permission.name }}
</mat-option>
</mat-select>
<mat-hint>{{ "APPLICATION.CHANGE-ORGANIZATION.USER-GROUP-AUTO-SELECT" | translate }}</mat-hint>
</div>
<div mat-dialog-actions class="d-flex flex-row">
<button (click)="onSubmit()" class="btn btn-primary">
{{ "GEN.SAVE" | translate }}
</button>
<button mat-dialog-close [mat-dialog-close]="false" class="btn btn-secondary ml-2">
{{ "GEN.CANCEL" | translate }}
</button>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.application-change-organization-dialog {
width: 50vw;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { Component, Inject, OnInit } from "@angular/core";
import { UntypedFormControl } from "@angular/forms";
import { MAT_DIALOG_DATA, MatDialogRef } from "@angular/material/dialog";
import { MatSnackBar } from "@angular/material/snack-bar";
import { Organisation } from "@app/admin/organisation/organisation.model";
import { OrganisationService } from "@app/admin/organisation/organisation.service";
import { PermissionResponse } from "@app/admin/permission/permission.model";
import { PermissionService } from "@app/admin/permission/permission.service";
import { Application, UpdateApplicationOrganization } from "@applications/application.model";
import { ApplicationService } from "@applications/application.service";
import { TranslateService } from "@ngx-translate/core";
import { ApplicationDialogModel } from "@shared/models/dialog.model";
import { SharedVariableService } from "@shared/shared-variable/shared-variable.service";
import { ReplaySubject, Subscription } from "rxjs";

@Component({
selector: "app-change-organization-dialog",
templateUrl: "./application-change-organization-dialog.component.html",
styleUrls: ["./application-change-organization-dialog.component.scss"],
})
export class ApplicationChangeOrganizationDialogComponent implements OnInit {
public applicationsSubscription: Subscription;
public permissionsSubscription: Subscription;
public organizationsSubscription: Subscription;
public application: UpdateApplicationOrganization;
public permissions: PermissionResponse[];
public organizations: Organisation[];
public filteredPermissionsMulti: ReplaySubject<PermissionResponse[]> = new ReplaySubject<PermissionResponse[]>(1);
public filteredOrganizations: ReplaySubject<Organisation[]> = new ReplaySubject<Organisation[]>(1);

constructor(
private applicationService: ApplicationService,
public translate: TranslateService,
private permissionService: PermissionService,
private organizationService: OrganisationService,
private sharedVariableService: SharedVariableService,
private snackBar: MatSnackBar,
private dialog: MatDialogRef<ApplicationChangeOrganizationDialogComponent>,
@Inject(MAT_DIALOG_DATA) public dialogModel: ApplicationDialogModel
) {
this.application = {
organizationId: this.dialogModel.organizationId ?? this.sharedVariableService.getSelectedOrganisationId(),
permissionIds: [],
};
}

ngOnInit(): void {
this.translate.use("da");
if (this.dialogModel.id) {
this.getApplication(this.dialogModel.id);
}
this.getOrganizations();
this.getPermissions();
}

getApplication(id: number): void {
this.applicationsSubscription = this.applicationService.getApplication(id).subscribe((application: Application) => {
this.application.permissionIds = application.permissionIds;
});
}

getOrganizations() {
this.organizationsSubscription = this.organizationService.getMinimal().subscribe(res => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Get minimal henter altid alle organisationer, man må kun flytte applikationer mellem organisationer man er applikations administrator på.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ja det kan jeg godt se, retter det til findAll, der er der allowed orgs/alle ved global admin

this.organizations = res.data;
this.filteredOrganizations.next(this.organizations.slice());
});
}

getPermissions() {
this.permissionsSubscription = this.permissionService.getPermissions(1000, 0).subscribe(res => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dit get permissions her henter kun for de organisationer hvor brugeren har userAdmin rettighed, hvilket vil sige at man ikke altid kan se alle da permissions ikke er herakiske. (Den bør nok laves om til at lave et fetch for den givne organisations permissions hvis man er applikations admin). Alternativt skal vi lige diskutere den rettighed her med Mogens

this.permissions = res.data.sort((a, b) => a.name.localeCompare(b.name, "da-DK", { numeric: true }));
this.filteredPermissionsMulti.next(
this.permissions.filter(p => p?.organization?.id === this?.application?.organizationId)
);
});
}

public compare(o1: any, o2: any): boolean {
return o1 === o2;
}

onOrganizationChange() {
this.filteredPermissionsMulti.next(
this.permissions.filter(p => p?.organization?.id === this?.application?.organizationId)
);
this.filteredPermissionsMulti.subscribe(res => {
this.application.permissionIds = res
.filter(permission => permission.automaticallyAddNewApplications)
.map(permission => permission.id);
});
}

onSubmit() {
this.applicationsSubscription = this.applicationService
.updateApplicationOrganization(this.application, this.dialogModel.id)
.subscribe(savedApplication => {
this.snackBar.open(
this.translate.instant("APPLICATION.CHANGE-ORGANIZATION.SNACKBAR-SAVED", {
applicationName: savedApplication.name,
organizationName: savedApplication.belongsTo.name,
}),
"",
{
duration: 10000,
}
);
this.dialog.close(true);
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
[dropDownButton]="dropdownButton"
(deleteSelectedInDropdown)="onDeleteApplication()"
[canEdit]="canEdit"
(extraDropdownOptions)="onExtraDropdownOptionClicked($event)"
>
</app-top-bar>
<div class="container-fluid">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ import { map } from "rxjs/operators";
import { SharedVariableService } from "@shared/shared-variable/shared-variable.service";
import { ChirpstackGatewayService } from "@shared/services/chirpstack-gateway.service";
import { Gateway, GatewayResponseMany } from "@app/gateway/gateway.model";
import { MatDialog } from "@angular/material/dialog";
import { ApplicationChangeOrganizationDialogComponent } from "../application-change-organization-dialog/application-change-organization-dialog.component";
import { ApplicationDialogModel } from "@shared/models/dialog.model";

@Component({
selector: "app-application",
Expand Down Expand Up @@ -57,6 +60,7 @@ export class ApplicationDetailComponent implements OnInit, OnDestroy, AfterViewI
public redMarker = "/assets/images/red-marker.png";
public greenMarker = "/assets/images/green-marker.png";
public greyMarker = "/assets/images/grey-marker.png";
private dropdownButtonExtraOptionsHandlers: Map<number, () => void> = new Map();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternativet til at have et map med onClick events, er at udvide Dropdown extra options til at kunne holde et onclick event, og så at det bliver kaldt onclick, frem for det event der ellers sendes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Har du da en situation her i hvor du har brug for mere end én handler?

Hvis ja. så synes jeg du skal udvide ExtraDropdownOption til at have en onclick handler som kan blive defineret samme sted som du definerer label

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Jeg har ikke lige pt en situation med brug for mere end én handler, men det kunne der sagtens være. Jeg anvender den på samme måde i Feature/IOT-1209-MoveGateway, så det ville også gøre det nemmere der.


constructor(
private applicationService: ApplicationService,
Expand All @@ -68,7 +72,8 @@ export class ApplicationDetailComponent implements OnInit, OnDestroy, AfterViewI
private deleteDialogService: DeleteDialogService,
private restService: RestService,
private sharedVariableService: SharedVariableService,
private chirpstackGatewayService: ChirpstackGatewayService
private chirpstackGatewayService: ChirpstackGatewayService,
private changeOrganizationDialog: MatDialog
) {}

ngOnInit(): void {
Expand All @@ -79,7 +84,18 @@ export class ApplicationDetailComponent implements OnInit, OnDestroy, AfterViewI
label: "",
editRouterLink: "../edit-application/" + this.id,
isErasable: true,
extraOptions: [],
};

this.translate.get("APPLICATION.CHANGE-ORGANIZATION.TITLE").subscribe(translation => {
const changeOrganizationButton = {
id: this.id,
label: translation,
onClick: () => this.onOpenChangeOrganizationDialog(),
};
this.dropdownButton.extraOptions.push(changeOrganizationButton);
this.dropdownButtonExtraOptionsHandlers.set(changeOrganizationButton.id, changeOrganizationButton.onClick);
});
}

this.translate
Expand Down Expand Up @@ -193,6 +209,21 @@ export class ApplicationDetailComponent implements OnInit, OnDestroy, AfterViewI
});
}

onOpenChangeOrganizationDialog() {
this.changeOrganizationDialog.open(ApplicationChangeOrganizationDialogComponent, {
data: {
id: this.id,
organizationId: this.application.belongsTo.id,
} as ApplicationDialogModel,
});
}

onExtraDropdownOptionClicked(id: string) {
const handler = this.dropdownButtonExtraOptionsHandlers.get(Number(id));

handler && handler();
}

bindApplication(id: number): void {
this.applicationsSubscription = this.applicationService.getApplication(id).subscribe(application => {
this.application = application;
Expand Down
5 changes: 5 additions & 0 deletions src/app/applications/application.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,8 @@ export interface ApplicationData {
ok?: boolean;
count?: number;
}

export class UpdateApplicationOrganization {
public organizationId: number;
public permissionIds: number[];
}
8 changes: 7 additions & 1 deletion src/app/applications/application.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Injectable } from "@angular/core";
import { Application, ApplicationData } from "@applications/application.model";
import { Application, ApplicationData, UpdateApplicationOrganization } from "@applications/application.model";
import { RestService } from "../shared/services/rest.service";
import { Observable } from "rxjs";
import { map } from "rxjs/operators";
Expand Down Expand Up @@ -76,4 +76,10 @@ export class ApplicationService {
deleteApplication(id: number) {
return this.restService.delete("application", id);
}

updateApplicationOrganization(body: UpdateApplicationOrganization, id: number): Observable<Application> {
return this.restService.put("application/updateApplicationOrganization", body, id, {
observe: "response",
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,11 @@
"APPLICATION-TABLE-ROW.EDIT" | translate
}}</a>
</li>
<li class="dropdown-item">
<a (click)="onOpenChangeOrganizationDialog(element.id)" routerLinkActive="active">{{
"APPLICATION.CHANGE-ORGANIZATION.TITLE" | translate
}}</a>
</li>
<li class="dropdown-item">
<a (click)="deleteApplication(element.id)" [routerLink]="[]">{{
"APPLICATION-TABLE-ROW.DELETE" | translate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ import { ApplicationDeviceType } from "@applications/models/application-device-t
import { Datatarget } from "@applications/datatarget/datatarget.model";
import { faFlag } from "@fortawesome/free-solid-svg-icons";
import { TableColumn } from "@shared/types/table.type";
import { MatDialog } from "@angular/material/dialog";
import { ApplicationDialogModel } from "@shared/models/dialog.model";
import { ApplicationChangeOrganizationDialogComponent } from "@applications/application-change-organization-dialog/application-change-organization-dialog.component";

const columnDefinitions: TableColumn[] = [
{
Expand Down Expand Up @@ -144,7 +147,8 @@ export class ApplicationsTableComponent implements AfterViewInit, OnInit {
private applicationService: ApplicationService,
private router: Router,
private deleteDialogService: DeleteDialogService,
private cdRef: ChangeDetectorRef
private cdRef: ChangeDetectorRef,
private changeOrganizationDialog: MatDialog
) {}

ngOnInit() {
Expand Down Expand Up @@ -268,5 +272,19 @@ export class ApplicationsTableComponent implements AfterViewInit, OnInit {
return !!result;
}

onOpenChangeOrganizationDialog(id: number) {
const dialog = this.changeOrganizationDialog.open(ApplicationChangeOrganizationDialogComponent, {
data: {
id: id,
} as ApplicationDialogModel,
});

dialog.afterClosed().subscribe(res => {
if (!res) return;

location.reload();
fcv-iteratorIt marked this conversation as resolved.
Show resolved Hide resolved
});
}

protected readonly columnDefinitions = columnDefinitions;
}
2 changes: 2 additions & 0 deletions src/app/applications/applications.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { PipesModule } from "@shared/pipes/pipes.module";
import { ApplicationsTableComponent } from "./applications-list/applications-table/applications-table.component";
import { MulticastModule } from "./multicast/multicast.module";
import { ReactiveFormsModule } from "@angular/forms";
import { ApplicationChangeOrganizationDialogComponent } from "./application-change-organization-dialog/application-change-organization-dialog.component";

@NgModule({
declarations: [
Expand All @@ -28,6 +29,7 @@ import { ReactiveFormsModule } from "@angular/forms";
ApplicationsListComponent,
ApplicationsTableComponent,
BulkImportComponent,
ApplicationChangeOrganizationDialogComponent,
],
exports: [ApplicaitonsRoutingModule, ApplicationsComponent, ApplicationsTableComponent],
imports: [
Expand Down
5 changes: 5 additions & 0 deletions src/app/shared/models/dialog.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,8 @@ export class DialogModel {
export class WelcomeDialogModel {
hasSomePermission: boolean;
}

export class ApplicationDialogModel {
id: number;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename til applicationId

organizationId?: number;
}
Loading