refs #1739. Rin script assistant
|
@ -40,6 +40,9 @@ import {EnvVarsComponent} from "./components/admin/env-vars/env-vars.component";
|
||||||
import {MenusComponent} from "./components/menus/menus.component";
|
import {MenusComponent} from "./components/menus/menus.component";
|
||||||
import {OgDhcpSubnetsComponent} from "./components/ogdhcp/og-dhcp-subnets.component";
|
import {OgDhcpSubnetsComponent} from "./components/ogdhcp/og-dhcp-subnets.component";
|
||||||
import {StatusComponent} from "./components/ogdhcp/status/status.component";
|
import {StatusComponent} from "./components/ogdhcp/status/status.component";
|
||||||
|
import {
|
||||||
|
RunScriptAssistantComponent
|
||||||
|
} from "./components/groups/components/client-main-view/run-script-assistant/run-script-assistant.component";
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{ path: '', redirectTo: 'auth/login', pathMatch: 'full' },
|
{ path: '', redirectTo: 'auth/login', pathMatch: 'full' },
|
||||||
{ path: '', component: MainLayoutComponent,
|
{ path: '', component: MainLayoutComponent,
|
||||||
|
@ -63,6 +66,7 @@ const routes: Routes = [
|
||||||
{ path: 'calendars', component: CalendarComponent },
|
{ path: 'calendars', component: CalendarComponent },
|
||||||
{ path: 'clients/deploy-image', component: DeployImageComponent },
|
{ path: 'clients/deploy-image', component: DeployImageComponent },
|
||||||
{ path: 'clients/partition-assistant', component: PartitionAssistantComponent },
|
{ path: 'clients/partition-assistant', component: PartitionAssistantComponent },
|
||||||
|
{ path: 'clients/run-script', component: RunScriptAssistantComponent },
|
||||||
{ path: 'clients/:id', component: ClientMainViewComponent },
|
{ path: 'clients/:id', component: ClientMainViewComponent },
|
||||||
{ path: 'clients/:id/create-image', component: CreateClientImageComponent },
|
{ path: 'clients/:id/create-image', component: CreateClientImageComponent },
|
||||||
{ path: 'repositories', component: RepositoriesComponent },
|
{ path: 'repositories', component: RepositoriesComponent },
|
||||||
|
|
|
@ -137,6 +137,7 @@ import { GlobalStatusComponent } from './components/global-status/global-status.
|
||||||
import { ShowImagesComponent } from './components/repositories/show-images/show-images.component';
|
import { ShowImagesComponent } from './components/repositories/show-images/show-images.component';
|
||||||
import { StatusTabComponent } from './components/global-status/status-tab/status-tab.component';
|
import { StatusTabComponent } from './components/global-status/status-tab/status-tab.component';
|
||||||
import { ConvertImageToVirtualComponent } from './components/repositories/convert-image-to-virtual/convert-image-to-virtual.component';
|
import { ConvertImageToVirtualComponent } from './components/repositories/convert-image-to-virtual/convert-image-to-virtual.component';
|
||||||
|
import { RunScriptAssistantComponent } from './components/groups/components/client-main-view/run-script-assistant/run-script-assistant.component';
|
||||||
|
|
||||||
export function HttpLoaderFactory(http: HttpClient) {
|
export function HttpLoaderFactory(http: HttpClient) {
|
||||||
return new TranslateHttpLoader(http, './locale/', '.json');
|
return new TranslateHttpLoader(http, './locale/', '.json');
|
||||||
|
@ -233,7 +234,8 @@ registerLocaleData(localeEs, 'es-ES');
|
||||||
GlobalStatusComponent,
|
GlobalStatusComponent,
|
||||||
ShowImagesComponent,
|
ShowImagesComponent,
|
||||||
StatusTabComponent,
|
StatusTabComponent,
|
||||||
ConvertImageToVirtualComponent
|
ConvertImageToVirtualComponent,
|
||||||
|
RunScriptAssistantComponent
|
||||||
],
|
],
|
||||||
bootstrap: [AppComponent],
|
bootstrap: [AppComponent],
|
||||||
imports: [BrowserModule,
|
imports: [BrowserModule,
|
||||||
|
|
|
@ -0,0 +1,214 @@
|
||||||
|
|
||||||
|
.divider {
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 20px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deploy-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 20px;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.script-container {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 20px;
|
||||||
|
padding: 20px;
|
||||||
|
background-color: #eaeff6;
|
||||||
|
border-radius: 12px;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.script-content {
|
||||||
|
flex: 2;
|
||||||
|
min-width: 60%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.script-params {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 35%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.script-container {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.script-content, .script-params {
|
||||||
|
min-width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-container {
|
||||||
|
margin-top: 20px;
|
||||||
|
align-items: center;
|
||||||
|
padding: 20px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 16px;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-field {
|
||||||
|
flex: 1 1 calc(33.33% - 16px);
|
||||||
|
min-width: 250px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.script-preview {
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-family: monospace;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
min-height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.full-width {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-string {
|
||||||
|
flex: 2;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-boolean {
|
||||||
|
flex: 1;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px 10px;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-elevation-z8 {
|
||||||
|
box-shadow: 0px 0px 0px rgba(0,0,0,0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.paginator-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: end;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clients-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-item {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-card {
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 6px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
padding: 8px;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.3s, transform 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
transform: scale(1.02);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-details {
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-name {
|
||||||
|
display: block;
|
||||||
|
font-size: 1.2em;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-ip {
|
||||||
|
display: block;
|
||||||
|
font-size: 0.9em;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-container-title {
|
||||||
|
flex-grow: 1;
|
||||||
|
text-align: left;
|
||||||
|
padding-left: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-row {
|
||||||
|
display: flex;
|
||||||
|
padding-right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-card {
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 6px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
padding: 8px;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.3s, transform 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
transform: scale(1.02);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .custom-tooltip {
|
||||||
|
white-space: pre-line !important;
|
||||||
|
max-width: 200px;
|
||||||
|
background: rgba(0, 0, 0, 0.8);
|
||||||
|
color: white;
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected-client {
|
||||||
|
background-color: #a0c2e5 !important;
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-row {
|
||||||
|
display: flex;
|
||||||
|
padding-right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.disabled-client {
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
<app-loading [isLoading]="loading"></app-loading>
|
||||||
|
|
||||||
|
<div class="header-container">
|
||||||
|
<div class="header-container-title">
|
||||||
|
<h2>
|
||||||
|
{{ 'runScript' | translate }}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<div class="button-row">
|
||||||
|
<button class="action-button" [disabled]="!selectedScript" (click)="save()">Ejecutar</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<mat-divider></mat-divider>
|
||||||
|
|
||||||
|
<div class="select-container">
|
||||||
|
<mat-expansion-panel hideToggle>
|
||||||
|
<mat-expansion-panel-header>
|
||||||
|
<mat-panel-title> Clientes </mat-panel-title>
|
||||||
|
<mat-panel-description> Listado de clientes donde se ejectutará el script </mat-panel-description>
|
||||||
|
</mat-expansion-panel-header>
|
||||||
|
|
||||||
|
<div class="clients-grid">
|
||||||
|
<div class="button-row">
|
||||||
|
<button class="action-button" (click)="toggleSelectAll()">
|
||||||
|
{{ allSelected ? 'Desmarcar' : 'Marcar' }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div *ngFor="let client of clientData" class="client-item">
|
||||||
|
<div class="client-card"
|
||||||
|
(click)="client.status === 'og-live' && toggleClientSelection(client)"
|
||||||
|
[ngClass]="{'selected-client': client.selected, 'disabled-client': client.status !== 'og-live'}"
|
||||||
|
[matTooltip]="getPartitionsTooltip(client)"
|
||||||
|
matTooltipPosition="above"
|
||||||
|
matTooltipClass="custom-tooltip">
|
||||||
|
|
||||||
|
<img
|
||||||
|
[src]="'assets/images/computer_' + client.status + '.svg'"
|
||||||
|
alt="Client Icon"
|
||||||
|
class="client-image" />
|
||||||
|
|
||||||
|
<div class="client-details">
|
||||||
|
<span class="client-name">{{ client.name }}</span>
|
||||||
|
<span class="client-ip">{{ client.ip }}</span>
|
||||||
|
<span class="client-ip">{{ client.mac }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-expansion-panel>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-divider style="margin-top: 20px;"></mat-divider>
|
||||||
|
|
||||||
|
<div class="select-container">
|
||||||
|
<div class="deploy-container">
|
||||||
|
<mat-form-field appearance="fill" class="full-width">
|
||||||
|
<mat-label>Seleccione script a ejecutar</mat-label>
|
||||||
|
<mat-select [(ngModel)]="selectedScript" (selectionChange)="onScriptChange()">
|
||||||
|
<mat-option *ngFor="let script of scripts" [value]="script">{{ script.name }}</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="selectedScript" class="script-container">
|
||||||
|
<div *ngIf="selectedScript" class="script-content">
|
||||||
|
<h3> Script:</h3>
|
||||||
|
<div class="script-preview" [innerHTML]="scriptContent"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="script-params" *ngIf="parameters.length > 0">
|
||||||
|
<h3>Ingrese los valores de los parámetros:</h3>
|
||||||
|
<div *ngFor="let param of parameters; let i = index">
|
||||||
|
<mat-form-field appearance="fill" class="full-width">
|
||||||
|
<mat-label>Parámetro {{ i + 1 }}</mat-label>
|
||||||
|
<input matInput [(ngModel)]="parameters[i]" (input)="updateScript()" placeholder="Ingrese el valor">
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { RunScriptAssistantComponent } from './run-script-assistant.component';
|
||||||
|
import {DeployImageComponent} from "../deploy-image/deploy-image.component";
|
||||||
|
import {LoadingComponent} from "../../../../../shared/loading/loading.component";
|
||||||
|
import {FormBuilder, FormsModule, ReactiveFormsModule} from "@angular/forms";
|
||||||
|
import {MAT_DIALOG_DATA, MatDialogModule, MatDialogRef} from "@angular/material/dialog";
|
||||||
|
import {MatFormFieldModule} from "@angular/material/form-field";
|
||||||
|
import {MatInputModule} from "@angular/material/input";
|
||||||
|
import {MatCheckboxModule} from "@angular/material/checkbox";
|
||||||
|
import {MatExpansionModule} from "@angular/material/expansion";
|
||||||
|
import {MatButtonModule} from "@angular/material/button";
|
||||||
|
import {MatTableModule} from "@angular/material/table";
|
||||||
|
import {MatDividerModule} from "@angular/material/divider";
|
||||||
|
import {MatRadioModule} from "@angular/material/radio";
|
||||||
|
import {MatSelectModule} from "@angular/material/select";
|
||||||
|
import {BrowserAnimationsModule} from "@angular/platform-browser/animations";
|
||||||
|
import {ToastrModule, ToastrService} from "ngx-toastr";
|
||||||
|
import {TranslateModule} from "@ngx-translate/core";
|
||||||
|
import {provideHttpClient} from "@angular/common/http";
|
||||||
|
import {provideHttpClientTesting} from "@angular/common/http/testing";
|
||||||
|
import {provideRouter} from "@angular/router";
|
||||||
|
import {ConfigService} from "@services/config.service";
|
||||||
|
|
||||||
|
describe('RunScriptAssistantComponent', () => {
|
||||||
|
let component: RunScriptAssistantComponent;
|
||||||
|
let fixture: ComponentFixture<RunScriptAssistantComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const mockConfigService = {
|
||||||
|
apiUrl: 'http://mock-api-url',
|
||||||
|
mercureUrl: 'http://mock-mercure-url'
|
||||||
|
};
|
||||||
|
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [DeployImageComponent, LoadingComponent],
|
||||||
|
imports: [
|
||||||
|
ReactiveFormsModule,
|
||||||
|
FormsModule,
|
||||||
|
MatDialogModule,
|
||||||
|
MatFormFieldModule,
|
||||||
|
MatInputModule,
|
||||||
|
MatCheckboxModule,
|
||||||
|
MatExpansionModule,
|
||||||
|
MatButtonModule,
|
||||||
|
MatTableModule,
|
||||||
|
MatDividerModule,
|
||||||
|
MatRadioModule,
|
||||||
|
MatSelectModule,
|
||||||
|
BrowserAnimationsModule,
|
||||||
|
ToastrModule.forRoot(),
|
||||||
|
TranslateModule.forRoot()
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
FormBuilder,
|
||||||
|
ToastrService,
|
||||||
|
provideHttpClient(),
|
||||||
|
provideHttpClientTesting(),
|
||||||
|
provideRouter([]),
|
||||||
|
{
|
||||||
|
provide: MatDialogRef,
|
||||||
|
useValue: {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: MAT_DIALOG_DATA,
|
||||||
|
useValue: {}
|
||||||
|
},
|
||||||
|
{ provide: ConfigService, useValue: mockConfigService }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(RunScriptAssistantComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,135 @@
|
||||||
|
import {Component, EventEmitter, Output} from '@angular/core';
|
||||||
|
import {SelectionModel} from "@angular/cdk/collections";
|
||||||
|
import {HttpClient} from "@angular/common/http";
|
||||||
|
import {ToastrService} from "ngx-toastr";
|
||||||
|
import {ConfigService} from "@services/config.service";
|
||||||
|
import {ActivatedRoute, Router} from "@angular/router";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-run-script-assistant',
|
||||||
|
templateUrl: './run-script-assistant.component.html',
|
||||||
|
styleUrl: './run-script-assistant.component.css'
|
||||||
|
})
|
||||||
|
export class RunScriptAssistantComponent {
|
||||||
|
baseUrl: string;
|
||||||
|
@Output() dataChange = new EventEmitter<any>();
|
||||||
|
|
||||||
|
errorMessage = '';
|
||||||
|
clientId: string | null = null;
|
||||||
|
name: string = '';
|
||||||
|
client: any = null;
|
||||||
|
clientData: any = [];
|
||||||
|
loading: boolean = false;
|
||||||
|
scripts: any[] = [];
|
||||||
|
scriptContent: string = "";
|
||||||
|
parameters: string[] = [];
|
||||||
|
selectedScript: any = null;
|
||||||
|
selectedClients: any[] = [];
|
||||||
|
allSelected: boolean = true;
|
||||||
|
selection = new SelectionModel(true, []);
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private http: HttpClient,
|
||||||
|
private toastService: ToastrService,
|
||||||
|
private configService: ConfigService,
|
||||||
|
private router: Router,
|
||||||
|
private route: ActivatedRoute
|
||||||
|
) {
|
||||||
|
this.baseUrl = this.configService.apiUrl;
|
||||||
|
this.route.queryParams.subscribe(params => {
|
||||||
|
if (params['clientData']) {
|
||||||
|
this.clientData = JSON.parse(params['clientData']);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.clientId = this.clientData?.length ? this.clientData[0]['@id'] : null;
|
||||||
|
this.clientData.forEach((client: { selected: boolean; status: string}) => {
|
||||||
|
if (client.status === 'og-live') {
|
||||||
|
client.selected = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.selectedClients = this.clientData.filter(
|
||||||
|
(client: { status: string }) => client.status === 'og-live'
|
||||||
|
);
|
||||||
|
this.loadScripts()
|
||||||
|
}
|
||||||
|
|
||||||
|
loadScripts(): void {
|
||||||
|
this.loading = true;
|
||||||
|
|
||||||
|
this.http.get(`${this.baseUrl}/commands?readOnly=false&enabled=true`).subscribe((data: any) => {
|
||||||
|
this.scripts = data['hydra:member'];
|
||||||
|
this.loading = false;
|
||||||
|
}, (error) => {
|
||||||
|
this.toastService.error(error.error['hydra:description']);
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleClientSelection(client: any) {
|
||||||
|
client.selected = !client.selected;
|
||||||
|
this.updateSelectedClients();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSelectedClients() {
|
||||||
|
this.selectedClients = this.clientData.filter(
|
||||||
|
(client: { selected: boolean; status: string }) => client.selected && client.status === "og-live"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleSelectAll() {
|
||||||
|
this.allSelected = !this.allSelected;
|
||||||
|
this.clientData.forEach((client: { selected: boolean; status: string }) => {
|
||||||
|
if (client.status === "og-live") {
|
||||||
|
client.selected = this.allSelected;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getPartitionsTooltip(client: any): string {
|
||||||
|
if (!client.partitions || client.partitions.length === 0) {
|
||||||
|
return 'No hay particiones disponibles';
|
||||||
|
}
|
||||||
|
|
||||||
|
return client.partitions
|
||||||
|
.map((p: { partitionNumber: any; size: any; filesystem: any }) => `#${p.partitionNumber} ${p.filesystem} - ${p.size / 1024 }GB`)
|
||||||
|
.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
onScriptChange() {
|
||||||
|
if (this.selectedScript) {
|
||||||
|
this.scriptContent = this.selectedScript.script;
|
||||||
|
|
||||||
|
const matches = this.scriptContent.match(/@\d+/g) || [];
|
||||||
|
this.parameters = new Array(matches.length).fill("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateScript() {
|
||||||
|
let updatedScript = this.selectedScript.script;
|
||||||
|
|
||||||
|
this.parameters.forEach((value, index) => {
|
||||||
|
updatedScript = updatedScript.replace(new RegExp(`@${index + 1}`, "g"), value || `@${index + 1}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.scriptContent = updatedScript;
|
||||||
|
}
|
||||||
|
|
||||||
|
save(): void {
|
||||||
|
this.loading = true;
|
||||||
|
|
||||||
|
this.http.post(`${this.baseUrl}/commands/run-script`, {
|
||||||
|
clients: this.selectedClients.map((client: any) => client.uuid),
|
||||||
|
script: this.scriptContent
|
||||||
|
}).subscribe(
|
||||||
|
response => {
|
||||||
|
this.toastService.success('Script ejecutado correctamente');
|
||||||
|
this.dataChange.emit();
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
this.toastService.error('Error al ejecutar el script');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="32px" viewBox="0 -960 960 960" width="32px" fill="#666666"><path d="M40-120v-80h880v80H40Zm120-120q-33 0-56.5-23.5T80-320v-440q0-33 23.5-56.5T160-840h640q33 0 56.5 23.5T880-760v440q0 33-23.5 56.5T800-240H160Zm0-80h640v-440H160v440Zm0 0v-440 440Z"/>
|
||||||
|
<path fill="#EA3323" d="M160-760h640v440H160Z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 355 B |
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="32px" viewBox="0 -960 960 960" width="32px" fill="#666666"><path d="M40-120v-80h880v80H40Zm120-120q-33 0-56.5-23.5T80-320v-440q0-33 23.5-56.5T160-840h640q33 0 56.5 23.5T880-760v440q0 33-23.5 56.5T800-240H160Zm0-80h640v-440H160v440Zm0 0v-440 440Z"/>
|
||||||
|
<path fill="#75FBFD" d="M160-760h640v440H160Z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 355 B |
|
@ -0,0 +1,5 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="32px" viewBox="0 -960 960 960" width="32px">
|
||||||
|
<path fill="#666666" d="M40-120v-80h880v80H40Zm120-120q-33 0-56.5-23.5T80-320v-440q0-33 23.5-56.5T160-840h640q33 0 56.5 23.5T880-760v440q0 33-23.5 56.5T800-240H160Zm0-80h640v-440H160v440Zm0 0v-440 440Z"/>
|
||||||
|
<path fill="#EA33F7" d="M160-760h640v440H160Z"/>
|
||||||
|
<rect x="250" y="-670" width="460" height="220" fill="#ffffff" stroke="#000000" stroke-width="4" rx="10" ry="10"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 475 B |
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="32px" viewBox="0 -960 960 960" width="32px" fill="#666666"><path d="M40-120v-80h880v80H40Zm120-120q-33 0-56.5-23.5T80-320v-440q0-33 23.5-56.5T160-840h640q33 0 56.5 23.5T880-760v440q0 33-23.5 56.5T800-240H160Zm0-80h640v-440H160v440Zm0 0v-440 440Z"/>
|
||||||
|
<path fill="#EA33F7" d="M160-760h640v440H160Z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 355 B |
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="32px" viewBox="0 -960 960 960" width="32px" fill="#666666"><path d="M40-120v-80h880v80H40Zm120-120q-33 0-56.5-23.5T80-320v-440q0-33 23.5-56.5T160-840h640q33 0 56.5 23.5T880-760v440q0 33-23.5 56.5T800-240H160Zm0-80h640v-440H160v440Zm0 0v-440 440Z"/>
|
||||||
|
<path fill="#F19E39" d="M160-760h640v440H160Z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 355 B |
|
@ -0,0 +1,2 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="32px" viewBox="0 -960 960 960" width="32px" fill="#666666"><path d="M40-120v-80h880v80H40Zm120-120q-33 0-56.5-23.5T80-320v-440q0-33 23.5-56.5T160-840h640q33 0 56.5 23.5T880-760v440q0 33-23.5 56.5T800-240H160Zm0-80h640v-440H160v440Zm0 0v-440 440Z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 304 B |
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="32px" viewBox="0 -960 960 960" width="32px" fill="#666666"><path d="M40-120v-80h880v80H40Zm120-120q-33 0-56.5-23.5T80-320v-440q0-33 23.5-56.5T160-840h640q33 0 56.5 23.5T880-760v440q0 33-23.5 56.5T800-240H160Zm0-80h640v-440H160v440Zm0 0v-440 440Z"/>
|
||||||
|
<path fill="#FFFF55" d="M160-760h640v440H160Z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 355 B |
|
@ -0,0 +1,2 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="32px" viewBox="0 -960 960 960" width="32px" fill="#666666"><path d="M40-120v-80h880v80H40Zm120-120q-33 0-56.5-23.5T80-320v-440q0-33 23.5-56.5T160-840h640q33 0 56.5 23.5T880-760v440q0 33-23.5 56.5T800-240H160Zm0-80h640v-440H160v440Zm0 0v-440 440Z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 304 B |
|
@ -0,0 +1,5 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="32px" viewBox="0 -960 960 960" width="32px">
|
||||||
|
<path fill="#666666" d="M40-120v-80h880v80H40Zm120-120q-33 0-56.5-23.5T80-320v-440q0-33 23.5-56.5T160-840h640q33 0 56.5 23.5T880-760v440q0 33-23.5 56.5T800-240H160Zm0-80h640v-440H160v440Zm0 0v-440 440Z"/>
|
||||||
|
<path fill="#0000F5" d="M160-760h640v440H160Z"/>
|
||||||
|
<rect x="250" y="-670" width="460" height="220" fill="#ffffff" stroke="#000000" stroke-width="4" rx="10" ry="10"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 475 B |
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="32px" viewBox="0 -960 960 960" width="32px" fill="#666666"><path d="M40-120v-80h880v80H40Zm120-120q-33 0-56.5-23.5T80-320v-440q0-33 23.5-56.5T160-840h640q33 0 56.5 23.5T880-760v440q0 33-23.5 56.5T800-240H160Zm0-80h640v-440H160v440Zm0 0v-440 440Z"/>
|
||||||
|
<path fill="#0000F5" d="M160-760h640v440H160Z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 355 B |
After Width: | Height: | Size: 1.1 MiB |