added prices and fixed the title of all pages
This commit is contained in:
parent
2b106c2741
commit
aabc192b31
@ -32,7 +32,7 @@ export class AccountComponent implements OnInit, OnDestroy
|
|||||||
private readonly titleService: Title,
|
private readonly titleService: Title,
|
||||||
private readonly translationService: TranslateService)
|
private readonly translationService: TranslateService)
|
||||||
{
|
{
|
||||||
translationService.get('account.title').pipe(first()).subscribe(x => titleService.setTitle(`Joyent - ${x}`));
|
translationService.get('account.title').pipe(first()).subscribe(x => titleService.setTitle(`Spearhead - ${x}`));
|
||||||
|
|
||||||
//accountService.getUsers().subscribe(x => console.log(x));
|
//accountService.getUsers().subscribe(x => console.log(x));
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
|
|||||||
import { Observable, Subject } from 'rxjs';
|
import { Observable, Subject } from 'rxjs';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { Cacheable } from 'ts-cacheable';
|
import { Cacheable } from 'ts-cacheable';
|
||||||
import { delay, filter, map, 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';
|
||||||
|
|
||||||
@ -38,7 +38,16 @@ export class CatalogService
|
|||||||
})
|
})
|
||||||
getPackages(): Observable<CatalogPackage[]>
|
getPackages(): Observable<CatalogPackage[]>
|
||||||
{
|
{
|
||||||
return this.httpClient.get<CatalogPackage[]>(`/api/my/packages`);
|
return this.httpClient.get<CatalogPackage[]>(`/api/my/packages`)
|
||||||
|
.pipe(mergeMap(packages =>
|
||||||
|
{
|
||||||
|
return this.httpClient.get(`./assets/data/packages.json`).pipe(map(prices =>
|
||||||
|
{
|
||||||
|
packages.forEach(x => x.price = prices[x.id])
|
||||||
|
|
||||||
|
return packages;
|
||||||
|
}))
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------------------------------
|
||||||
@ -47,7 +56,16 @@ export class CatalogService
|
|||||||
})
|
})
|
||||||
getPackage(packageId: string): Observable<CatalogPackage>
|
getPackage(packageId: string): Observable<CatalogPackage>
|
||||||
{
|
{
|
||||||
return this.httpClient.get<CatalogPackage>(`/api/my/packages/${packageId}`);
|
return this.httpClient.get<CatalogPackage>(`/api/my/packages/${packageId}`)
|
||||||
|
.pipe(mergeMap(pkg =>
|
||||||
|
{
|
||||||
|
return this.httpClient.get(`./assets/data/packages.json`).pipe(map(prices =>
|
||||||
|
{
|
||||||
|
pkg.price = prices[pkg.id];
|
||||||
|
|
||||||
|
return pkg;
|
||||||
|
}))
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------------------------------
|
||||||
@ -56,7 +74,16 @@ export class CatalogService
|
|||||||
})
|
})
|
||||||
getImages(allStates = false): Observable<CatalogImage[]>
|
getImages(allStates = false): Observable<CatalogImage[]>
|
||||||
{
|
{
|
||||||
return this.httpClient.get<CatalogImage[]>(`/api/my/images?${allStates ? 'state=all' : ''}`);
|
return this.httpClient.get<CatalogImage[]>(`/api/my/images?${allStates ? 'state=all' : ''}`)
|
||||||
|
.pipe(mergeMap(images =>
|
||||||
|
{
|
||||||
|
return this.httpClient.get(`./assets/data/images.json`).pipe(map(prices =>
|
||||||
|
{
|
||||||
|
images.forEach(x => x.price = prices[x.id])
|
||||||
|
|
||||||
|
return images;
|
||||||
|
}))
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------------------------------
|
||||||
@ -65,7 +92,16 @@ export class CatalogService
|
|||||||
})
|
})
|
||||||
getImage(id: string): Observable<CatalogImage>
|
getImage(id: string): Observable<CatalogImage>
|
||||||
{
|
{
|
||||||
return this.httpClient.get<CatalogImage>(`/api/my/images/${id}`);
|
return this.httpClient.get<CatalogImage>(`/api/my/images/${id}`)
|
||||||
|
.pipe(mergeMap(image =>
|
||||||
|
{
|
||||||
|
return this.httpClient.get(`./assets/data/images.json`).pipe(map(prices =>
|
||||||
|
{
|
||||||
|
image.price = prices[image.id];
|
||||||
|
|
||||||
|
return image;
|
||||||
|
}))
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------------------------------
|
||||||
@ -141,7 +177,7 @@ export class CatalogService
|
|||||||
// ----------------------------------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------------------------------
|
||||||
cloneImage(imageId: string): Observable<any>
|
cloneImage(imageId: string): Observable<any>
|
||||||
{
|
{
|
||||||
// https://apidocs.joyent.com/cloudapi/#CloneImage
|
// https://apidocs.Spearhead.com/cloudapi/#CloneImage
|
||||||
return this.httpClient.post<any>(`/api/my/images/${imageId}?action=clone`, {})
|
return this.httpClient.post<any>(`/api/my/images/${imageId}?action=clone`, {})
|
||||||
.pipe(tap(() => imagesCacheBuster$.next()));
|
.pipe(tap(() => imagesCacheBuster$.next()));
|
||||||
}
|
}
|
||||||
|
@ -15,11 +15,11 @@
|
|||||||
<div class="btn-group flex-grow-1 flex-grow-sm-0 w-sm-auto w-100" dropdown placement="bottom left">
|
<div class="btn-group flex-grow-1 flex-grow-sm-0 w-sm-auto w-100" dropdown placement="bottom left">
|
||||||
<button class="btn btn-outline-info dropdown-toggle" dropdownToggle>
|
<button class="btn btn-outline-info dropdown-toggle" dropdownToggle>
|
||||||
Sort by
|
Sort by
|
||||||
<b *ngIf="editorForm.get('sortProperty').value === 'name'">name</b>
|
<span *ngIf="editorForm.get('sortProperty').value === 'name'">name</span>
|
||||||
<b *ngIf="editorForm.get('sortProperty').value === 'description'">description</b>
|
<span *ngIf="editorForm.get('sortProperty').value === 'description'">description</span>
|
||||||
<b *ngIf="editorForm.get('sortProperty').value === 'os'">operating system</b>
|
<span *ngIf="editorForm.get('sortProperty').value === 'os'">operating system</span>
|
||||||
<b *ngIf="editorForm.get('sortProperty').value === 'type'">type</b>
|
<span *ngIf="editorForm.get('sortProperty').value === 'type'">type</span>
|
||||||
<b *ngIf="editorForm.get('sortProperty').value === 'state'">status</b>
|
<span *ngIf="editorForm.get('sortProperty').value === 'state'">status</span>
|
||||||
</button>
|
</button>
|
||||||
<ul id="dropdown-split" *dropdownMenu class="dropdown-menu dropdown-menu-right" role="menu">
|
<ul id="dropdown-split" *dropdownMenu class="dropdown-menu dropdown-menu-right" role="menu">
|
||||||
<li role="menuitem">
|
<li role="menuitem">
|
||||||
@ -62,7 +62,7 @@
|
|||||||
<accordion [isAnimated]="false" [closeOthers]="false">
|
<accordion [isAnimated]="false" [closeOthers]="false">
|
||||||
<accordion-group [isOpen]="myImagesExpanded">
|
<accordion-group [isOpen]="myImagesExpanded">
|
||||||
<div class="d-flex justify-content-between align-items-center sticky-top" accordion-heading
|
<div class="d-flex justify-content-between align-items-center sticky-top" accordion-heading
|
||||||
tooltip="Show or hide my images" placement="top" container="body">
|
tooltip="Show or hide my images" placement="top" container="body" [adaptivePosition]="false">
|
||||||
<h4 class="text-info text-uppercase mb-0">My images</h4>
|
<h4 class="text-info text-uppercase mb-0">My images</h4>
|
||||||
|
|
||||||
<fa-icon icon="angle-right" [fixedWidth]="true" [rotate]="myImagesExpanded ? 90 : 0" class="text-info"></fa-icon>
|
<fa-icon icon="angle-right" [fixedWidth]="true" [rotate]="myImagesExpanded ? 90 : 0" class="text-info"></fa-icon>
|
||||||
|
@ -42,7 +42,7 @@ export class ImagesComponent implements OnInit, OnDestroy
|
|||||||
private readonly titleService: Title,
|
private readonly titleService: Title,
|
||||||
private readonly translationService: TranslateService)
|
private readonly translationService: TranslateService)
|
||||||
{
|
{
|
||||||
translationService.get('catalog.images.title').pipe(first()).subscribe(x => titleService.setTitle(`Joyent - ${x}`));
|
translationService.get('catalog.images.title').pipe(first()).subscribe(x => titleService.setTitle(`Spearhead - ${x}`));
|
||||||
|
|
||||||
// Configure FuseJs
|
// Configure FuseJs
|
||||||
this.fuseJsOptions = {
|
this.fuseJsOptions = {
|
||||||
|
@ -14,4 +14,5 @@ export class CatalogImage
|
|||||||
requirements: any;
|
requirements: any;
|
||||||
homepage: string;
|
homepage: string;
|
||||||
image_size: number;
|
image_size: number;
|
||||||
|
price: number;
|
||||||
}
|
}
|
||||||
|
@ -17,4 +17,5 @@ export class CatalogPackage
|
|||||||
description: string;
|
description: string;
|
||||||
disks: any[];
|
disks: any[];
|
||||||
flexible_disk: boolean;
|
flexible_disk: boolean;
|
||||||
|
price: number;
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
<span class="d-block">
|
<span class="d-block">
|
||||||
<span class="h3 text-uppercase">
|
<span class="h3 text-uppercase">
|
||||||
{{ pkg.name }}
|
{{ pkg.name }}
|
||||||
|
<span class="price" *ngIf="pkg.price">{{ pkg.price | currency }}/h</span>
|
||||||
<!--<small *ngIf="pkg.brand">{{ pkg.brand }}</small>-->
|
<!--<small *ngIf="pkg.brand">{{ pkg.brand }}</small>-->
|
||||||
</span>
|
</span>
|
||||||
<small class="text-faded pb-1 d-block">v<b>{{ pkg.version }}</b></small>
|
<small class="text-faded pb-1 d-block">v<b>{{ pkg.version }}</b></small>
|
||||||
|
@ -137,7 +137,7 @@ export class PackagesComponent implements OnInit, OnDestroy, OnChanges
|
|||||||
p.visible = this.image.requirements.brand === p.brand;
|
p.visible = this.image.requirements.brand === p.brand;
|
||||||
|
|
||||||
if (this.image.type === 'zone-dataset')
|
if (this.image.type === 'zone-dataset')
|
||||||
p.visible = ['joyent', 'joyent-minimal'].includes(p.brand);
|
p.visible = ['Spearhead', 'Spearhead-minimal'].includes(p.brand);
|
||||||
|
|
||||||
if (this.image.type === 'lx-dataset')
|
if (this.image.type === 'lx-dataset')
|
||||||
p.visible = p.brand === 'lx';
|
p.visible = p.brand === 'lx';
|
||||||
|
@ -208,6 +208,20 @@ export class InstancesService
|
|||||||
{
|
{
|
||||||
return this.httpClient.get(`/api/my/machines/${instanceId}/audit`);
|
return this.httpClient.get(`/api/my/machines/${instanceId}/audit`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------------------------------------------
|
||||||
|
@Cacheable()
|
||||||
|
getImagesRates(): Observable<any>
|
||||||
|
{
|
||||||
|
return this.httpClient.get(`./assets/data/images.json`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------------------------------------------
|
||||||
|
@Cacheable()
|
||||||
|
getPackagesRates(): Observable<any>
|
||||||
|
{
|
||||||
|
return this.httpClient.get(`./assets/data/packages.json`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type InstanceCallbackFunction = ((instance: Instance) => void);
|
export type InstanceCallbackFunction = ((instance: Instance) => void);
|
||||||
|
@ -35,7 +35,7 @@ export class MigrationsService
|
|||||||
// ----------------------------------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------------------------------
|
||||||
migrate(instanceId: string): Observable<any>
|
migrate(instanceId: string): Observable<any>
|
||||||
{
|
{
|
||||||
// https://apidocs.joyent.com/cloudapi/#Migrate
|
// https://apidocs.Spearhead.com/cloudapi/#Migrate
|
||||||
return this.httpClient.post(`/api/my/machines/${instanceId}/migrate`, { action: 'begin | sync | switch | automatic | pause | abort | watch', affinity: [] })
|
return this.httpClient.post(`/api/my/machines/${instanceId}/migrate`, { action: 'begin | sync | switch | automatic | pause | abort | watch', affinity: [] })
|
||||||
.pipe(tap(() => cacheBuster$.next()));
|
.pipe(tap(() => cacheBuster$.next()));
|
||||||
}
|
}
|
||||||
@ -43,7 +43,7 @@ export class MigrationsService
|
|||||||
// ----------------------------------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------------------------------
|
||||||
getMigrationProgress(instanceId: string): Observable<any>
|
getMigrationProgress(instanceId: string): Observable<any>
|
||||||
{
|
{
|
||||||
// https://apidocs.joyent.com/cloudapi/#Migrate
|
// https://apidocs.Spearhead.com/cloudapi/#Migrate
|
||||||
return this.httpClient.get(`/api/my/machines/${instanceId}/migrate?action=watch`);
|
return this.httpClient.get(`/api/my/machines/${instanceId}/migrate?action=watch`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,7 +61,10 @@
|
|||||||
<span class="badge rounded-pill" [class.bg-success]="image.state === 'active'"> </span>
|
<span class="badge rounded-pill" [class.bg-success]="image.state === 'active'"> </span>
|
||||||
</small>
|
</small>
|
||||||
<span class="d-block">
|
<span class="d-block">
|
||||||
<span class="h3">{{ image.name }}</span>
|
<span class="h3">
|
||||||
|
{{ image.name }}
|
||||||
|
<span class="price" *ngIf="image.price">{{ image.price | currency }}</span>
|
||||||
|
</span>
|
||||||
<small class="text-faded pb-1 d-block">v<b>{{ image.version }}</b></small>
|
<small class="text-faded pb-1 d-block">v<b>{{ image.version }}</b></small>
|
||||||
</span>
|
</span>
|
||||||
<span class="small">
|
<span class="small">
|
||||||
@ -248,7 +251,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="px-4" *ngIf="currentStep === 4">
|
<div class="px-4 d-flex flex-column h-100" *ngIf="currentStep === 4">
|
||||||
<!--<h5>Tell us which <b>data center</b> we should place this machine in</h5>
|
<!--<h5>Tell us which <b>data center</b> we should place this machine in</h5>
|
||||||
<select class="form-select image-type-selector" aria-label="Choose data center" formControlName="dataCenter">
|
<select class="form-select image-type-selector" aria-label="Choose data center" formControlName="dataCenter">
|
||||||
<option [value]="dataCenter" *ngFor="let dataCenter of dataCenters">{{ dataCenter | uppercase }}</option>
|
<option [value]="dataCenter" *ngFor="let dataCenter of dataCenters">{{ dataCenter | uppercase }}</option>
|
||||||
@ -303,25 +306,32 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="mt-3 lead text-success">
|
<div class="my-3">
|
||||||
You're about to create
|
<h5>Get an <b>estimated montly cost</b> based on the machine's running time</h5>
|
||||||
<b *ngIf="editorForm.get('imageType').value == 1">an infrastructure container</b>
|
|
||||||
<b *ngIf="editorForm.get('imageType').value == 2">a virtual machine</b>
|
|
||||||
<!--<b *ngIf="editorForm.get('imageType').value == 3">a Docker container</b>-->,
|
|
||||||
|
|
||||||
<b *ngIf="editorForm.get('package').value.description">{{ editorForm.get('package').value.description }}</b>
|
<div class="input-group input-group-cost">
|
||||||
|
<input type="number" min="1" max="1000000" class="form-control text-center" formControlName="estimatedMinutesRan"
|
||||||
|
placeholder="Number of hours"
|
||||||
|
tooltip="Number of hours this machine is running" />
|
||||||
|
<span class="input-group-text" tooltip="Package hourly rate">
|
||||||
|
hours
|
||||||
|
<b class="px-1">× {{ editorForm.get('package').value.price | currency: 'USD': 'symbol': '1.2-4' }}</b>
|
||||||
|
<span class="d-none d-sm-inline text-lowercase">(package hourly rate)</span>
|
||||||
|
</span>
|
||||||
|
<span class="input-group-text" *ngIf="editorForm.get('image').value.price" tooltip="Image monthly price">
|
||||||
|
<b class="px-1">+ {{ editorForm.get('image').value.price | currency: 'USD': 'symbol': '1.2-4' }}</b>
|
||||||
|
<span class="d-none d-sm-inline ps-1 text-lowercase">(Image monthly price)</span>
|
||||||
|
</span>
|
||||||
|
<span class="input-group-text" tooltip="Estimated cost per month">
|
||||||
|
<b>= {{ estimatedCost | currency: 'USD': 'symbol': '1.2-4' }}</b>
|
||||||
|
<span class="d-none d-sm-inline ps-1">(per month)</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<span *ngIf="!editorForm.get('package').value.description">
|
<span class="flex-grow-1"></span>
|
||||||
having
|
|
||||||
<b>{{ editorForm.get('package').value.vcpus || 1 }} vCPUs</b>,
|
|
||||||
<b>{{ editorForm.get('package').value.memory*1024*1024 | fileSize }} RAM</b> and
|
|
||||||
<b>{{ editorForm.get('package').value.disk*1024*1024 | fileSize }} storage</b>
|
|
||||||
</span>,
|
|
||||||
|
|
||||||
named <b>{{ editorForm.get('name').value }}</b>,
|
<p class="my-3 lead text-success" [innerHtml]="readyText"></p>
|
||||||
|
|
||||||
based on the <b>{{ editorForm.get('image').value.description }}</b>
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -130,7 +130,7 @@ p
|
|||||||
float: none;
|
float: none;
|
||||||
width: 1.4em;
|
width: 1.4em;
|
||||||
max-width: 1rem;
|
max-width: 1rem;
|
||||||
margin-bottom: .25rem;
|
margin-bottom: .75rem;
|
||||||
cursor: inherit;
|
cursor: inherit;
|
||||||
background-color: #0dc3e9;
|
background-color: #0dc3e9;
|
||||||
border-color: #0dc3e9;
|
border-color: #0dc3e9;
|
||||||
@ -213,6 +213,7 @@ p
|
|||||||
a
|
a
|
||||||
{
|
{
|
||||||
color: #0dc3e9;
|
color: #0dc3e9;
|
||||||
|
margin-left: .25rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -337,3 +338,24 @@ p.lead b
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.input-group-cost
|
||||||
|
{
|
||||||
|
.form-control
|
||||||
|
{
|
||||||
|
border-top-left-radius: .25rem;
|
||||||
|
border-bottom-left-radius: .25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group-text
|
||||||
|
{
|
||||||
|
background: transparent;
|
||||||
|
color: #3d5e8e;
|
||||||
|
border-color: #3d5e8e;
|
||||||
|
|
||||||
|
b
|
||||||
|
{
|
||||||
|
color: #ff9c07;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -12,6 +12,7 @@ import { NetworkingService } from '../../networking/helpers/networking.service';
|
|||||||
import { ToastrService } from 'ngx-toastr';
|
import { ToastrService } from 'ngx-toastr';
|
||||||
import { VolumesService } from '../../volumes/helpers/volumes.service';
|
import { VolumesService } from '../../volumes/helpers/volumes.service';
|
||||||
import { AuthService } from '../../helpers/auth.service';
|
import { AuthService } from '../../helpers/auth.service';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-instance-wizard',
|
selector: 'app-instance-wizard',
|
||||||
@ -49,6 +50,8 @@ export class InstanceWizardComponent implements OnInit, OnDestroy
|
|||||||
steps: any[];
|
steps: any[];
|
||||||
preselectedPackage: string;
|
preselectedPackage: string;
|
||||||
kvmRequired: boolean;
|
kvmRequired: boolean;
|
||||||
|
estimatedCost: number;
|
||||||
|
readyText: string;
|
||||||
|
|
||||||
private destroy$ = new Subject();
|
private destroy$ = new Subject();
|
||||||
private userId: string;
|
private userId: string;
|
||||||
@ -63,7 +66,8 @@ export class InstanceWizardComponent implements OnInit, OnDestroy
|
|||||||
private readonly catalogService: CatalogService,
|
private readonly catalogService: CatalogService,
|
||||||
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)
|
||||||
{
|
{
|
||||||
// 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
|
||||||
@ -134,7 +138,8 @@ export class InstanceWizardComponent implements OnInit, OnDestroy
|
|||||||
}),
|
}),
|
||||||
dataCenter: [],
|
dataCenter: [],
|
||||||
tags,
|
tags,
|
||||||
metadata
|
metadata,
|
||||||
|
estimatedMinutesRan: [24]
|
||||||
});
|
});
|
||||||
|
|
||||||
this.configureForm();
|
this.configureForm();
|
||||||
@ -257,6 +262,8 @@ export class InstanceWizardComponent 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.loadingPackages = true;
|
||||||
|
|
||||||
|
this.computeEstimatedCost();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.editorForm.get('package').valueChanges
|
this.editorForm.get('package').valueChanges
|
||||||
@ -269,6 +276,8 @@ export class InstanceWizardComponent implements OnInit, OnDestroy
|
|||||||
this.kvmRequired = this.editorForm.get('image').value?.requirements['brand'] === 'kvm' ||
|
this.kvmRequired = this.editorForm.get('image').value?.requirements['brand'] === 'kvm' ||
|
||||||
this.editorForm.get('image').value?.type === 'zvol' ||
|
this.editorForm.get('image').value?.type === 'zvol' ||
|
||||||
x?.brand === 'kvm' || false;
|
x?.brand === 'kvm' || false;
|
||||||
|
|
||||||
|
this.computeEstimatedCost();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.editorForm.get(['affinity', 'farFrom']).valueChanges.pipe(startWith(null))
|
this.editorForm.get(['affinity', 'farFrom']).valueChanges.pipe(startWith(null))
|
||||||
@ -278,8 +287,22 @@ export class InstanceWizardComponent implements OnInit, OnDestroy
|
|||||||
this.editorForm.get(['affinity', 'closeTo']).valueChanges.pipe(startWith(null))
|
this.editorForm.get(['affinity', 'closeTo']).valueChanges.pipe(startWith(null))
|
||||||
.pipe(takeUntil(this.destroy$))
|
.pipe(takeUntil(this.destroy$))
|
||||||
.subscribe(this.setAffinity.bind(this));
|
.subscribe(this.setAffinity.bind(this));
|
||||||
|
|
||||||
|
this.editorForm.get('estimatedMinutesRan').valueChanges
|
||||||
|
.pipe(takeUntil(this.destroy$))
|
||||||
|
.subscribe(this.computeEstimatedCost.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------------------------------------------
|
||||||
|
private computeEstimatedCost()
|
||||||
|
{
|
||||||
|
const estimatedMinutesRan = this.editorForm.get('estimatedMinutesRan').value || 1;
|
||||||
|
const imagePrice = this.editorForm.get('image').value?.price || 0;
|
||||||
|
const packagePrice = this.editorForm.get('package').value?.price || 0;
|
||||||
|
|
||||||
|
this.estimatedCost = imagePrice + packagePrice * estimatedMinutesRan;
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------------------------------
|
||||||
private atLeastOneSelectionValidator: ValidatorFn = (array: FormArray): ValidationErrors | null =>
|
private atLeastOneSelectionValidator: ValidatorFn = (array: FormArray): ValidationErrors | null =>
|
||||||
{
|
{
|
||||||
@ -373,6 +396,20 @@ export class InstanceWizardComponent implements OnInit, OnDestroy
|
|||||||
nextStep()
|
nextStep()
|
||||||
{
|
{
|
||||||
this.currentStep = this.currentStep < this.steps.length ? this.currentStep + 1 : this.steps.length;
|
this.currentStep = this.currentStep < this.steps.length ? this.currentStep + 1 : this.steps.length;
|
||||||
|
|
||||||
|
if (this.currentStep < this.steps.length) return;
|
||||||
|
|
||||||
|
this.readyText = this.translateService.instant('dashboard.wizard.ready', {
|
||||||
|
imageType: this.editorForm.get('imageType').value == 1
|
||||||
|
? this.translateService.instant('dashboard.wizard.readyImageTypeContainer')
|
||||||
|
: this.translateService.instant('dashboard.wizard.readyImageTypeVm'),
|
||||||
|
packageDescription: this.editorForm.get('package').value.description ||
|
||||||
|
`<b>${this.editorForm.get('package').value.vcpus || 1}</b> vCPUs, ` +
|
||||||
|
`<b>${this.fileSizePipe.transform(this.editorForm.get('package').value.memory * 1024 * 1024)}</b> RAM, ` +
|
||||||
|
`<b>${this.fileSizePipe.transform(this.editorForm.get('package').value.disk * 1024 * 1024)}</b> storage`,
|
||||||
|
machineName: this.editorForm.get('name').value,
|
||||||
|
imageDescription: this.editorForm.get('image').value.description
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------------------------------
|
||||||
|
@ -83,8 +83,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="overflow-auto flex-grow-1 mt-3" id="scrollingBlock">
|
<div class="overflow-auto flex-grow-1 mt-3 d-flex flex-column" id="scrollingBlock">
|
||||||
<div class="container py-2">
|
<div class="container flex-grow-1 py-2">
|
||||||
<h2 *ngIf="listItems && listItems.length === 0 && instances && instances.length > 0" class="text-uppercase">
|
<h2 *ngIf="listItems && listItems.length === 0 && instances && instances.length > 0" class="text-uppercase">
|
||||||
{{ 'dashboard.list.noResults' | translate }}
|
{{ 'dashboard.list.noResults' | translate }}
|
||||||
</h2>
|
</h2>
|
||||||
@ -159,19 +159,13 @@
|
|||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div class="btn-group btn-group-sm" dropdown placement="bottom right" *ngIf="!instance.loading">
|
<div class="btn-group btn-group-sm" dropdown placement="bottom right" *ngIf="!instance.loading">
|
||||||
<button class="btn btn-link text-warning" (click)="restartMachine(instance)"
|
|
||||||
*ngIf="instance.state === 'running'">
|
|
||||||
<fa-icon icon="power-off" [fixedWidth]="true" size="sm" tooltip="Restart this machine"
|
|
||||||
container="body" placement="top" [adaptivePosition]="false"></fa-icon>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button class="btn btn-link text-success" (click)="startMachine(instance)"
|
<button class="btn btn-link text-success" (click)="startMachine(instance)"
|
||||||
*ngIf="instance.state === 'stopped'">
|
*ngIf="instance.state === 'stopped'">
|
||||||
<fa-icon icon="play" [fixedWidth]="true" size="sm" tooltip="Start this machine" container="body"
|
<fa-icon icon="power-off" [fixedWidth]="true" size="sm" tooltip="Start this machine" container="body"
|
||||||
placement="top" [adaptivePosition]="false"></fa-icon>
|
placement="top" [adaptivePosition]="false"></fa-icon>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button class="btn btn-link text-info" [popover]="instanceContextMenu"
|
<button class="btn btn-link text-info" [popover]="instanceContextMenu" container="body"
|
||||||
[popoverContext]="{ instance: instance }" placement="bottom left" containerClass="menu-dropdown"
|
[popoverContext]="{ instance: instance }" placement="bottom left" containerClass="menu-dropdown"
|
||||||
[outsideClick]="true">
|
[outsideClick]="true">
|
||||||
<fa-icon icon="ellipsis-v" [fixedWidth]="true" size="sm"></fa-icon>
|
<fa-icon icon="ellipsis-v" [fixedWidth]="true" size="sm"></fa-icon>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
:host
|
:host
|
||||||
{
|
{
|
||||||
flex-direction: column;
|
flex-grow: 1;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ export class InstancesComponent implements OnInit, OnDestroy
|
|||||||
private readonly titleService: Title,
|
private readonly titleService: Title,
|
||||||
private readonly translationService: TranslateService)
|
private readonly translationService: TranslateService)
|
||||||
{
|
{
|
||||||
translationService.get('dashboard.title').pipe(first()).subscribe(x => titleService.setTitle(`Joyent - ${x}`));
|
translationService.get('dashboard.title').pipe(first()).subscribe(x => titleService.setTitle(`Spearhead - ${x}`));
|
||||||
|
|
||||||
this.lazyLoadDelay = this.minimumLazyLoadDelay;
|
this.lazyLoadDelay = this.minimumLazyLoadDelay;
|
||||||
|
|
||||||
|
@ -21,9 +21,9 @@
|
|||||||
<div class="btn-group flex-grow-1 flex-grow-sm-0 w-sm-auto w-100" dropdown placement="bottom right">
|
<div class="btn-group flex-grow-1 flex-grow-sm-0 w-sm-auto w-100" dropdown placement="bottom right">
|
||||||
<button class="btn btn-outline-info dropdown-toggle" dropdownToggle>
|
<button class="btn btn-outline-info dropdown-toggle" dropdownToggle>
|
||||||
Sort by
|
Sort by
|
||||||
<b *ngIf="editorForm.get('sortProperty').value === 'action'">action</b>
|
<span *ngIf="editorForm.get('sortProperty').value === 'action'">action</span>
|
||||||
<b *ngIf="editorForm.get('sortProperty').value === 'enabled'">status</b>
|
<span *ngIf="editorForm.get('sortProperty').value === 'enabled'">status</span>
|
||||||
<b *ngIf="editorForm.get('sortProperty').value === 'description'">description</b>
|
<span *ngIf="editorForm.get('sortProperty').value === 'description'">description</span>
|
||||||
</button>
|
</button>
|
||||||
<ul *dropdownMenu class="dropdown-menu dropdown-menu-right" role="menu">
|
<ul *dropdownMenu class="dropdown-menu dropdown-menu-right" role="menu">
|
||||||
<li role="menuitem">
|
<li role="menuitem">
|
||||||
|
@ -39,7 +39,7 @@ export class FirewallRulesComponent implements OnInit, OnDestroy
|
|||||||
private readonly titleService: Title,
|
private readonly titleService: Title,
|
||||||
private readonly translationService: TranslateService)
|
private readonly translationService: TranslateService)
|
||||||
{
|
{
|
||||||
translationService.get('networking.firewall.title').pipe(first()).subscribe(x => titleService.setTitle(`Joyent - ${x}`));
|
translationService.get('networking.firewall.title').pipe(first()).subscribe(x => titleService.setTitle(`Spearhead - ${x}`));
|
||||||
|
|
||||||
// Configure FuseJs
|
// Configure FuseJs
|
||||||
this.fuseJsOptions = {
|
this.fuseJsOptions = {
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
<div class="btn-group flex-grow-1 flex-grow-sm-0 mb-3 w-sm-auto w-100" dropdown placement="bottom left">
|
<div class="btn-group flex-grow-1 flex-grow-sm-0 mb-3 w-sm-auto w-100" dropdown placement="bottom left">
|
||||||
<button class="btn btn-outline-info dropdown-toggle" dropdownToggle>
|
<button class="btn btn-outline-info dropdown-toggle" dropdownToggle>
|
||||||
Sort by <b>{{ editorForm.get('sortProperty').value === 'vlan_id' ? 'id' : editorForm.get('sortProperty').value }}</b>
|
Sort by {{ editorForm.get('sortProperty').value === 'vlan_id' ? 'id' : editorForm.get('sortProperty').value }}
|
||||||
</button>
|
</button>
|
||||||
<ul id="dropdown-split" *dropdownMenu class="dropdown-menu dropdown-menu-right" role="menu">
|
<ul id="dropdown-split" *dropdownMenu class="dropdown-menu dropdown-menu-right" role="menu">
|
||||||
<li role="menuitem">
|
<li role="menuitem">
|
||||||
|
@ -38,7 +38,7 @@ export class NetworksComponent implements OnInit, OnDestroy
|
|||||||
private readonly titleService: Title,
|
private readonly titleService: Title,
|
||||||
private readonly translationService: TranslateService)
|
private readonly translationService: TranslateService)
|
||||||
{
|
{
|
||||||
translationService.get('networking.networks.title').pipe(first()).subscribe(x => titleService.setTitle(`Joyent - ${x}`));
|
translationService.get('networking.networks.title').pipe(first()).subscribe(x => titleService.setTitle(`Spearhead - ${x}`));
|
||||||
|
|
||||||
this.getVlans();
|
this.getVlans();
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ export class SecurityComponent implements OnInit, OnDestroy
|
|||||||
private readonly titleService: Title,
|
private readonly titleService: Title,
|
||||||
private readonly translationService: TranslateService)
|
private readonly translationService: TranslateService)
|
||||||
{
|
{
|
||||||
translationService.get('security.title').pipe(first()).subscribe(x => titleService.setTitle(`Joyent - ${x}`));
|
translationService.get('security.title').pipe(first()).subscribe(x => titleService.setTitle(`Spearhead - ${x}`));
|
||||||
|
|
||||||
forkJoin({
|
forkJoin({
|
||||||
users: securityService.getUsers(),
|
users: securityService.getUsers(),
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
<div class="btn-group flex-grow-1 flex-grow-sm-0 w-sm-auto w-100" dropdown placement="bottom left">
|
<div class="btn-group flex-grow-1 flex-grow-sm-0 w-sm-auto w-100" dropdown placement="bottom left">
|
||||||
<button class="btn btn-outline-info dropdown-toggle" dropdownToggle>
|
<button class="btn btn-outline-info dropdown-toggle" dropdownToggle>
|
||||||
Sort by <b>{{ editorForm.get('sortProperty').value === 'vlan_id' ? 'id' : editorForm.get('sortProperty').value }}</b>
|
Sort by {{ editorForm.get('sortProperty').value === 'vlan_id' ? 'id' : editorForm.get('sortProperty').value }}
|
||||||
</button>
|
</button>
|
||||||
<ul id="dropdown-split" *dropdownMenu class="dropdown-menu dropdown-menu-right" role="menu">
|
<ul id="dropdown-split" *dropdownMenu class="dropdown-menu dropdown-menu-right" role="menu">
|
||||||
<li role="menuitem">
|
<li role="menuitem">
|
||||||
|
@ -43,7 +43,7 @@ export class VolumesComponent implements OnInit, OnDestroy
|
|||||||
private readonly titleService: Title,
|
private readonly titleService: Title,
|
||||||
private readonly translationService: TranslateService)
|
private readonly translationService: TranslateService)
|
||||||
{
|
{
|
||||||
translationService.get('volumes.title').pipe(first()).subscribe(x => titleService.setTitle(`Joyent - ${x}`));
|
translationService.get('volumes.title').pipe(first()).subscribe(x => titleService.setTitle(`Spearhead - ${x}`));
|
||||||
|
|
||||||
// Configure FuseJs
|
// Configure FuseJs
|
||||||
this.fuseJsOptions = {
|
this.fuseJsOptions = {
|
||||||
|
6
app/src/assets/data/images.json
Normal file
6
app/src/assets/data/images.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"9d73e502-9fa6-11e3-7da8-bb6a8876bb21": 70,
|
||||||
|
"8d73e502-9fa6-11e3-7da8-bb6a8876bb21": 70,
|
||||||
|
"6d73e502-9fa6-11e3-7da8-bb6a8876bb21": 70,
|
||||||
|
"3d73e502-9fa6-11e3-8fa8-bb6a8876bb21": 70
|
||||||
|
}
|
248
app/src/assets/data/packages.json
Normal file
248
app/src/assets/data/packages.json
Normal file
@ -0,0 +1,248 @@
|
|||||||
|
{
|
||||||
|
"22218452-a2d5-ea3c-d7db-8ac394f396b7": 0.066,
|
||||||
|
"f10e7447-c35f-4e0f-9e0b-f7d22366341e": 0.027,
|
||||||
|
"8b4fdd0b-6448-692e-8b47-924d9b6fa298": 0.089,
|
||||||
|
"47fe0a45-c839-eecf-df1a-ffbb835ceb79": 0.034,
|
||||||
|
"468c03e2-334c-cd99-a8cd-c24e220f18c8": 0.296,
|
||||||
|
"0b95abef-a7b4-4875-db2b-d7711f4ea31f": 0.186,
|
||||||
|
"372b15e7-2a4f-4e1a-b46d-97f795623ec7": 0.178,
|
||||||
|
"127a3a30-d7a3-c31b-cac3-b76c79afca71": 0.186,
|
||||||
|
"372271df-c3b6-e9a1-9308-e2e91ea7a83b": 0.027,
|
||||||
|
"a6df1682-d50b-c357-9c11-de94d5893c44": 0.044,
|
||||||
|
"5af898f4-34c9-c9bc-c232-aef873f37c07": 0.089,
|
||||||
|
"bd32d8e3-e5b7-6e8e-91f1-8eadc2e3fbd9": 0.355,
|
||||||
|
"182a5990-a524-cd82-eb2e-90aa1b092abc": 0.044,
|
||||||
|
"948d12c0-3270-c097-9d0c-8efb9f9353eb": 0.034,
|
||||||
|
"db0093fc-916f-682b-c1c1-b69275ed45bf": 0.298,
|
||||||
|
"7cca3866-dad8-615e-b349-b28c632b625a": 0.298,
|
||||||
|
"c3c0de9d-91a6-46d0-f750-b3da8437bd74": 0.235,
|
||||||
|
"88e0f747-29fc-6787-9c11-ac0914bd7527": 0.178,
|
||||||
|
"600737b8-081a-ceed-8ff9-ffae9feba617": 0.044,
|
||||||
|
"cedb1804-54df-4bb2-e069-cdd4cc684704": 0.044,
|
||||||
|
"8d88e28a-ef4f-ef3a-a006-917c252e3c70": 0.178,
|
||||||
|
"70bc395a-3d8b-4833-81d1-af4be39af974": 0.089,
|
||||||
|
"52068036-7178-4645-b988-be84f5195332": 0.355,
|
||||||
|
"26a95af3-dd19-6df2-d22c-a14c8d485cd8": 0.004,
|
||||||
|
"8ed7a214-dfa9-6462-923a-ff67279cfa66": 0.002,
|
||||||
|
"5dd22dfb-e595-4f36-bada-f690b8166376": 0.006,
|
||||||
|
"0580e086-d4a6-442c-c7a7-d674f2245301": 0.008,
|
||||||
|
"8e93c22f-d513-6fd8-d440-ffcaf0cd4589": 0.012,
|
||||||
|
"6de53561-6e14-6a8a-bc94-a1566b5725a9": 0.014,
|
||||||
|
"3e64d126-0a6e-c4e4-9d84-81243ea43493": 0.016,
|
||||||
|
"c47d5124-63cd-6ceb-dbba-b6f42464b147": 0.041,
|
||||||
|
"c37a77ba-f0cd-42fa-feb6-9e8dff956d87": 0.062,
|
||||||
|
"4faddb31-1c71-ef29-bcd8-b7910ea3e6fe": 0.021,
|
||||||
|
"ed4d095d-f18c-443a-c847-ad64c4af44fa": 0.018,
|
||||||
|
"393e32ae-79f7-6b97-c43c-e5e7614cacf4": 0.082,
|
||||||
|
"1b2cc5f5-5ce4-62c8-f552-f3b8dec2caea": 0.010,
|
||||||
|
"fb25ac7c-78ee-6cd1-ede4-dd7e1c703a89": 0.103,
|
||||||
|
"bf25f174-41d3-eae5-b158-980dcdd99051": 0.123,
|
||||||
|
"0737f0ad-4303-ed1d-d400-88cc21698cd8": 0.164,
|
||||||
|
"683e437e-1f9c-c713-caed-fd2a89e15ac5": 0.185,
|
||||||
|
"4406105d-ff34-438e-f097-d8f3c1a3fc49": 0.144,
|
||||||
|
"963b791d-b101-4021-bf51-fed400088ab3": 0.205,
|
||||||
|
"9031e7d1-2965-65e2-9c57-801930bdf67f": 0.030,
|
||||||
|
"123407e6-c995-6ece-b2cf-b391e653385c": 0.060,
|
||||||
|
"8238843b-5daf-c98f-8c65-c09f064370d9": 0.040,
|
||||||
|
"edd4d2b9-ee55-ebbb-aff4-ef0a61917bed": 0.080,
|
||||||
|
"22b44234-b13b-47a7-f7fa-dcb68cccc3e5": 0.160,
|
||||||
|
"03ce4af4-4d24-c4b6-f31e-ad2e9fede8f6": 0.268,
|
||||||
|
"0fcc3289-8cdf-ce8c-83ec-ab275fec9183": 0.276,
|
||||||
|
"a188646b-530d-cd90-cd22-cf2f42a93351": 0.276,
|
||||||
|
"a93f6fd2-ca7b-e15c-96e8-cc1c3b61fce8": 0.283,
|
||||||
|
"c88e3006-6f04-66d6-a742-ad6b43e1ccd2": 0.283,
|
||||||
|
"3e64bdda-ef4e-4c9a-c481-e15c0d40c9b9": 0.030,
|
||||||
|
"53213fe1-3d60-6ed6-9904-ed8c32aab4e6": 0.040,
|
||||||
|
"d9a46a90-9816-6a74-abed-f67a198f646e": 0.080,
|
||||||
|
"fe4bcadd-eb50-e1c9-e712-fb0cd724aefe": 0.160,
|
||||||
|
"34e48ec3-3872-c4e6-c17c-eb626145833e": 0.268,
|
||||||
|
"14b4c0d9-32ab-e23c-a03a-b7420829c66c": 0.276,
|
||||||
|
"232ef917-2e0a-68d0-9238-b825e08e640f": 0.060,
|
||||||
|
"b03c27ca-bf76-e6e1-8404-e486b0aee197": 0.117,
|
||||||
|
"2950e56a-33af-ca43-b19a-ea165f64632e": 0.241,
|
||||||
|
"cca75627-5669-4fd6-8849-df2bf3fe3891": 0.121,
|
||||||
|
"ff3348da-dc29-40c8-a5b5-c3f73198e27e": 0.024,
|
||||||
|
"28804ada-03e5-ef21-94a2-bf1af50ed71f": 0.093,
|
||||||
|
"9c20bda4-aeb2-ed87-e90f-e965ea6bb06a": 0.320,
|
||||||
|
"64ceedda-b153-e18b-bcd6-8c604fd7aa8e": 0.076,
|
||||||
|
"85b4bd34-19fa-6d7b-f957-bc041f74f465": 1.682,
|
||||||
|
"f8130ce9-6f0a-cdb6-9d15-d6975a0db5d2": 0.118,
|
||||||
|
"380a57a4-7b12-cf8f-b376-eef360d3d544": 0.235,
|
||||||
|
"3f9f4d22-e762-cb15-db7b-b9a354ae4ccd": 0.026,
|
||||||
|
"402b7496-63f3-e60a-d852-96e3f1abc976": 0.053,
|
||||||
|
"d577dcb8-f7a5-672c-ae2a-b83141659f83": 0.106,
|
||||||
|
"b7a97f19-4b0a-4704-fcf0-a3796a2c04fe": 0.211,
|
||||||
|
"b31fc9e7-59ae-46ec-bd37-9038abffe940": 0.422,
|
||||||
|
"5d04037d-313d-cc39-e780-baafeb352376": 0.855,
|
||||||
|
"c387fb70-f95a-4026-f470-ef8a6ec170c9": 0.080,
|
||||||
|
"0702404f-94e7-666e-cc33-ec0d602d13ea": 0.150,
|
||||||
|
"b76821ed-d842-6965-a960-b576cb239153": 0.300,
|
||||||
|
"082aa0f1-5cae-6789-98e2-cacacbf0c13d": 0.370,
|
||||||
|
"28001852-d8c0-4697-b36c-fe3d41b092b4": 0.422,
|
||||||
|
"50cdae86-3136-ead9-b3e1-fe9b369fa43c": 0.034,
|
||||||
|
"7f69ed8b-2c9e-6757-bae1-9732710fc965": 0.159,
|
||||||
|
"69e46054-1961-487a-9e6f-a37ebdfe2048": 0.283,
|
||||||
|
"06959c2d-2390-ce94-9a6b-fc38537e632e": 0.024,
|
||||||
|
"aa7976f9-ee28-e16e-8a91-f0bd601dd0d1": 0.076,
|
||||||
|
"03b52642-7572-cbb4-89b1-80bd1b93e2e4": 0.060,
|
||||||
|
"ad22bdf1-a3c4-c6b8-b5b9-e2dc5d67b1cd": 0.060,
|
||||||
|
"dd153024-c7fe-44b0-929c-ea3e3e16e1ae": 0.117,
|
||||||
|
"063843ba-dc3b-6a09-8630-cf34c11245e2": 0.040,
|
||||||
|
"d4341f46-c9e6-e4cc-e9f3-b9fb3f9c8adb": 0.380,
|
||||||
|
"e90e0436-f01b-c3a2-bad3-92e37659c34b": 0.370,
|
||||||
|
"74ca19ac-741e-cd9f-fba7-dd87f8ad7caa": 0.370,
|
||||||
|
"71eb8b96-1792-c639-94e7-924f76014bf6": 0.053,
|
||||||
|
"7e1ac676-b3df-c80e-cf10-d7550caccd07": 0.106,
|
||||||
|
"e516512d-1539-6dc5-e1b5-9d6acb812279": 0.211,
|
||||||
|
"7f588535-1e16-e77a-dde0-b133031f96c1": 0.855,
|
||||||
|
"430ae3ee-a8c6-e92d-feb7-aebe6d86d897": 0.080,
|
||||||
|
"19e12410-1f04-e822-e828-999d7a7a4140": 0.150,
|
||||||
|
"8847f7dc-a195-66a2-a316-c273e9622f04": 0.300,
|
||||||
|
"1951fdbe-b753-e80f-89b1-aca3bffdc20d": 0.380,
|
||||||
|
"6e47f0c5-8d86-6ae1-b9e9-85d9d60177f8": 0.283,
|
||||||
|
"034ede05-5bc4-cc9d-cebe-9e013e9b8fa2": 0.159,
|
||||||
|
"eb662801-1f41-6b49-96d0-f19baf370978": 0.040,
|
||||||
|
"e22534bb-bbee-e834-a158-c46a4debe1cb": 0.080,
|
||||||
|
"80930ddd-cb9d-6cf6-d4fd-ddb6c9b3dd99": 0.160,
|
||||||
|
"1d08bf5d-c5a3-cf4f-ab6b-a1c224415cf5": 0.268,
|
||||||
|
"a9fb2452-5674-efee-be80-b07f1e1fa713": 0.276,
|
||||||
|
"d75a45f1-5063-4b6b-d4b1-d02c3fb342e7": 0.121,
|
||||||
|
"7e6f2e3e-f644-47a3-b751-fd94f3f1acec": 0.211,
|
||||||
|
"9047dfd5-0c2e-6b2e-8e37-d41d1687b7a5": 0.320,
|
||||||
|
"39207fcc-338c-e0e5-ad8e-f528ce4fc684": 0.093,
|
||||||
|
"cca6a0a8-5685-cd79-de28-ac7bda8427bc": 0.283,
|
||||||
|
"87828074-22c5-6e4e-a2de-9783bf424eb2": 0.118,
|
||||||
|
"a22daa6c-6973-ecba-c354-eb618808f8f6": 0.241,
|
||||||
|
"42b27154-3478-ce5d-8425-f026fb6b52f7": 1.682,
|
||||||
|
"8f799461-8139-cc23-9b26-9b049d2c62f8": 0.106,
|
||||||
|
"8b9cf667-aac9-69a0-c3ea-fcb7a4824a0d": 0.106,
|
||||||
|
"fb92e427-a4b9-4cae-dfa7-dc71bcd41863": 0.076,
|
||||||
|
"85293756-d2b1-6c50-d79c-ea0d9bcfc7e5": 0.079,
|
||||||
|
"287c3d76-deec-4aed-9407-c0bb73e6ba4a": 0.134,
|
||||||
|
"c62c0364-b47c-c723-b48a-a98299d3133d": 0.268,
|
||||||
|
"52e42908-74e9-e702-a1ef-a54e92587f92": 0.390,
|
||||||
|
"a585f939-ea86-6a4d-a28e-b083f08a8cea": 0.280,
|
||||||
|
"67764556-cc01-c47d-c994-99e1fd03b768": 0.241,
|
||||||
|
"c76cd2f7-6034-e117-f796-d54364002ecf": 0.022,
|
||||||
|
"d2170614-2c80-cdc8-853f-e92bbd09e568": 0.022,
|
||||||
|
"0b5f4762-9666-eeba-d3d7-95eecc815db7": 0.022,
|
||||||
|
"e131e417-f7e3-48bf-91b4-ea12d5df6b94": 0.038,
|
||||||
|
"9a6c17ba-02f4-c187-b16a-9e9c8cfbf575": 0.030,
|
||||||
|
"d60377d2-3e4f-ecf0-c013-b9231e814df4": 0.049,
|
||||||
|
"059da774-2e9f-eeb7-eaea-a25275e534f4": 0.380,
|
||||||
|
"0c592d4a-5bd6-e2d3-c58c-885ee024c88f": 0.607,
|
||||||
|
"8cf5eca4-a00e-e826-b803-a38398bb0a81": 0.607,
|
||||||
|
"2c0eb6a0-896b-c60c-ce38-cbae9aa3705f": 0.243,
|
||||||
|
"05859b98-ecfe-cd38-a06e-ac5bf224031f": 0.133,
|
||||||
|
"e8079c97-09ce-ea1c-8d58-ea864d22d836": 0.017,
|
||||||
|
"b80a7cd8-4699-ed26-dbe6-c9c804f03d9c": 0.268,
|
||||||
|
"7fb90259-f044-c432-bf27-f5008963e37e": 0.117,
|
||||||
|
"f5bee04c-208c-66e5-927c-86ebed05fe4e": 0.117,
|
||||||
|
"f3cfc57b-5aa8-ebdc-c4ff-ea1ea9ccbab4": 0.106,
|
||||||
|
"3bead424-e188-60e8-d435-82ebec698c4d": 0.053,
|
||||||
|
"125117ae-5d01-ca19-a9db-bd55fcae6149": 0.268,
|
||||||
|
"a1b71c42-f9ca-6874-d6eb-b10053bf17c1": 0.106,
|
||||||
|
"e97fd9a8-afe3-4b95-e7c1-b6fc186b615c": 0.080,
|
||||||
|
"5e62be81-ae92-efc4-c42a-c6954a23de78": 0.117,
|
||||||
|
"aaabc1cb-9857-4f7a-8893-ff179dde23c6": 0.243,
|
||||||
|
"f4979b69-4fd0-6e1a-db4c-ff4de8170071": 0.076,
|
||||||
|
"66b73715-85ed-cb64-b5a1-bab78eb20e15": 0.160,
|
||||||
|
"b744d6d8-82f4-6a6c-bc10-a8fc2034ef0d": 0.205,
|
||||||
|
"d7544477-53b3-e8eb-e70f-eb3142bc4e19": 0.034,
|
||||||
|
"af1bce69-09ed-6ba4-c3a6-d2f14a3ad493": 0.026,
|
||||||
|
"5597cead-e17a-e675-9a4b-e412610cad07": 0.205,
|
||||||
|
"99fc25e2-605c-c1a7-f490-a445aef18795": 0.060,
|
||||||
|
"113ebd76-81b3-ed7d-f35f-f713bdb72249": 0.241,
|
||||||
|
"78283ad0-7c5e-6a76-d46b-b5dad5d06338": 0.211,
|
||||||
|
"1e95adcb-e160-c773-e375-b57933d0aae0": 0.040,
|
||||||
|
"a40afe3b-67be-64fa-df7e-aadda730e283": 0.855,
|
||||||
|
"6c01f552-6cd4-687d-b7f4-81ae67cbde18": 0.133,
|
||||||
|
"406815fc-dd97-48da-904d-c7bd311aba76": 0.283,
|
||||||
|
"d799e656-b36d-ef42-f1ee-cfef82af082f": 0.283,
|
||||||
|
"8688a1b3-9242-c57c-a845-aa3d4119bb5b": 0.320,
|
||||||
|
"5a134b97-60d7-ea74-8913-c52f4c94e4b5": 0.012,
|
||||||
|
"62e20132-3f0d-eb82-ecbd-cf90c04093ce": 0.013,
|
||||||
|
"8b15210a-da5f-4964-b851-d618cff5c573": 0.027,
|
||||||
|
"63c21b52-935a-4e05-f646-9d373cc3a133": 0.028,
|
||||||
|
"20f10e49-027a-c0bb-932a-f1eec8ca113f": 0.042,
|
||||||
|
"34301e2d-bcf5-4dad-b796-c2d3a3cdbe47": 0.043,
|
||||||
|
"12f9fee9-5784-c680-da49-b4ada33f1d9b": 0.017,
|
||||||
|
"70f9800c-a6a3-c9c5-c849-8ebf193ddeea": 0.018,
|
||||||
|
"5e9d3545-69c0-ca03-f18b-c99c45c9791f": 0.029,
|
||||||
|
"17888b77-0366-6ae5-8639-a5eec5fef1b9": 0.030,
|
||||||
|
"a7c106f6-547d-cc81-95a1-b42c61c5816d": 0.044,
|
||||||
|
"421b3879-dd3c-6898-fa54-a720ff473999": 0.045,
|
||||||
|
"4a2b3050-ac7f-4b4a-b31c-c64e7cf2dcfc": 0.034,
|
||||||
|
"4840c833-6512-4fb7-fcf3-d72dbf29dbe2": 0.036,
|
||||||
|
"a7bca2ec-1117-6655-8dc7-8f41f3d50b64": 0.039,
|
||||||
|
"539774b0-a675-48f2-fd0d-8315e0542693": 0.040,
|
||||||
|
"7bdee0ba-8645-cbfc-9445-b95921ebcd62": 0.054,
|
||||||
|
"c0ef4b70-5de1-c3b3-ef3a-f54d831c3784": 0.055,
|
||||||
|
"ef90a622-ca4b-ef21-8ba7-e8121b3ee67a": 0.085,
|
||||||
|
"0a045323-e248-6e15-8d1e-f7444dae67e9": 0.086,
|
||||||
|
"bf2a90a7-8c9d-6c6d-e64c-c2544cbe04a7": 0.059,
|
||||||
|
"6366217f-de24-6582-b579-87bfa760ce82": 0.061,
|
||||||
|
"ed2a66de-e6f2-c271-cd3e-efdbca767414": 0.089,
|
||||||
|
"5102fab9-de80-67ea-ef04-c705196db078": 0.091,
|
||||||
|
"094a37cd-30a4-6d7f-cc50-e4fc847c8f73": 0.149,
|
||||||
|
"21dbb603-b3cf-4b45-9daf-9540266c28d9": 0.151,
|
||||||
|
"de83c846-d1ff-c3db-8b00-b4f9ebb80f65": 0.270,
|
||||||
|
"c28d671a-d97e-eefc-90c3-da266d2feb03": 0.272,
|
||||||
|
"9d7ef1b3-827f-4450-ed67-c33eb23c475f": 0.067,
|
||||||
|
"7e248a9a-1733-e896-e807-c477cd7c238c": 0.071,
|
||||||
|
"8ab362c8-845a-cca8-cc92-cfa4ab63edfa": 0.097,
|
||||||
|
"d61d0546-34a3-c830-d417-9d6585e0330e": 0.101,
|
||||||
|
"c67ffac8-a5b9-cc1b-d7b3-8af7972c877d": 0.157,
|
||||||
|
"34c08664-8df3-68ea-af28-941c23b90f18": 0.162,
|
||||||
|
"e5946d15-cf2b-4c6d-f873-c99429791c62": 0.084,
|
||||||
|
"771a1b42-ee5a-c1c8-cf05-ec1585008c1e": 0.092,
|
||||||
|
"bbef2ee6-d72a-cc18-b20e-ec8b67bbb313": 0.114,
|
||||||
|
"0916e773-ee6e-4e19-eef9-80f481758a32": 0.122,
|
||||||
|
"0ba70a68-2c1f-657d-e7f4-a2930a996dfe": 0.174,
|
||||||
|
"cc8a39a7-3c24-4da7-e899-b3214081ce97": 0.183,
|
||||||
|
"172e9c01-80e6-ecae-de2a-e7228cbfa144": 0.295,
|
||||||
|
"95449608-bf37-cbcc-e2eb-d60f2ac1db35": 0.303,
|
||||||
|
"166bebac-fd73-e468-fe2d-e8b699375053": 0.042,
|
||||||
|
"cf102146-317d-e11b-e16c-f9187cc3b00b": 0.053,
|
||||||
|
"ae407ea8-8461-41e3-a472-f75d2d522416": 0.117,
|
||||||
|
"eccca454-7282-e301-a195-8254ecde71ad": 0.121,
|
||||||
|
"10678275-7ed1-66d1-b3a9-eaf6683e90b9": 0.178,
|
||||||
|
"0835d49c-1457-614e-d313-bf1453dc3cff": 0.182,
|
||||||
|
"653d6003-d480-e1b7-a5a3-ccd543831f39": 0.298,
|
||||||
|
"47e9a13d-b26f-ea6b-d612-c9f027387076": 0.302,
|
||||||
|
"2e4fda7b-f26f-c154-8269-de574d471aba": 0.419,
|
||||||
|
"30c668ce-60b3-e16c-c5aa-8639aebaf204": 0.423,
|
||||||
|
"a52bf2a8-30a5-438c-b12c-86c409f57d8e": 0.134,
|
||||||
|
"95c502a0-f3a6-690a-d879-e818fb5459c3": 0.142,
|
||||||
|
"1a7bde87-21c2-e8d2-c377-909561a5c012": 0.194,
|
||||||
|
"44070f9d-a679-e7a1-a6cc-829b28353331": 0.203,
|
||||||
|
"6045d5d1-b32d-6f99-bfbd-aa9b9b10f75f": 0.315,
|
||||||
|
"e8f0e856-34d1-efca-b4d5-fd77122e779c": 0.323,
|
||||||
|
"23944cb0-9ad9-60aa-f030-b83fee428a0f": 0.436,
|
||||||
|
"e176dbf4-2b1b-e2cf-cd67-faf4294f0c3d": 0.444,
|
||||||
|
"ce0d9d18-4ed9-e6fb-8a0e-ddd9c809a69d": 0.469,
|
||||||
|
"39595bee-160e-6138-b7f3-9b115f68b843": 0.486,
|
||||||
|
"96b20a93-a579-e27b-fbdb-e37f83e7334e": 0.094,
|
||||||
|
"782b2dcc-0d37-cb1f-e84b-ee07558e9af1": 0.188,
|
||||||
|
"f724f364-6e29-4333-d486-9b262317c0a4": 0.192,
|
||||||
|
"299def39-e8cd-67b5-bedd-896e10e6dede": 0.235,
|
||||||
|
"53d7d9d5-b32e-614f-dcf7-9ab961195d48": 0.243,
|
||||||
|
"a36a2eb4-00a2-6a53-8077-83e69d4275f7": 0.355,
|
||||||
|
"f4a527f7-04a0-61af-e076-b118055acaaf": 0.363,
|
||||||
|
"6bf8b9dd-538c-ce57-8acc-9dda062bab3c": 0.476,
|
||||||
|
"d0457e57-85f6-656e-f06f-cb642d149934": 0.484,
|
||||||
|
"910f7fe4-c6f4-c880-fca5-d8c9a879a33f": 0.268,
|
||||||
|
"8a83258f-fbe1-caf7-c66f-f3ee258cb8a0": 0.285,
|
||||||
|
"2c0aeb79-14e4-c554-ff83-9becfbd22549": 0.389,
|
||||||
|
"ba3ffdf0-c362-60f0-96b1-c16a7be36c51": 0.405,
|
||||||
|
"23ed3a18-5664-4349-f927-c9d67fd3c4a5": 0.509,
|
||||||
|
"e06b4d1b-14d3-4d44-fca8-d61449a8830d": 0.526,
|
||||||
|
"ae681b38-58c1-6047-e038-9aefa021f7de": 0.288,
|
||||||
|
"9f2ec40f-2ae6-441f-b1a3-9f3ae518b912": 0.305,
|
||||||
|
"de01c282-b7e2-45a3-c789-87102bf13cde": 0.469,
|
||||||
|
"69f98564-9094-4582-ba4d-ae90ce769325": 0.486,
|
||||||
|
"f35153c9-d979-6a17-f66e-82c3532f76ef": 0.576,
|
||||||
|
"4a3c6572-84ba-4149-d619-e02002a082ae": 0.610,
|
||||||
|
"93602faa-c78c-e104-fb0b-f7e802f4bd97": 0.992,
|
||||||
|
"1efc247e-0213-e7de-97de-daa5767dd3cf": 1.059
|
||||||
|
}
|
@ -2,7 +2,7 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<title>Joyent</title>
|
<title>Spearhead</title>
|
||||||
<base href="/" />
|
<base href="/" />
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
@ -541,3 +541,13 @@ accordion
|
|||||||
{
|
{
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.price
|
||||||
|
{
|
||||||
|
color: #cd5c5c;
|
||||||
|
vertical-align: middle;
|
||||||
|
padding: 0 .5rem;
|
||||||
|
margin-bottom: .25rem;
|
||||||
|
display: inline-block;
|
||||||
|
text-transform: none;
|
||||||
|
}
|
Reference in New Issue
Block a user