display packages based on the "group"
This commit is contained in:
parent
87da8fc49d
commit
4a100cf9d4
@ -5,6 +5,8 @@ import { Cacheable } from 'ts-cacheable';
|
|||||||
import { delay, filter, map, mergeMap, repeatWhen, take, tap } from 'rxjs/operators';
|
import { delay, filter, map, mergeMap, repeatWhen, take, tap } from 'rxjs/operators';
|
||||||
import { CatalogPackage } from '../models/package';
|
import { CatalogPackage } from '../models/package';
|
||||||
import { CatalogImage } from '../models/image';
|
import { CatalogImage } from '../models/image';
|
||||||
|
import { PackageGroupsEnum } from '../models/package-groups';
|
||||||
|
import { FileSizePipe } from 'src/app/pipes/file-size.pipe';
|
||||||
|
|
||||||
const cacheBuster$ = new Subject<void>();
|
const cacheBuster$ = new Subject<void>();
|
||||||
const imagesCacheBuster$ = new Subject<void>();
|
const imagesCacheBuster$ = new Subject<void>();
|
||||||
@ -15,7 +17,8 @@ const imagesCacheBuster$ = new Subject<void>();
|
|||||||
export class CatalogService
|
export class CatalogService
|
||||||
{
|
{
|
||||||
// ----------------------------------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------------------------------
|
||||||
constructor(private readonly httpClient: HttpClient) { }
|
constructor(private readonly httpClient: HttpClient,
|
||||||
|
private readonly fileSizePipe: FileSizePipe) { }
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------------------------------
|
||||||
@Cacheable({
|
@Cacheable({
|
||||||
@ -43,9 +46,23 @@ export class CatalogService
|
|||||||
{
|
{
|
||||||
return this.httpClient.get(`./assets/data/packages.json`).pipe(map(prices =>
|
return this.httpClient.get(`./assets/data/packages.json`).pipe(map(prices =>
|
||||||
{
|
{
|
||||||
packages.forEach(x => x.price = prices[x.id])
|
let filteredPackages: CatalogPackage[] = [];
|
||||||
|
|
||||||
return packages;
|
for (let pkg of packages)
|
||||||
|
if (pkg.group === PackageGroupsEnum.Vm || pkg.group === PackageGroupsEnum.Infra)
|
||||||
|
{
|
||||||
|
pkg.price = prices[pkg.id];
|
||||||
|
|
||||||
|
let size = this.fileSizePipe.transform(pkg.memory * 1024 * 1024);
|
||||||
|
[pkg.memorySize, pkg.memorySizeLabel] = size.split(' ');
|
||||||
|
|
||||||
|
size = this.fileSizePipe.transform(pkg.disk * 1024 * 1024);
|
||||||
|
[pkg.diskSize, pkg.diskSizeLabel] = size.split(' ');
|
||||||
|
|
||||||
|
filteredPackages.push(pkg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return filteredPackages;
|
||||||
}))
|
}))
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
5
app/src/app/catalog/models/package-groups.ts
Normal file
5
app/src/app/catalog/models/package-groups.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export enum PackageGroupsEnum
|
||||||
|
{
|
||||||
|
Vm = 'Virtual machine',
|
||||||
|
Infra = 'Infrastructure container'
|
||||||
|
}
|
@ -5,16 +5,9 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ng-container *ngIf="!loadingIndicator">
|
<ng-container *ngIf="!loadingIndicator">
|
||||||
<div class="btn-group w-100" btnRadioGroup>
|
|
||||||
<label [btnRadio]="group" class="btn" [class.active]="group === selectedPackageGroup" *ngFor="let group of packageGroups"
|
|
||||||
(click)="setPackageGroup($event, group)">
|
|
||||||
{{ group }}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="list-group list-group-flush flex-grow-1" *ngIf="packages">
|
<div class="list-group list-group-flush flex-grow-1" *ngIf="packages">
|
||||||
<ng-container *ngFor="let pkg of packages[selectedPackageGroup]">
|
<ng-container *ngFor="let pkg of packages">
|
||||||
<a *ngIf="pkg.visible" class="list-group-item list-group-item-action d-flex align-items-center justify-content-between">
|
<a class="list-group-item list-group-item-action d-flex align-items-center justify-content-between" id="package-{{ pkg.id }}">
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<input class="form-check-input" type="radio" id="pkg-{{ pkg.id }}" name="pkg" [value]="pkg" [(ngModel)]="selectedPackage">
|
<input class="form-check-input" type="radio" id="pkg-{{ pkg.id }}" name="pkg" [value]="pkg" [(ngModel)]="selectedPackage">
|
||||||
<label class="form-check-label d-flex justify-content-between align-items-center pb-2" for="pkg-{{ pkg.id }}">
|
<label class="form-check-label d-flex justify-content-between align-items-center pb-2" for="pkg-{{ pkg.id }}">
|
||||||
@ -23,7 +16,6 @@
|
|||||||
<span class="h3 text-uppercase">
|
<span class="h3 text-uppercase">
|
||||||
{{ pkg.name }}
|
{{ pkg.name }}
|
||||||
<span class="price" *ngIf="pkg.price">{{ pkg.price | currency: 'USD': 'symbol': '1.0-2' }}/h</span>
|
<span class="price" *ngIf="pkg.price">{{ pkg.price | currency: 'USD': 'symbol': '1.0-2' }}/h</span>
|
||||||
<!--<small *ngIf="pkg.brand">{{ pkg.brand }}</small>-->
|
|
||||||
</span>
|
</span>
|
||||||
<small class="text-faded pb-1 d-block">
|
<small class="text-faded pb-1 d-block">
|
||||||
v<b>{{ pkg.version }}</b>
|
v<b>{{ pkg.version }}</b>
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import { Component, OnInit, OnChanges, Input, Output, EventEmitter, SimpleChanges } from '@angular/core';
|
import { Component, OnInit, OnChanges, Input, Output, EventEmitter, SimpleChanges, ElementRef } from '@angular/core';
|
||||||
import { OnDestroy } from '@angular/core/core';
|
import { OnDestroy } from '@angular/core/core';
|
||||||
import { ReplaySubject, Subject } from 'rxjs';
|
import { ReplaySubject, Subject } from 'rxjs';
|
||||||
import { takeUntil } from 'rxjs/operators';
|
import { takeUntil } from 'rxjs/operators';
|
||||||
import { FileSizePipe } from '../../pipes/file-size.pipe';
|
|
||||||
import { CatalogService } from '../helpers/catalog.service';
|
import { CatalogService } from '../helpers/catalog.service';
|
||||||
import { CatalogImage } from '../../catalog/models/image';
|
import { CatalogImage } from '../../catalog/models/image';
|
||||||
import { CatalogImageType } from '../models/image';
|
import { CatalogImageType } from '../models/image';
|
||||||
|
import { CatalogPackage } from '../models/package';
|
||||||
|
import { PackageGroupsEnum } from '../models/package-groups';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-packages',
|
selector: 'app-packages',
|
||||||
@ -15,7 +16,7 @@ import { CatalogImageType } from '../models/image';
|
|||||||
export class PackagesComponent implements OnInit, OnDestroy, OnChanges
|
export class PackagesComponent implements OnInit, OnDestroy, OnChanges
|
||||||
{
|
{
|
||||||
@Input()
|
@Input()
|
||||||
imageType: number;
|
imageType: CatalogImageType;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
image: CatalogImage;
|
image: CatalogImage;
|
||||||
@ -26,57 +27,29 @@ export class PackagesComponent implements OnInit, OnDestroy, OnChanges
|
|||||||
@Output()
|
@Output()
|
||||||
select = new EventEmitter();
|
select = new EventEmitter();
|
||||||
|
|
||||||
packageGroups: any[];
|
|
||||||
loadingIndicator: boolean;
|
loadingIndicator: boolean;
|
||||||
selectedPackageGroup: string;
|
packages: CatalogPackage[];
|
||||||
|
|
||||||
private packages: {};
|
private _packages: CatalogPackage[];
|
||||||
private _selectedPackage: {};
|
private _selectedPackage: CatalogPackage;
|
||||||
private destroy$ = new Subject();
|
private destroy$ = new Subject();
|
||||||
private onChanges$ = new ReplaySubject();
|
private onChanges$ = new ReplaySubject();
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------------------------------
|
||||||
constructor(private readonly catalogService: CatalogService,
|
constructor(private readonly catalogService: CatalogService,
|
||||||
private readonly fileSizePipe: FileSizePipe)
|
private readonly elementRef: ElementRef)
|
||||||
{
|
{
|
||||||
this.getPackages();
|
this.getPackages();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------------------------------
|
||||||
setPackageGroup(event, packageGroup: string)
|
set selectedPackage(value: CatalogPackage)
|
||||||
{
|
|
||||||
this.selectedPackageGroup = packageGroup;
|
|
||||||
|
|
||||||
if (!packageGroup) return;
|
|
||||||
|
|
||||||
switch (packageGroup)
|
|
||||||
{
|
|
||||||
case 'cpu':
|
|
||||||
this.packages[packageGroup].sort((a, b) => (a.vcpus || 1) - (b.vcpus || 1));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'disk':
|
|
||||||
this.packages[packageGroup].sort((a, b) => a.disk - b.disk);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'memory optimized':
|
|
||||||
this.packages[packageGroup].sort((a, b) => a.memory - b.memory);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
this.packages[packageGroup].sort((a, b) => ((a.vcpus || 1) - (b.vcpus || 1)) || (a.memory - b.memory) || (a.disk - b.disk));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------------------------------------
|
|
||||||
set selectedPackage(value)
|
|
||||||
{
|
{
|
||||||
this._selectedPackage = value;
|
this._selectedPackage = value;
|
||||||
|
|
||||||
this.select.next(value);
|
this.select.next(value);
|
||||||
}
|
}
|
||||||
get selectedPackage()
|
get selectedPackage(): CatalogPackage
|
||||||
{
|
{
|
||||||
return this._selectedPackage;
|
return this._selectedPackage;
|
||||||
}
|
}
|
||||||
@ -87,102 +60,50 @@ export class PackagesComponent implements OnInit, OnDestroy, OnChanges
|
|||||||
this.loadingIndicator = true;
|
this.loadingIndicator = true;
|
||||||
|
|
||||||
this.catalogService.getPackages()
|
this.catalogService.getPackages()
|
||||||
.subscribe(response =>
|
.subscribe(response =>
|
||||||
{
|
|
||||||
if (this.packages)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this.packages = response.reduce((groups, pkg) =>
|
|
||||||
{
|
{
|
||||||
let size = this.fileSizePipe.transform(pkg.memory * 1024 * 1024);
|
this._packages = response;
|
||||||
[pkg.memorySize, pkg.memorySizeLabel] = size.split(' ');
|
|
||||||
|
|
||||||
size = this.fileSizePipe.transform(pkg.disk * 1024 * 1024);
|
this.setPackagesByImageType();
|
||||||
[pkg.diskSize, pkg.diskSizeLabel] = size.split(' ');
|
|
||||||
|
|
||||||
const groupName = pkg.group.toLowerCase() || 'standard';
|
this.loadingIndicator = false;
|
||||||
|
});
|
||||||
const group = (groups[groupName] || []);
|
|
||||||
group.push(pkg);
|
|
||||||
groups[groupName] = group;
|
|
||||||
|
|
||||||
return groups;
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
this.setPackageGroups();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------------------------------
|
||||||
private setPackageGroups()
|
private setPackagesByImageType()
|
||||||
{
|
{
|
||||||
if (!this.packages || !this.image || !this.imageType)
|
this._selectedPackage = null;
|
||||||
return;
|
|
||||||
|
this.packages = this._packages.filter(x =>
|
||||||
// Setup the operating systems array-like object, sorted alphabetically
|
{
|
||||||
this.packageGroups = Object.keys(this.packages)
|
if (this.imageType === CatalogImageType.InfrastructureContainer && x.group === PackageGroupsEnum.Infra ||
|
||||||
.filter(packageGroup =>
|
this.imageType === CatalogImageType.VirtualMachine && x.group === PackageGroupsEnum.Vm)
|
||||||
{
|
{
|
||||||
this.packages[packageGroup].forEach(p =>
|
if (x.name === this.package)
|
||||||
{
|
this._selectedPackage = x;
|
||||||
if (p.name === this.package)
|
|
||||||
this._selectedPackage = p;
|
|
||||||
|
|
||||||
if (!p.brand || !this.image)
|
return true;
|
||||||
{
|
}
|
||||||
p.visible = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
p.visible = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.image.requirements.brand)
|
return false;
|
||||||
p.visible = p.visible && this.image.requirements.brand === p.brand;
|
}).sort((a, b) =>
|
||||||
|
{
|
||||||
|
if (a.vcpus === b.vcpus && a.memory === b.memory)
|
||||||
|
return a.memory > b.memory ? a.memory : b.memory;
|
||||||
|
|
||||||
if (this.image.type === 'zone-dataset')
|
if (a.memory === b.memory)
|
||||||
p.visible = p.visible && ['Spearhead', 'Spearhead-minimal'].includes(p.brand);
|
return a.disk > b.disk ? a.disk : b.disk;
|
||||||
|
|
||||||
if (this.image.type === 'lx-dataset')
|
return a.vcpus > b.vcpus ? a.vcpus : b.vcpus;
|
||||||
p.visible = p.visible && p.brand === 'lx';
|
});
|
||||||
|
|
||||||
if (this.image.type === 'zvol')
|
if (this._selectedPackage)
|
||||||
p.visible = p.visible && ['bhyve', 'kvm'].includes(p.brand);
|
setTimeout(() =>
|
||||||
|
{
|
||||||
if (this.imageType === CatalogImageType.InfrastructureContainer)
|
this.elementRef.nativeElement.querySelector(`#package-${this._selectedPackage.id}`)
|
||||||
p.visible = p.visible && packageGroup === 'infrastructure container';
|
.scrollIntoView({behavior:'auto', block: 'center'});
|
||||||
else if (this.imageType === CatalogImageType.VirtualMachine)
|
}, 0);
|
||||||
p.visible = p.visible && packageGroup === 'virtual machine';
|
|
||||||
});
|
|
||||||
|
|
||||||
switch (this.imageType | 0)
|
|
||||||
{
|
|
||||||
case CatalogImageType.InfrastructureContainer:
|
|
||||||
return this.packages[packageGroup].filter(x => x.visible).length &&
|
|
||||||
(!packageGroup || ['cpu', 'disk', 'memory optimized', 'standard', 'triton'].includes(packageGroup));
|
|
||||||
|
|
||||||
case CatalogImageType.VirtualMachine:
|
|
||||||
return this.packages[packageGroup].filter(x => x.visible).length &&
|
|
||||||
(!packageGroup || ['standard', 'triton', 'bhyve'].includes(packageGroup));
|
|
||||||
|
|
||||||
case CatalogImageType.Custom:
|
|
||||||
return this.packages[packageGroup].filter(x => x.visible).length &&
|
|
||||||
packageGroup !== 'infrastructure container' && packageGroup !== 'virtual machine';
|
|
||||||
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.sort((a, b) => a.localeCompare(b));
|
|
||||||
|
|
||||||
// Set the pre-selected package group
|
|
||||||
this.selectedPackageGroup = this.packageGroups[0];
|
|
||||||
|
|
||||||
if (this.selectedPackage)
|
|
||||||
this.select.emit(this.selectedPackage);
|
|
||||||
|
|
||||||
this.loadingIndicator = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------------------------------
|
||||||
@ -192,7 +113,7 @@ export class PackagesComponent implements OnInit, OnDestroy, OnChanges
|
|||||||
.subscribe((changes: SimpleChanges) =>
|
.subscribe((changes: SimpleChanges) =>
|
||||||
{
|
{
|
||||||
if (changes.image?.currentValue && changes.imageType?.currentValue)
|
if (changes.image?.currentValue && changes.imageType?.currentValue)
|
||||||
this.setPackageGroups();
|
this.setPackagesByImageType();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,11 +49,11 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="list-group list-group-flush flex-grow-1" *ngIf="images">
|
<div class="list-group list-group-flush flex-grow-1">
|
||||||
<div class="list-group-item list-group-item-action p-0 pe-2" *ngFor="let image of imageList">
|
<div class="list-group-item list-group-item-action p-0 pe-2" *ngFor="let image of imageList" id="image-{{ image.id }}">
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<input class="form-check-input" type="radio" id="image-{{ image.id }}" [value]="image" formControlName="image">
|
<input class="form-check-input" type="radio" id="img-{{ image.id }}" [value]="image" formControlName="image">
|
||||||
<label class="form-check-label" for="image-{{ image.id }}">
|
<label class="form-check-label" for="img-{{ image.id }}">
|
||||||
<small class="float-end d-flex align-items-center">
|
<small class="float-end d-flex align-items-center">
|
||||||
<span class="text-faded me-1">
|
<span class="text-faded me-1">
|
||||||
<span class="d-block text-end">{{ image.published_at | timeago }}</span>
|
<span class="d-block text-end">{{ image.published_at | timeago }}</span>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Component, OnInit, Input, Output, EventEmitter, OnDestroy } from '@angular/core';
|
import { Component, OnInit, Input, Output, EventEmitter, OnDestroy, ElementRef } from '@angular/core';
|
||||||
import { BsModalRef } from 'ngx-bootstrap/modal';
|
import { BsModalRef } from 'ngx-bootstrap/modal';
|
||||||
import { combineLatest, forkJoin, Subject } from 'rxjs';
|
import { combineLatest, forkJoin, Subject } from 'rxjs';
|
||||||
import { FormGroup, FormBuilder, Validators, AbstractControl, FormArray, ValidatorFn, ValidationErrors } from '@angular/forms';
|
import { FormGroup, FormBuilder, Validators, AbstractControl, FormArray, ValidatorFn, ValidationErrors } from '@angular/forms';
|
||||||
@ -43,7 +43,6 @@ export class MachineWizardComponent implements OnInit, OnDestroy
|
|||||||
dataCenters: any[];
|
dataCenters: any[];
|
||||||
|
|
||||||
loadingIndicator: boolean;
|
loadingIndicator: boolean;
|
||||||
loadingPackages: boolean;
|
|
||||||
save = new Subject<Machine>();
|
save = new Subject<Machine>();
|
||||||
working: boolean;
|
working: boolean;
|
||||||
editorForm: FormGroup;
|
editorForm: FormGroup;
|
||||||
@ -68,7 +67,8 @@ export class MachineWizardComponent implements OnInit, OnDestroy
|
|||||||
private readonly networkingService: NetworkingService,
|
private readonly networkingService: NetworkingService,
|
||||||
private readonly volumesService: VolumesService,
|
private readonly volumesService: VolumesService,
|
||||||
private readonly toastr: ToastrService,
|
private readonly toastr: ToastrService,
|
||||||
private readonly translateService: TranslateService)
|
private readonly translateService: TranslateService,
|
||||||
|
private readonly elementRef: ElementRef)
|
||||||
{
|
{
|
||||||
// When the user navigates away from this route, hide the modal
|
// When the user navigates away from this route, hide the modal
|
||||||
router.events
|
router.events
|
||||||
@ -257,8 +257,6 @@ export class MachineWizardComponent implements OnInit, OnDestroy
|
|||||||
|
|
||||||
this.kvmRequired = x?.requirements['brand'] === 'kvm' || x?.type === 'zvol' || false;
|
this.kvmRequired = x?.requirements['brand'] === 'kvm' || x?.type === 'zvol' || false;
|
||||||
|
|
||||||
this.loadingPackages = true;
|
|
||||||
|
|
||||||
this.computeEstimatedCost();
|
this.computeEstimatedCost();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -375,6 +373,13 @@ export class MachineWizardComponent implements OnInit, OnDestroy
|
|||||||
previousStep()
|
previousStep()
|
||||||
{
|
{
|
||||||
this.currentStep = this.currentStep > 1 ? this.currentStep - 1 : 1;
|
this.currentStep = this.currentStep > 1 ? this.currentStep - 1 : 1;
|
||||||
|
|
||||||
|
if (this.currentStep === 1)
|
||||||
|
setTimeout(() =>
|
||||||
|
{
|
||||||
|
this.elementRef.nativeElement.querySelector(`#image-${this.editorForm.get('image').value.id}`)
|
||||||
|
.scrollIntoView({behavior:'auto', block: 'center'});
|
||||||
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------------------------------
|
||||||
@ -400,6 +405,7 @@ export class MachineWizardComponent implements OnInit, OnDestroy
|
|||||||
// ----------------------------------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------------------------------
|
||||||
setPackage(selection: any)
|
setPackage(selection: any)
|
||||||
{
|
{
|
||||||
|
this.preselectedPackage = selection.name;
|
||||||
this.steps[1].selection = selection;
|
this.steps[1].selection = selection;
|
||||||
this.steps[1].complete = true;
|
this.steps[1].complete = true;
|
||||||
|
|
||||||
|
@ -174,9 +174,9 @@
|
|||||||
placement="top" [adaptivePosition]="false"></fa-icon>
|
placement="top" [adaptivePosition]="false"></fa-icon>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button class="btn btn-link text-info" [popover]="machineContextMenu" container="body"
|
<button class="btn btn-link text-info" [popover]="machineContextMenu" container="body" (click)="machine.contextMenu = true"
|
||||||
[popoverContext]="{ machine: machine }" placement="bottom left" containerClass="menu-dropdown"
|
[popoverContext]="{ machine: machine }" placement="bottom left" containerClass="menu-dropdown"
|
||||||
[outsideClick]="true">
|
[outsideClick]="true" triggers="" [isOpen]="machine.contextMenu" (onHidden)="machine.contextMenu = false">
|
||||||
<fa-icon icon="ellipsis-v" [fixedWidth]="true" size="sm"></fa-icon>
|
<fa-icon icon="ellipsis-v" [fixedWidth]="true" size="sm"></fa-icon>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -388,6 +388,8 @@ export class MachinesComponent implements OnInit, OnDestroy
|
|||||||
// ----------------------------------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------------------------------
|
||||||
startMachine(machine: Machine)
|
startMachine(machine: Machine)
|
||||||
{
|
{
|
||||||
|
machine.contextMenu = false;
|
||||||
|
|
||||||
if (machine.state !== 'stopped')
|
if (machine.state !== 'stopped')
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -425,6 +427,8 @@ export class MachinesComponent implements OnInit, OnDestroy
|
|||||||
// ----------------------------------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------------------------------
|
||||||
restartMachine(machine: Machine)
|
restartMachine(machine: Machine)
|
||||||
{
|
{
|
||||||
|
machine.contextMenu = false;
|
||||||
|
|
||||||
if (machine.state !== 'running')
|
if (machine.state !== 'running')
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -462,6 +466,8 @@ export class MachinesComponent implements OnInit, OnDestroy
|
|||||||
// ----------------------------------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------------------------------
|
||||||
stopMachine(machine: Machine)
|
stopMachine(machine: Machine)
|
||||||
{
|
{
|
||||||
|
machine.contextMenu = false;
|
||||||
|
|
||||||
if (machine.state !== 'running')
|
if (machine.state !== 'running')
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -498,6 +504,8 @@ export class MachinesComponent implements OnInit, OnDestroy
|
|||||||
// ----------------------------------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------------------------------
|
||||||
resizeMachine(machine: Machine)
|
resizeMachine(machine: Machine)
|
||||||
{
|
{
|
||||||
|
machine.contextMenu = false;
|
||||||
|
|
||||||
const modalConfig = {
|
const modalConfig = {
|
||||||
ignoreBackdropClick: true,
|
ignoreBackdropClick: true,
|
||||||
keyboard: false,
|
keyboard: false,
|
||||||
@ -541,6 +549,8 @@ export class MachinesComponent implements OnInit, OnDestroy
|
|||||||
// ----------------------------------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------------------------------
|
||||||
renameMachine(machine: Machine)
|
renameMachine(machine: Machine)
|
||||||
{
|
{
|
||||||
|
machine.contextMenu = false;
|
||||||
|
|
||||||
const machineName = machine.name;
|
const machineName = machine.name;
|
||||||
|
|
||||||
const modalConfig = {
|
const modalConfig = {
|
||||||
@ -590,6 +600,8 @@ export class MachinesComponent implements OnInit, OnDestroy
|
|||||||
// ----------------------------------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------------------------------
|
||||||
showTagEditor(machine: Machine, showMetadata = false)
|
showTagEditor(machine: Machine, showMetadata = false)
|
||||||
{
|
{
|
||||||
|
machine.contextMenu = false;
|
||||||
|
|
||||||
const modalConfig = {
|
const modalConfig = {
|
||||||
ignoreBackdropClick: true,
|
ignoreBackdropClick: true,
|
||||||
keyboard: false,
|
keyboard: false,
|
||||||
@ -609,6 +621,8 @@ export class MachinesComponent implements OnInit, OnDestroy
|
|||||||
// ----------------------------------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------------------------------
|
||||||
createImageFromMachine(machine: Machine)
|
createImageFromMachine(machine: Machine)
|
||||||
{
|
{
|
||||||
|
machine.contextMenu = false;
|
||||||
|
|
||||||
const modalConfig = {
|
const modalConfig = {
|
||||||
ignoreBackdropClick: true,
|
ignoreBackdropClick: true,
|
||||||
keyboard: false,
|
keyboard: false,
|
||||||
@ -673,6 +687,8 @@ export class MachinesComponent implements OnInit, OnDestroy
|
|||||||
// ----------------------------------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------------------------------
|
||||||
deleteMachine(machine: Machine)
|
deleteMachine(machine: Machine)
|
||||||
{
|
{
|
||||||
|
machine.contextMenu = false;
|
||||||
|
|
||||||
const modalConfig = {
|
const modalConfig = {
|
||||||
ignoreBackdropClick: true,
|
ignoreBackdropClick: true,
|
||||||
keyboard: false,
|
keyboard: false,
|
||||||
@ -716,6 +732,8 @@ export class MachinesComponent implements OnInit, OnDestroy
|
|||||||
// ----------------------------------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------------------------------
|
||||||
showMachineHistory(machine: Machine)
|
showMachineHistory(machine: Machine)
|
||||||
{
|
{
|
||||||
|
machine.contextMenu = false;
|
||||||
|
|
||||||
const modalConfig = {
|
const modalConfig = {
|
||||||
ignoreBackdropClick: true,
|
ignoreBackdropClick: true,
|
||||||
keyboard: false,
|
keyboard: false,
|
||||||
|
@ -51,4 +51,5 @@ export class Machine extends MachineRequest
|
|||||||
volumesEnabled: boolean;
|
volumesEnabled: boolean;
|
||||||
metadataKeys: string[];
|
metadataKeys: string[];
|
||||||
tagKeys: string[];
|
tagKeys: string[];
|
||||||
|
contextMenu: boolean;
|
||||||
}
|
}
|
||||||
|
@ -547,11 +547,10 @@ accordion
|
|||||||
.price
|
.price
|
||||||
{
|
{
|
||||||
color: #cd5c5c;
|
color: #cd5c5c;
|
||||||
vertical-align: middle;
|
|
||||||
padding: 0 .5rem;
|
|
||||||
margin-bottom: .25rem;
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
|
font-size: 1rem;
|
||||||
|
vertical-align: baseline;
|
||||||
}
|
}
|
||||||
|
|
||||||
.badge-discreet
|
.badge-discreet
|
||||||
|
Reference in New Issue
Block a user