Skip to content

Commit

Permalink
NAS-131978 / 25.04 / Instances table and layout (#10971)
Browse files Browse the repository at this point in the history
* NAS-131978: Instances table and layout

* NAS-131978: Instances table and layout

* NAS-131978: Instances table and layout

* NAS-131978: Update tests
  • Loading branch information
denysbutenko authored Nov 6, 2024
1 parent 8b74fc7 commit c636124
Show file tree
Hide file tree
Showing 111 changed files with 1,401 additions and 128 deletions.
10 changes: 10 additions & 0 deletions src/app/enums/virtualization.enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,21 @@ export enum VirtualizationType {
Vm = 'VM',
}

export const virtualizationTypeMap = new Map<VirtualizationType, string>([
[VirtualizationType.Container, T('Container')],
[VirtualizationType.Vm, T('VM')],
]);

export enum VirtualizationStatus {
Running = 'RUNNING',
Stopped = 'STOPPED',
}

export const virtualizationStatusMap = new Map<VirtualizationStatus, string>([
[VirtualizationStatus.Running, T('Running')],
[VirtualizationStatus.Stopped, T('Stopped')],
]);

export enum VirtualizationRemote {
LinuxContainers = 'LINUX_CONTAINERS',
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Group } from 'app/interfaces/group.interface';
import { Pool } from 'app/interfaces/pool.interface';
import { User } from 'app/interfaces/user.interface';
import { VirtualMachine } from 'app/interfaces/virtual-machine.interface';
import { VirtualizationInstance } from 'app/interfaces/virtualization.interface';

/**
* Directory of compatible API call and subscribe methods.
Expand All @@ -15,6 +16,7 @@ export interface ApiCallAndSubscribeEventDirectory {
'group.query': { response: Group };
'app.image.query': { response: ContainerImage };
'app.query': { response: App };
'virt.instance.query': { response: VirtualizationInstance };
}

export type ApiCallAndSubscribeMethod = keyof ApiCallAndSubscribeEventDirectory;
Expand Down
2 changes: 1 addition & 1 deletion src/app/interfaces/api/api-job-directory.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ export interface ApiJobDirectory {
'virt.instance.delete ': { params: [instanceId: string]; response: boolean };
'virt.instance.restart': { params: VirtualizationStopParams; response: boolean };
'virt.instance.start': { params: [instanceId: string]; response: boolean };
'virt.instance.stop': { params: [instanceId: string]; response: boolean };
'virt.instance.stop': { params: VirtualizationStopParams; response: boolean };
'virt.instance.update': {
params: [instanceId: string, update: UpdateVirtualizationInstance];
response: VirtualizationInstance;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,18 @@
<ix-all-instances-header></ix-all-instances-header>
</ix-page-header>

<div class="demo-layout">
<div class="demo-table"></div>
<div class="container">
<div class="table-container">
<ix-instance-list></ix-instance-list>
</div>

<ix-instance-details [instance]="demoInstance"></ix-instance-details>
<div
ixDetailsHeight="rightside-content-hold"
class="details-container"
[class.details-container-mobile]="showMobileDetails()"
>
@if (selectedInstance()) {
<ix-instance-details [instance]="selectedInstance()"></ix-instance-details>
}
</div>
</div>
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
.demo-layout {
display: grid;
grid-template-columns: 1fr 1fr;
@import 'scss-imports/variables';
@import 'mixins/layout';

.demo-table {
height: 100%;
@include tree-node-with-details-container;

:host {
box-sizing: border-box;
display: flex;
flex-direction: column;
height: 100%;

.table-container {
background: var(--bg2);
}
}
Original file line number Diff line number Diff line change
@@ -1,27 +1,38 @@
import { createComponentFactory, mockProvider, Spectator } from '@ngneat/spectator/jest';
import { MockComponents } from 'ng-mocks';
import { MockComponents, MockDirective } from 'ng-mocks';
import { mockAuth } from 'app/core/testing/utils/mock-auth.utils';
import { DetailsHeightDirective } from 'app/directives/details-height/details-height.directive';
import {
AllInstancesHeaderComponent,
} from 'app/pages/virtualization/components/all-instances/all-instances-header/all-instances-header.component';
import { AllInstancesComponent } from 'app/pages/virtualization/components/all-instances/all-instances.component';
import {
InstanceDetailsComponent,
} from 'app/pages/virtualization/components/all-instances/instance-details/instance-details.component';
import { InstanceListComponent } from 'app/pages/virtualization/components/all-instances/instance-list/instance-list.component';
import { VirtualizationConfigStore } from 'app/pages/virtualization/stores/virtualization-config.store';
import { VirtualizationInstancesStore } from 'app/pages/virtualization/stores/virtualization-instances.store';

describe('AllInstancesComponent', () => {
let spectator: Spectator<AllInstancesComponent>;
const createComponent = createComponentFactory({
component: AllInstancesComponent,
providers: [
mockAuth(),
mockProvider(VirtualizationConfigStore, {
initialize: jest.fn(),
}),
mockProvider(VirtualizationInstancesStore, {
initialize: jest.fn(),
selectedInstance: jest.fn(),
}),
],
declarations: [
MockDirective(DetailsHeightDirective),
MockComponents(
AllInstancesHeaderComponent,
InstanceDetailsComponent,
InstanceListComponent,
),
],
});
Expand All @@ -32,5 +43,6 @@ describe('AllInstancesComponent', () => {

it('initializes config store on init', () => {
expect(spectator.inject(VirtualizationConfigStore).initialize).toHaveBeenCalled();
expect(spectator.inject(VirtualizationInstancesStore).initialize).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import {
ChangeDetectionStrategy, Component, OnInit, signal,
} from '@angular/core';
import { TranslateModule } from '@ngx-translate/core';
import { VirtualizationInstance } from 'app/interfaces/virtualization.interface';
import { DetailsHeightDirective } from 'app/directives/details-height/details-height.directive';
import { PageHeaderComponent } from 'app/modules/page-header/page-title-header/page-header.component';
import {
AllInstancesHeaderComponent,
} from 'app/pages/virtualization/components/all-instances/all-instances-header/all-instances-header.component';
import {
InstanceDetailsComponent,
} from 'app/pages/virtualization/components/all-instances/instance-details/instance-details.component';
import { InstanceListComponent } from 'app/pages/virtualization/components/all-instances/instance-list/instance-list.component';
import { VirtualizationConfigStore } from 'app/pages/virtualization/stores/virtualization-config.store';
import { VirtualizationInstancesStore } from 'app/pages/virtualization/stores/virtualization-instances.store';

@Component({
selector: 'ix-instance-list',
selector: 'ix-all-instances',
templateUrl: './all-instances.component.html',
styleUrls: ['./all-instances.component.scss'],
standalone: true,
Expand All @@ -21,30 +25,21 @@ import { VirtualizationConfigStore } from 'app/pages/virtualization/stores/virtu
TranslateModule,
AllInstancesHeaderComponent,
InstanceDetailsComponent,
InstanceListComponent,
DetailsHeightDirective,
],
})
export class AllInstancesComponent implements OnInit {
readonly demoInstance = {
id: 'demo',
name: 'Demo',
type: 'CONTAINER',
status: 'RUNNING',
cpu: '525',
autostart: true,
image: {
architecture: 'amd64',
description: 'Almalinux 8 amd64 (20241030_23:38)',
os: 'Almalinux',
release: '8',
},
memory: 131072000,
} as unknown as VirtualizationInstance;
readonly selectedInstance = this.instancesStore.selectedInstance;
readonly showMobileDetails = signal(false);

constructor(
private configStore: VirtualizationConfigStore,
private instancesStore: VirtualizationInstancesStore,
) {}

ngOnInit(): void {
this.configStore.initialize();
this.instancesStore.initialize();
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div class="cards">
<div class="scroll-window">
<ix-instance-devices></ix-instance-devices>
<ix-instance-devices [instance]="instance()"></ix-instance-devices>
<ix-instance-general-info [instance]="instance()"></ix-instance-general-info>
</div>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,47 @@
@import 'scss-imports/variables';

:host {

display: block;
width: 100%;
width: 100%;

.header {
color: var(--fg1);

@media (max-width: calc($breakpoint-hidden - 1px)) {
border-bottom: solid 1px var(--lines);
margin: 0 16px 16px 0;
}
}

.title {
margin-bottom: 12px;
margin-top: 20px;
min-height: 36px;

@media (max-width: calc($breakpoint-hidden - 1px)) {
margin-top: 0;
}

.mobile-prefix {
display: none;

@media (max-width: $breakpoint-hidden) {
align-items: center;
display: flex;
}
}

.prefix {
display: flex;

@media (max-width: $breakpoint-hidden) {
display: none;
}
}
}

&::ng-deep {
.cards .card {
@include details-card();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import { ChangeDetectionStrategy, Component, input } from '@angular/core';
import {
ChangeDetectionStrategy, Component, inject, input,
} from '@angular/core';
import { TranslateModule } from '@ngx-translate/core';
import { VirtualizationInstance } from 'app/interfaces/virtualization.interface';
import { MobileBackButtonComponent } from 'app/modules/buttons/mobile-back-button/mobile-back-button.component';
import {
InstanceDevicesComponent,
} from 'app/pages/virtualization/components/all-instances/instance-details/instance-devices/instance-devices.component';
import {
InstanceGeneralInfoComponent,
} from 'app/pages/virtualization/components/all-instances/instance-details/instance-general-info/instance-general-info.component';
import { VirtualizationInstancesStore } from 'app/pages/virtualization/stores/virtualization-instances.store';

@Component({
selector: 'ix-instance-details',
Expand All @@ -14,10 +19,16 @@ import {
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
TranslateModule,
InstanceDevicesComponent,
InstanceGeneralInfoComponent,
MobileBackButtonComponent,
],
})
export class InstanceDetailsComponent {
instance = input.required<VirtualizationInstance>();

onCloseMobileDetails(): void {
inject(VirtualizationInstancesStore).selectInstance(null);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
@import 'mixins/cards';
@import 'scss-imports/variables';

mat-card-content p {
margin: 0 0 6px;
:host {
.card {
@include details-card();
margin: 0;

mat-card-header {
button {
margin-left: 8px;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { ChangeDetectionStrategy, Component, input } from '@angular/core';
import { MatButton } from '@angular/material/button';
import {
MatCard, MatCardContent, MatCardHeader, MatCardTitle,
} from '@angular/material/card';
import { MatCardModule } from '@angular/material/card';
import { TranslateModule } from '@ngx-translate/core';
import { VirtualizationInstance } from 'app/interfaces/virtualization.interface';

@Component({
selector: 'ix-instance-devices',
Expand All @@ -13,12 +12,10 @@ import { TranslateModule } from '@ngx-translate/core';
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
MatButton,
MatCard,
MatCardHeader,
MatCardTitle,
MatCardModule,
TranslateModule,
MatCardContent,
],
})
export class InstanceDevicesComponent {
instance = input.required<VirtualizationInstance>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<div class="container">
<div class="table-container">
<div class="item-search">
@if (!showMobileDetails()) {
<ix-fake-progress-bar
class="loader-bar"
[loading]="isLoading()"
></ix-fake-progress-bar>
}

<ix-search-input1
[maxLength]="100"
[disabled]="!instances().length"
[value]="searchQuery()"
(search)="onSearch($event)"
></ix-search-input1>
</div>

<div class="instances">
@if (filteredInstances()?.length) {
<div class="instances-header-row">
<div class="cell checkbox">
<mat-checkbox
color="primary"
ixTest="select-all-app"
[checked]="isAllSelected()"
[indeterminate]="!isAllSelected() && !!selection.selected.length"
(change)="toggleAllChecked($event.checked)"
></mat-checkbox>
</div>
<div class="cell">{{ 'Name' | translate }}</div>
<div class="cell">{{ 'Type' | translate }}</div>
<div class="cell">{{ 'Status' | translate }}</div>
<div class="cell actions">{{ 'Controls' | translate }}</div>
</div>
} @else {
<ix-empty [conf]="emptyConfig()"></ix-empty>
}

@for (instance of filteredInstances(); track instance.id) {
<ix-instance-row
[instance]="instance"
[class.selected]="selectedInstance()?.id === instance.id"
[selected]="selection.isSelected(instance.id)"
(click)="viewDetails(instance)"
(keydown.enter)="viewDetails(instance)"
(onStart)="start(instance.id)"
(onStop)="stop(instance.id)"
(onRestart)="restart(instance.id)"
></ix-instance-row>
}
</div>
</div>
</div>
Loading

0 comments on commit c636124

Please sign in to comment.