sc-portal/app/src/app/volumes/volumes.component.ts

260 lines
7.8 KiB
TypeScript

import { Component, OnInit, OnDestroy } from '@angular/core';
import { Volume } from './models/volume';
import { VolumesService } from './helpers/volumes.service';
import { MachinesService } from '../machines/helpers/machines.service';
import { ConfirmationDialogComponent } from '../components/confirmation-dialog/confirmation-dialog.component';
import { BsModalService } from 'ngx-bootstrap/modal';
import { ToastrService } from 'ngx-toastr';
import { VolumeEditorComponent } from './volume-editor/volume-editor.component';
import { PromptDialogComponent } from '../components/prompt-dialog/prompt-dialog.component';
import { Subject } from 'rxjs';
import { NetworkingService } from '../networking/helpers/networking.service';
import { sortArray } from '../helpers/utils.service';
import Fuse from 'fuse.js';
import { FormGroup, FormBuilder, Validators, AbstractControl, FormArray } from '@angular/forms';
import { distinctUntilChanged, first, takeUntil, debounceTime, filter, switchMap } from 'rxjs/operators';
import { Title } from "@angular/platform-browser";
import { TranslateService } from '@ngx-translate/core';
@Component({
selector: 'app-volumes',
templateUrl: './volumes.component.html',
styleUrls: ['./volumes.component.scss']
})
export class VolumesComponent implements OnInit, OnDestroy
{
volumes: Volume[];
listItems: Volume[];
networks = {};
loadingIndicator = true;
editorForm: FormGroup;
machines = {};
private destroy$ = new Subject();
private readonly fuseJsOptions: {};
// ----------------------------------------------------------------------------------------------------------------
constructor(private readonly volumesService: VolumesService,
private readonly networkingService: NetworkingService,
private readonly machinesService: MachinesService,
private readonly modalService: BsModalService,
private readonly toastr: ToastrService,
private readonly fb: FormBuilder,
private readonly titleService: Title,
private readonly translationService: TranslateService)
{
translationService.get('volumes.title').pipe(first()).subscribe(x => titleService.setTitle(`Spearhead - ${x}`));
// 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: 'tags.key', weight: .7 }
]
};
this.createForm();
this.machinesService.get()
.subscribe(x =>
{
this.machines = x.reduce((a, b) =>
{
a[b.id] = b.name;
return a;
}, {});
});
this.networkingService.getNetworks().subscribe(x =>
{
this.networks = x.filter(n => n.fabric).reduce((a, b) =>
{
a[b.id] = b.name;
return a;
}, {});
});
this.getVolumes();
}
// ----------------------------------------------------------------------------------------------------------------
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 getVolumes()
{
this.volumesService.getVolumes()
.subscribe(volumes =>
{
this.volumes = volumes.map(x => x as Volume);
this.applyFiltersAndSort();
this.loadingIndicator = false;
});
}
// ----------------------------------------------------------------------------------------------------------------
private applyFiltersAndSort()
{
let listItems: Volume[] = null;
const searchTerm = this.editorForm.get('searchTerm').value;
if (searchTerm.length >= 2)
{
const fuse = new Fuse(this.volumes, this.fuseJsOptions);
const fuseResults = fuse.search(searchTerm);
listItems = fuseResults.map(x => x.item as Volume);
}
if (!listItems)
listItems = [...this.volumes];
this.listItems = sortArray(listItems, this.editorForm.get('sortProperty').value);
}
// ----------------------------------------------------------------------------------------------------------------
setSortProperty(propertyName: string)
{
this.editorForm.get('sortProperty').setValue(propertyName);
}
// ----------------------------------------------------------------------------------------------------------------
clearSearch()
{
this.editorForm.get('searchTerm').setValue('');
}
// ----------------------------------------------------------------------------------------------------------------
showEditor()
{
const modalConfig = {
ignoreBackdropClick: true,
keyboard: false,
animated: true,
initialState: {}
};
const modalRef = this.modalService.show(VolumeEditorComponent, modalConfig);
modalRef.setClass('modal-lg');
modalRef.content.save.pipe(first()).subscribe(x =>
{
this.volumes.push(x as Volume);
this.applyFiltersAndSort();
this.toastr.info(`The volume "${x.name}" has been created`);
});
}
// ----------------------------------------------------------------------------------------------------------------
renameVolume(volume: Volume)
{
const modalConfig = {
ignoreBackdropClick: true,
keyboard: false,
animated: true,
initialState: {
value: volume.name,
required: true,
title: 'Rename volume',
prompt: 'Type in the new name for your volume',
placeholder: 'New volume name',
saveButtonText: 'Change volume name'
}
};
const modalRef = this.modalService.show(PromptDialogComponent, modalConfig);
modalRef.content.save
.pipe(
switchMap(name => this.volumesService.renameVolume(volume.id, name))
)
.subscribe(x =>
{
volume.name = x.name;
this.toastr.info(`The volume "${volume.name}" has been renamed`);
});
}
// ----------------------------------------------------------------------------------------------------------------
deleteVolume(volume: Volume)
{
const modalConfig = {
ignoreBackdropClick: true,
keyboard: false,
animated: true,
initialState: {
prompt: `Are you sure you wish to permanently delete this volume?`,
confirmButtonText: 'Yes, delete it',
declineButtonText: 'No, keep it',
confirmByDefault: false
}
};
const modalRef = this.modalService.show(ConfirmationDialogComponent, modalConfig);
modalRef.content.confirm.pipe(
first(),
switchMap(() => this.volumesService.deleteVolume(volume))
)
.subscribe(() =>
{
const index = this.volumes.findIndex(x => x.id === volume.id);
if (index >= 0)
{
this.volumes.splice(index, 1);
this.applyFiltersAndSort();
}
this.toastr.info(`The volume has been deleted`);
}, err =>
{
const errorDetails = err.error?.message ? `(${err.error.message})` : '';
this.toastr.error(`Failed to remove the volume ${errorDetails}`);
});
}
// ----------------------------------------------------------------------------------------------------------------
ngOnInit(): void
{
}
// ----------------------------------------------------------------------------------------------------------------
ngOnDestroy()
{
this.destroy$.next();
}
}