192 lines
6.0 KiB
TypeScript
192 lines
6.0 KiB
TypeScript
|
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||
|
import { ColumnMode, SelectionType } from '@swimlane/ngx-datatable';
|
||
|
import { CatalogService } from '../helpers/catalog.service';
|
||
|
import { AuthService } from '../../helpers/auth.service';
|
||
|
import { debounceTime, distinctUntilChanged, filter, first, map, switchMap, takeUntil } from 'rxjs/operators';
|
||
|
import { Subject } from 'rxjs';
|
||
|
import { ToastrService } from 'ngx-toastr';
|
||
|
import { CatalogImage } from '../models/image';
|
||
|
import { FormGroup, FormBuilder, Validators, AbstractControl, FormArray } from '@angular/forms';
|
||
|
import Fuse from 'fuse.js';
|
||
|
import { sortArray } from '../../helpers/utils.service';
|
||
|
import { BsModalService } from 'ngx-bootstrap/modal';
|
||
|
import { ConfirmationDialogComponent } from '../../components/confirmation-dialog/confirmation-dialog.component';
|
||
|
|
||
|
@Component({
|
||
|
selector: 'app-custom-images',
|
||
|
templateUrl: './custom-images.component.html',
|
||
|
styleUrls: ['./custom-images.component.scss']
|
||
|
})
|
||
|
export class CustomImagesComponent implements OnInit, OnDestroy
|
||
|
{
|
||
|
images: CatalogImage[] = [];
|
||
|
listItems: CatalogImage[] = [];
|
||
|
editorForm: FormGroup;
|
||
|
loadingIndicator = true;
|
||
|
|
||
|
private destroy$ = new Subject();
|
||
|
private readonly fuseJsOptions: {};
|
||
|
|
||
|
// ----------------------------------------------------------------------------------------------------------------
|
||
|
constructor(private readonly catalogService: CatalogService,
|
||
|
private readonly modalService: BsModalService,
|
||
|
private readonly authService: AuthService,
|
||
|
private readonly toastr: ToastrService,
|
||
|
private readonly fb: FormBuilder)
|
||
|
{
|
||
|
// Configure FuseJs
|
||
|
this.fuseJsOptions = {
|
||
|
includeScore: false,
|
||
|
minMatchCharLength: 2,
|
||
|
includeMatches: true,
|
||
|
shouldSort: false,
|
||
|
threshold: .3, // Lower value means a more exact search
|
||
|
keys: [
|
||
|
{ name: 'name', weight: .9 },
|
||
|
{ name: 'description', weight: .8 },
|
||
|
{ name: 'os', weight: .7 },
|
||
|
{ name: 'type', weight: .7 }
|
||
|
]
|
||
|
};
|
||
|
|
||
|
this.createForm();
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------------------------------------------
|
||
|
private createForm()
|
||
|
{
|
||
|
this.editorForm = this.fb.group(
|
||
|
{
|
||
|
searchTerm: [''],
|
||
|
sortProperty: ['name']
|
||
|
});
|
||
|
|
||
|
this.editorForm.get('searchTerm').valueChanges
|
||
|
.pipe(
|
||
|
debounceTime(300),
|
||
|
distinctUntilChanged(),
|
||
|
takeUntil(this.destroy$)
|
||
|
)
|
||
|
.subscribe(() => this.applyFiltersAndSort());
|
||
|
|
||
|
this.editorForm.get('sortProperty').valueChanges
|
||
|
.pipe(
|
||
|
distinctUntilChanged(),
|
||
|
takeUntil(this.destroy$)
|
||
|
)
|
||
|
.subscribe(() => this.applyFiltersAndSort());
|
||
|
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------------------------------------------
|
||
|
private applyFiltersAndSort()
|
||
|
{
|
||
|
let listItems: CatalogImage[] = null;
|
||
|
|
||
|
const searchTerm = this.editorForm.get('searchTerm').value;
|
||
|
if (searchTerm.length >= 2)
|
||
|
{
|
||
|
const fuse = new Fuse(this.images, this.fuseJsOptions);
|
||
|
const fuseResults = fuse.search(searchTerm);
|
||
|
listItems = fuseResults.map(x => x.item as CatalogImage);
|
||
|
}
|
||
|
|
||
|
if (!listItems)
|
||
|
listItems = [...this.images];
|
||
|
|
||
|
this.listItems = sortArray(listItems, this.editorForm.get('sortProperty').value);
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------------------------------------------
|
||
|
setSortProperty(propertyName: string)
|
||
|
{
|
||
|
this.editorForm.get('sortProperty').setValue(propertyName);
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------------------------------------------
|
||
|
clearSearch()
|
||
|
{
|
||
|
this.editorForm.get('searchTerm').setValue('');
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------------------------------------------
|
||
|
private getCustomImages()
|
||
|
{
|
||
|
this.loadingIndicator = true;
|
||
|
|
||
|
this.authService.userInfoUpdated$
|
||
|
.pipe(
|
||
|
takeUntil(this.destroy$),
|
||
|
filter(userInfo => userInfo != null),
|
||
|
switchMap(userInfo => this.catalogService.getCustomImages(userInfo.id))
|
||
|
)
|
||
|
.subscribe(images =>
|
||
|
{
|
||
|
this.images = images;
|
||
|
|
||
|
this.applyFiltersAndSort();
|
||
|
|
||
|
this.loadingIndicator = false
|
||
|
}, err =>
|
||
|
{
|
||
|
const errorDetails = err.error?.message ? `(${err.error.message})` : '';
|
||
|
this.toastr.error(`Failed to retrieve the list of custom images ${errorDetails}`);
|
||
|
|
||
|
this.loadingIndicator = false;
|
||
|
});
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------------------------------------------
|
||
|
deleteCustomImage(image: CatalogImage)
|
||
|
{
|
||
|
const modalConfig = {
|
||
|
ignoreBackdropClick: true,
|
||
|
keyboard: false,
|
||
|
animated: true,
|
||
|
initialState: {
|
||
|
prompt: `Are you sure you wish to permanently delete the "${image.name}" image?`,
|
||
|
confirmButtonText: 'Yes, delete this image',
|
||
|
declineButtonText: 'No, keep it',
|
||
|
confirmByDefault: false
|
||
|
}
|
||
|
};
|
||
|
|
||
|
const modalRef = this.modalService.show(ConfirmationDialogComponent, modalConfig);
|
||
|
|
||
|
modalRef.content.confirm.pipe(first()).subscribe(() =>
|
||
|
{
|
||
|
this.toastr.info(`Removing machine "${image.name}"...`);
|
||
|
|
||
|
this.catalogService.deleteImage(image.id)
|
||
|
.subscribe(() =>
|
||
|
{
|
||
|
const index = this.images.findIndex(i => i.id === image.id);
|
||
|
if (index >= 0)
|
||
|
this.images.splice(index, 1);
|
||
|
|
||
|
this.applyFiltersAndSort();
|
||
|
|
||
|
this.toastr.info(`The image "${image.name}" has been removed`);
|
||
|
},
|
||
|
err =>
|
||
|
{
|
||
|
this.toastr.error(`Failed to delete the "${image.name}" image ${err.error.message}`);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------------------------------------------
|
||
|
ngOnInit(): void
|
||
|
{
|
||
|
this.getCustomImages();
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------------------------------------------
|
||
|
ngOnDestroy()
|
||
|
{
|
||
|
this.destroy$.next();
|
||
|
}
|
||
|
|
||
|
}
|