import {Component, EventEmitter, Inject, OnInit, 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"; import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from "@angular/material/dialog"; import { SaveScriptComponent } from "../../../groups/components/client-main-view/run-script-assistant/save-script/save-script.component"; import {FormBuilder, FormGroup} from "@angular/forms"; import {TranslateService} from "@ngx-translate/core"; interface ActionCategory { id: string; icon: string; labelKey: string; descriptionKey: string; color: string; infoOnly: boolean; actions: ActionOption[]; } interface ActionOption { type: string; icon: string; labelKey: string; } @Component({ selector: 'app-create-task-script', templateUrl: './create-task-script.component.html', styleUrl: './create-task-script.component.css' }) export class CreateTaskScriptComponent implements OnInit { form: FormGroup; baseUrl: string; @Output() dataChange = new EventEmitter(); loading: boolean = false; scripts: any[] = []; scriptContent: string = ""; parameters: any = {}; commandType: string = 'existing'; selectedScript: any = null; newScript: string = ''; executionOrder: number = 0; selection = new SelectionModel(true, []); parameterNames: string[] = Object.keys(this.parameters); commandTask: any; selectedCategory: string = ''; selectedAction: string = ''; categories: ActionCategory[] = [ { id: 'image-ops', icon: 'perm_media', labelKey: 'catImageOps', descriptionKey: 'catImageOpsDesc', color: '#1565c0', infoOnly: true, actions: [ { type: 'deploy-image', icon: 'cloud_download', labelKey: 'deployImage' }, { type: 'create-image', icon: 'add_photo_alternate', labelKey: 'createImage' }, ] }, { id: 'partition', icon: 'storage', labelKey: 'catPartition', descriptionKey: 'catPartitionDesc', color: '#2e7d32', infoOnly: true, actions: [ { type: 'partition-assistant', icon: 'view_column', labelKey: 'partitionAndFormat' }, ] }, { id: 'system', icon: 'settings_power', labelKey: 'catSystemControl', descriptionKey: 'catSystemControlDesc', color: '#e65100', infoOnly: false, actions: [ { type: 'power-on', icon: 'power', labelKey: 'powerOnDevice' }, { type: 'shutdown', icon: 'power_off', labelKey: 'shutdownDevice' }, { type: 'reboot', icon: 'restart_alt', labelKey: 'rebootDevice' }, { type: 'login', icon: 'login', labelKey: 'loginSession' }, ] }, { id: 'script', icon: 'code', labelKey: 'catCustomScript', descriptionKey: 'catCustomScriptDesc', color: '#6a1b9a', infoOnly: false, actions: [ { type: 'run-script', icon: 'terminal', labelKey: 'runScript' }, ] } ]; constructor( private fb: FormBuilder, private http: HttpClient, public dialogRef: MatDialogRef, private toastService: ToastrService, private configService: ConfigService, private router: Router, private dialog: MatDialog, private route: ActivatedRoute, private translate: TranslateService, @Inject(MAT_DIALOG_DATA) public data: any ) { this.baseUrl = this.configService.apiUrl; this.commandTask = this.data.commandTask; this.loadScripts(); this.form = this.fb.group({ content: [''], order: [''], }); } ngOnInit(): void {} selectCategory(categoryId: string): void { const cat = this.categories.find(c => c.id === categoryId); if (cat?.infoOnly) { this.selectedCategory = categoryId; this.selectedAction = ''; return; } this.selectedCategory = categoryId; if (cat && cat.actions.length === 1) { this.selectAction(cat.actions[0].type); } else { this.selectedAction = ''; } } selectAction(actionType: string): void { this.selectedAction = actionType; } goBackToCategories(): void { this.selectedCategory = ''; this.selectedAction = ''; } goBackToActions(): void { this.selectedAction = ''; } isInfoOnlyCategory(): boolean { const cat = this.categories.find(c => c.id === this.selectedCategory); return cat?.infoOnly || false; } 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; }); } onScriptChange(): void { if (this.selectedScript) { this.scriptContent = this.selectedScript.script; const matches = this.scriptContent.match(/@(\w+)/g) || []; const uniqueParams = Array.from(new Set(matches.map((m: string) => m.slice(1)))); this.parameters = {}; uniqueParams.forEach(param => this.parameters[param] = ''); this.parameterNames = uniqueParams; this.updateScript(); } } onParamChange(name: string, value: string): void { this.parameters[name] = value; this.updateScript(); } updateScript(): void { let updatedScript = this.selectedScript.script; for (const [key, value] of Object.entries(this.parameters)) { const regex = new RegExp(`@${key}\\b`, 'g'); updatedScript = updatedScript.replace(regex, value || `@${key}`); } this.scriptContent = updatedScript; } saveNewScript(): void { if (!this.newScript.trim()) { this.toastService.error(this.translate.instant('youMustEnterAScriptBefore')); return; } const dialogRef = this.dialog.open(SaveScriptComponent, { width: '400px', data: this.newScript }); dialogRef.afterClosed().subscribe(result => { if (result) { this.toastService.success(this.translate.instant('scriptSavedSuccessfully')); } }); } onSubmit(): void { if (!this.selectedAction) { this.toastService.error(this.translate.instant('selectActionTypeError')); return; } if (this.executionOrder === null || this.executionOrder === undefined) { this.toastService.error(this.translate.instant('executionOrderRequired')); return; } let type = ''; let content = ''; switch (this.selectedAction) { case 'run-script': if (!this.validateScript()) return; type = 'run-script'; content = this.commandType === 'existing' ? this.scriptContent : this.newScript; break; case 'power-on': case 'shutdown': case 'reboot': case 'login': type = this.selectedAction; content = this.selectedAction; break; default: this.toastService.error(this.translate.instant('unrecognizedActionType')); return; } const payload: any = { commandTask: this.commandTask['@id'], content: content, order: this.executionOrder, type: type, }; this.http.post(`${this.baseUrl}/command-task-scripts`, payload).subscribe({ next: () => { this.toastService.success(this.translate.instant('actionAddedSuccess')); this.dialogRef.close(true); }, error: (error) => { this.toastService.error(error.error['hydra:description']); } }); } private validateScript(): boolean { if (this.commandType === 'new') { if (!this.newScript.trim()) { this.toastService.error(this.translate.instant('youMustEnterAScript')); return false; } } else if (this.commandType === 'existing') { if (!this.selectedScript) { this.toastService.error(this.translate.instant('youMustSelectAScript')); return false; } } return true; } onCancel(): void { this.dialogRef.close(false); } getSelectedCategoryColor(): string { const cat = this.categories.find(c => c.id === this.selectedCategory); return cat?.color || '#1976d2'; } getSelectedActionLabel(): string { for (const cat of this.categories) { const action = cat.actions.find(a => a.type === this.selectedAction); if (action) return this.translate.instant(action.labelKey); } return ''; } getSelectedActionIcon(): string { for (const cat of this.categories) { const action = cat.actions.find(a => a.type === this.selectedAction); if (action) return action.icon; } return ''; } }