Solve conflics
commit
53519442ff
10
CHANGELOG.md
10
CHANGELOG.md
|
|
@ -1,4 +1,14 @@
|
|||
# Changelog
|
||||
|
||||
## [1.3.3] - 2026-04-13
|
||||
### Improved
|
||||
- Mejorada la visualizacion del selector de particiones mostrando el contenido de cache y la imagen asociada a la partición.
|
||||
- Mejorada usabilidad de la funcion updateCache para que no haga falta elegir disco y particion para lanzar el comando.
|
||||
|
||||
## [1.3.2] - 2026-04-13
|
||||
### Added
|
||||
- Añadida opción para restaurar sólo cache o cache y particion.
|
||||
|
||||
## [1.3.1] - 2026-04-09
|
||||
### Added
|
||||
- Añadida información de la fecha de despliegue de una imagen en una particion.
|
||||
|
|
|
|||
|
|
@ -112,7 +112,17 @@
|
|||
}
|
||||
|
||||
.image-config-section {
|
||||
flex: 0 0 320px;
|
||||
flex: 0 0 340px;
|
||||
}
|
||||
|
||||
.image-config-section .filters-row {
|
||||
flex-direction: column;
|
||||
gap: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.image-config-section .filters-row mat-form-field {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.partition-select-section {
|
||||
|
|
@ -746,11 +756,83 @@ table {
|
|||
font-size: 13px;
|
||||
}
|
||||
|
||||
.section-hint mat-icon {
|
||||
color: #667eea;
|
||||
font-size: 18px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
/* Deploy mode radio group */
|
||||
.form-section-row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 20px;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 20px;
|
||||
padding: 16px 20px;
|
||||
background: #f7faff;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #e2e8ff;
|
||||
}
|
||||
|
||||
.deploy-mode-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #2c3e50;
|
||||
min-width: 140px;
|
||||
padding-top: 12px;
|
||||
}
|
||||
|
||||
.deploy-mode-label mat-icon {
|
||||
color: #667eea;
|
||||
}
|
||||
|
||||
.deploy-mode-radio-group {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.deploy-mode-radio {
|
||||
border: 2px solid #e2e8ff;
|
||||
border-radius: 10px;
|
||||
padding: 10px 16px;
|
||||
background: white;
|
||||
transition: border-color 0.2s, box-shadow 0.2s;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.deploy-mode-radio:has(.mat-radio-button-checked),
|
||||
.deploy-mode-radio:hover {
|
||||
border-color: #667eea;
|
||||
box-shadow: 0 2px 8px rgba(102, 126, 234, 0.15);
|
||||
}
|
||||
|
||||
.deploy-mode-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.deploy-mode-option mat-icon {
|
||||
color: #667eea;
|
||||
font-size: 20px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.deploy-mode-option div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.deploy-mode-option-title {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #2c3e50;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.deploy-mode-option-desc {
|
||||
font-size: 11px;
|
||||
color: #6c757d;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -157,6 +157,36 @@
|
|||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<!-- Modo de despliegue: solo visible para métodos no-direct -->
|
||||
@if (imageType === 'monolithic' && selectedMethod && !isDirectMethod()) {
|
||||
<div class="form-section-row">
|
||||
<div class="deploy-mode-label">
|
||||
<mat-icon>cached</mat-icon>
|
||||
<span>Modo de operación</span>
|
||||
</div>
|
||||
<mat-radio-group [(ngModel)]="deployMode" name="deployMode" class="deploy-mode-radio-group">
|
||||
<mat-radio-button value="deploy" color="primary" class="deploy-mode-radio">
|
||||
<div class="deploy-mode-option">
|
||||
<mat-icon>storage</mat-icon>
|
||||
<div>
|
||||
<span class="deploy-mode-option-title">Despliegue completo</span>
|
||||
<span class="deploy-mode-option-desc">Actualiza caché y restaura partición</span>
|
||||
</div>
|
||||
</div>
|
||||
</mat-radio-button>
|
||||
<mat-radio-button value="update-cache" color="primary" class="deploy-mode-radio">
|
||||
<div class="deploy-mode-option">
|
||||
<mat-icon>download</mat-icon>
|
||||
<div>
|
||||
<span class="deploy-mode-option-title">Solo actualizar caché</span>
|
||||
<span class="deploy-mode-option-desc">Descarga imagen sin restaurar partición</span>
|
||||
</div>
|
||||
</div>
|
||||
</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div *ngIf="errorMessage" class="error-message">
|
||||
{{ errorMessage }}
|
||||
</div>
|
||||
|
|
@ -172,36 +202,57 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sección: Selección de partición -->
|
||||
<div class="form-section compact-section partition-select-section" id="partition-selection">
|
||||
<div class="form-section-title">
|
||||
<mat-icon>storage</mat-icon>
|
||||
Selección de partición
|
||||
</div>
|
||||
|
||||
<div class="partition-table-container">
|
||||
<div *ngIf="showInstructions" class="instructions-box">
|
||||
<mat-card class="instructions-card">
|
||||
<mat-card-title>
|
||||
Instrucciones generadas
|
||||
<button mat-icon-button (click)="showInstructions = false" style="float: right;">
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
</mat-card-title>
|
||||
<mat-card-content>
|
||||
<pre>{{ ogInstructions }}</pre>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
<!-- Sección: Selección de partición (oculta en modo solo-caché) -->
|
||||
@if (deployMode !== 'update-cache') {
|
||||
<div class="form-section compact-section partition-select-section" id="partition-selection">
|
||||
<div class="form-section-title">
|
||||
<mat-icon>storage</mat-icon>
|
||||
Selección de partición
|
||||
</div>
|
||||
|
||||
<app-disk-partition-selector
|
||||
[(selectedPartition)]="selectedPartition"
|
||||
[partitions]="filteredPartitions"
|
||||
[columns]="columns"
|
||||
[displayedColumns]="displayedColumns">
|
||||
</app-disk-partition-selector>
|
||||
<div class="partition-table-container">
|
||||
<div *ngIf="showInstructions" class="instructions-box">
|
||||
<mat-card class="instructions-card">
|
||||
<mat-card-title>
|
||||
Instrucciones generadas
|
||||
<button mat-icon-button (click)="showInstructions = false" style="float: right;">
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
</mat-card-title>
|
||||
<mat-card-content>
|
||||
<pre>{{ ogInstructions }}</pre>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
|
||||
<app-disk-partition-selector
|
||||
[(selectedPartition)]="selectedPartition"
|
||||
[partitions]="filteredPartitions"
|
||||
[columns]="columns"
|
||||
[displayedColumns]="displayedColumns">
|
||||
</app-disk-partition-selector>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- En modo solo-caché: tabla informativa de particiones (sin selección) -->
|
||||
@if (deployMode === 'update-cache') {
|
||||
<div class="form-section compact-section partition-select-section" id="partition-info">
|
||||
<div class="form-section-title">
|
||||
<mat-icon>info</mat-icon>
|
||||
Estado actual de particiones
|
||||
</div>
|
||||
<div class="partition-table-container">
|
||||
<app-disk-partition-selector
|
||||
[(selectedPartition)]="selectedPartition"
|
||||
[partitions]="filteredPartitions"
|
||||
[columns]="columns"
|
||||
[displayedColumns]="displayedColumns"
|
||||
[readonly]="true">
|
||||
</app-disk-partition-selector>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- Tabla de commits Git (solo cuando hay commits cargados) -->
|
||||
|
|
@ -254,6 +305,7 @@
|
|||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<app-scroll-to-top [threshold]="100" targetElement=".header-container" position="bottom-right" [showTooltip]="true"
|
||||
|
|
|
|||
|
|
@ -1,21 +1,21 @@
|
|||
import {Component, EventEmitter, OnInit, Output} from '@angular/core';
|
||||
import { Component, EventEmitter, OnInit, Output } from '@angular/core';
|
||||
import { MatTableDataSource } from "@angular/material/table";
|
||||
import { SelectionModel } from "@angular/cdk/collections";
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { ToastrService } from "ngx-toastr";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
import { ConfigService } from '@services/config.service';
|
||||
import {CreateTaskComponent} from "../../../../commands/commands-task/create-task/create-task.component";
|
||||
import {MatDialog} from "@angular/material/dialog";
|
||||
import {QueueConfirmationModalComponent} from "../../../../../shared/queue-confirmation-modal/queue-confirmation-modal.component";
|
||||
import {DeployMethodConfigDialogComponent} from "./deploy-method-config-dialog/deploy-method-config-dialog.component";
|
||||
import { CreateTaskComponent } from "../../../../commands/commands-task/create-task/create-task.component";
|
||||
import { MatDialog } from "@angular/material/dialog";
|
||||
import { QueueConfirmationModalComponent } from "../../../../../shared/queue-confirmation-modal/queue-confirmation-modal.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-deploy-image',
|
||||
templateUrl: './deploy-image.component.html',
|
||||
styleUrl: './deploy-image.component.css'
|
||||
})
|
||||
export class DeployImageComponent implements OnInit{
|
||||
export class DeployImageComponent implements OnInit {
|
||||
baseUrl: string;
|
||||
@Output() dataChange = new EventEmitter<any>();
|
||||
|
||||
|
|
@ -44,6 +44,7 @@ export class DeployImageComponent implements OnInit{
|
|||
showInstructions: boolean = false;
|
||||
loadingCommits: boolean = false;
|
||||
selectedGitRepository: string = '';
|
||||
deployMode: string = 'deploy'; // 'deploy' | 'update-cache'
|
||||
|
||||
protected p2pModeOptions = [
|
||||
{ name: 'Leecher', value: 'leecher' },
|
||||
|
|
@ -58,7 +59,7 @@ export class DeployImageComponent implements OnInit{
|
|||
selectedClients: any[] = [];
|
||||
selectedModelClient: any = null;
|
||||
filteredPartitions: any[] = [];
|
||||
|
||||
|
||||
selectedRepository: any = null;
|
||||
imageType: string = 'monolithic';
|
||||
|
||||
|
|
@ -128,9 +129,9 @@ export class DeployImageComponent implements OnInit{
|
|||
}
|
||||
});
|
||||
this.clientId = this.clientData?.length ? this.clientData[0]['@id'] : null;
|
||||
this.clientData.forEach((client: { selected: boolean; status: string}) => { client.selected = true; });
|
||||
this.selectedClients = this.clientData.filter((client: { selected: boolean; status: string}) => client.selected);
|
||||
this.selectedModelClient = this.clientData.find((client: { selected: boolean; status: string}) => client.selected) || null;
|
||||
this.clientData.forEach((client: { selected: boolean; status: string }) => { client.selected = true; });
|
||||
this.selectedClients = this.clientData.filter((client: { selected: boolean; status: string }) => client.selected);
|
||||
this.selectedModelClient = this.clientData.find((client: { selected: boolean; status: string }) => client.selected) || null;
|
||||
|
||||
if (this.selectedModelClient) {
|
||||
this.loadPartitions(this.selectedModelClient);
|
||||
|
|
@ -150,6 +151,9 @@ export class DeployImageComponent implements OnInit{
|
|||
this.loadGitRepositories();
|
||||
}
|
||||
this.loadImages();
|
||||
setTimeout(() => {
|
||||
this.scrollToPartitionSection();
|
||||
}, 200);
|
||||
}
|
||||
|
||||
get runScriptTitle(): string {
|
||||
|
|
@ -173,6 +177,9 @@ export class DeployImageComponent implements OnInit{
|
|||
|
||||
onModelSelected(client: any) {
|
||||
this.loadPartitions(client);
|
||||
setTimeout(() => {
|
||||
this.scrollToPartitionSection();
|
||||
}, 200);
|
||||
}
|
||||
|
||||
toggleClientSelection(client: any) {
|
||||
|
|
@ -197,7 +204,7 @@ export class DeployImageComponent implements OnInit{
|
|||
}
|
||||
|
||||
return client.partitions
|
||||
.map((p: { partitionNumber: any; size: any; filesystem: any }) => `#${p.partitionNumber} ${p.filesystem} - ${p.size / 1024 }GB`)
|
||||
.map((p: { partitionNumber: any; size: any; filesystem: any }) => `#${p.partitionNumber} ${p.filesystem} - ${p.size / 1024}GB`)
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
|
|
@ -331,6 +338,7 @@ export class DeployImageComponent implements OnInit{
|
|||
}
|
||||
|
||||
save(): void {
|
||||
const needsPartition = this.deployMode !== 'update-cache';
|
||||
|
||||
if (!this.selectedClients.length) {
|
||||
this.toastService.error('Debe seleccionar al menos un cliente');
|
||||
|
|
@ -347,7 +355,7 @@ export class DeployImageComponent implements OnInit{
|
|||
return;
|
||||
}
|
||||
|
||||
if (!this.selectedPartition) {
|
||||
if (needsPartition && !this.selectedPartition) {
|
||||
this.toastService.error('Debe seleccionar una partición');
|
||||
return;
|
||||
}
|
||||
|
|
@ -369,15 +377,15 @@ export class DeployImageComponent implements OnInit{
|
|||
dialogRef.afterClosed().subscribe(result => {
|
||||
if (result !== undefined) {
|
||||
this.loading = true;
|
||||
|
||||
|
||||
let payload: any;
|
||||
let url: string;
|
||||
|
||||
if (this.imageType === 'git') {
|
||||
payload = {
|
||||
type: 'git',
|
||||
diskNumber: this.selectedPartition.diskNumber,
|
||||
partitionNumber: this.selectedPartition.partitionNumber,
|
||||
diskNumber: this.selectedPartition?.diskNumber || 1,
|
||||
partitionNumber: this.selectedPartition?.partitionNumber || 1,
|
||||
repositoryName: this.selectedGitRepository,
|
||||
branch: this.selectedBranch,
|
||||
hexsha: this.selectedCommit.hexsha,
|
||||
|
|
@ -389,8 +397,8 @@ export class DeployImageComponent implements OnInit{
|
|||
} else {
|
||||
payload = {
|
||||
method: this.selectedMethod,
|
||||
diskNumber: this.selectedPartition.diskNumber,
|
||||
partitionNumber: this.selectedPartition.partitionNumber,
|
||||
diskNumber: this.selectedPartition?.diskNumber || 1,
|
||||
partitionNumber: this.selectedPartition?.partitionNumber || 1,
|
||||
p2pMode: this.p2pMode,
|
||||
p2pTime: this.p2pMode === 'seeder' ? this.p2pTime : 0,
|
||||
mcastIp: this.mcastIp,
|
||||
|
|
@ -402,6 +410,7 @@ export class DeployImageComponent implements OnInit{
|
|||
type: this.imageType,
|
||||
queue: result,
|
||||
clients: this.selectedClients.map((client: any) => client.uuid),
|
||||
deployMode: this.isDirectMethod() ? 'deploy' : this.deployMode,
|
||||
};
|
||||
|
||||
url = `${this.baseUrl}/image-image-repositories/${this.selectedImage.uuid}/deploy-image`;
|
||||
|
|
@ -432,7 +441,8 @@ export class DeployImageComponent implements OnInit{
|
|||
}
|
||||
|
||||
isFormValid(): boolean {
|
||||
if (!this.allSelected || !this.selectedModelClient || !this.selectedPartition) {
|
||||
const needsPartition = this.deployMode !== 'update-cache';
|
||||
if (!this.allSelected || !this.selectedModelClient || (needsPartition && !this.selectedPartition)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -466,18 +476,23 @@ export class DeployImageComponent implements OnInit{
|
|||
return true;
|
||||
}
|
||||
|
||||
isDirectMethod(): boolean {
|
||||
return this.selectedMethod === 'unicast-direct' ||
|
||||
this.selectedMethod === 'udpcast-direct';
|
||||
}
|
||||
|
||||
|
||||
openScheduleModal(): void {
|
||||
let scope = this.runScriptContext?.type || 'clients';
|
||||
let selectedClients = null;
|
||||
|
||||
|
||||
if (this.selectedClients.length === 0 && this.clientData.length > 0) {
|
||||
this.updateSelectedClients();
|
||||
}
|
||||
|
||||
const isOrganizationalContext = this.runScriptContext?.type &&
|
||||
|
||||
const isOrganizationalContext = this.runScriptContext?.type &&
|
||||
['organizational-unit', 'classroom', 'classrooms-group', 'clients-group'].includes(this.runScriptContext.type);
|
||||
|
||||
|
||||
if (!isOrganizationalContext && this.selectedClients && this.selectedClients.length > 0) {
|
||||
scope = 'clients';
|
||||
selectedClients = this.selectedClients;
|
||||
|
|
@ -490,7 +505,7 @@ export class DeployImageComponent implements OnInit{
|
|||
width: '800px',
|
||||
data: {
|
||||
scope: scope,
|
||||
selectedClients: selectedClients,
|
||||
selectedClients: selectedClients,
|
||||
organizationalUnit: this.runScriptContext?.['@id'],
|
||||
source: 'assistant',
|
||||
runScriptContext: this.runScriptContext
|
||||
|
|
@ -499,10 +514,11 @@ export class DeployImageComponent implements OnInit{
|
|||
|
||||
dialogRef.afterClosed().subscribe((result: { [x: string]: any; }) => {
|
||||
if (result) {
|
||||
// Si es un updateCache no hay que indicar disco ni partición, se envían a 1 ambas
|
||||
const basePayload: any = {
|
||||
method: this.selectedMethod,
|
||||
diskNumber: this.selectedPartition.diskNumber,
|
||||
partitionNumber: this.selectedPartition.partitionNumber,
|
||||
diskNumber: this.selectedPartition?.diskNumber || 1,
|
||||
partitionNumber: this.selectedPartition?.partitionNumber || 1,
|
||||
imageName: this.selectedImage.name,
|
||||
imageUuid: this.selectedImage.uuid,
|
||||
type: this.imageType,
|
||||
|
|
@ -560,10 +576,32 @@ export class DeployImageComponent implements OnInit{
|
|||
|
||||
generateOgInstructions() {
|
||||
this.showInstructions = true;
|
||||
this.ogInstructions = `og-deploy-image --image ${this.selectedImage.name} --partition ${this.selectedPartition.partitionNumber} --method ${this.selectedMethod}`;
|
||||
this.ogInstructions = `og-deploy-image --image ${this.selectedImage.name} --partition ${this.selectedPartition?.partitionNumber || 1} --method ${this.selectedMethod}`;
|
||||
}
|
||||
|
||||
|
||||
scrollToPartitionSection() {
|
||||
const partitionSection = document.getElementById('partition-selection');
|
||||
if (partitionSection) {
|
||||
partitionSection.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'start'
|
||||
});
|
||||
console.log('Scroll ejecutado');
|
||||
}
|
||||
}
|
||||
|
||||
scrollToAdvancedOptions() {
|
||||
const advancedOptions = document.getElementById('advanced-options');
|
||||
|
||||
if (advancedOptions) {
|
||||
advancedOptions.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'start'
|
||||
});
|
||||
console.log('Scroll hacia opciones avanzadas ejecutado');
|
||||
}
|
||||
}
|
||||
|
||||
get selectedPartition(): any {
|
||||
return this._selectedPartition;
|
||||
|
|
@ -641,7 +679,7 @@ export class DeployImageComponent implements OnInit{
|
|||
this.loadingCommits = false;
|
||||
this.filterCommits();
|
||||
if (this.filteredCommits.length > 0) {
|
||||
this.selectedCommit = this.filteredCommits.reduce((a, b) =>
|
||||
this.selectedCommit = this.filteredCommits.reduce((a, b) =>
|
||||
new Date(a.committed_date * 1000) > new Date(b.committed_date * 1000) ? a : b
|
||||
);
|
||||
}
|
||||
|
|
@ -655,7 +693,7 @@ export class DeployImageComponent implements OnInit{
|
|||
|
||||
filterCommits() {
|
||||
if (this.showOnlyTagged) {
|
||||
this.filteredCommits = this.commits.filter(commit =>
|
||||
this.filteredCommits = this.commits.filter(commit =>
|
||||
commit.tags && commit.tags.length > 0
|
||||
);
|
||||
} else {
|
||||
|
|
@ -666,7 +704,7 @@ export class DeployImageComponent implements OnInit{
|
|||
onTagFilterChange() {
|
||||
this.filterCommits();
|
||||
if (this.filteredCommits.length > 0 && !this.filteredCommits.includes(this.selectedCommit)) {
|
||||
this.selectedCommit = this.filteredCommits.reduce((a, b) =>
|
||||
this.selectedCommit = this.filteredCommits.reduce((a, b) =>
|
||||
new Date(a.committed_date * 1000) > new Date(b.committed_date * 1000) ? a : b
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,73 +49,69 @@
|
|||
<h3 class="disk-card-title">Disco {{ disk.diskNumber }}</h3>
|
||||
<div class="disk-card-metrics-wrapper">
|
||||
<mat-chip class="disk-card-metrics-chip">
|
||||
Total: {{ disk.total.toFixed(2) }} GB | Usado: {{ (disk.used).toFixed(2) }} GB ({{ disk.percentage }}%)
|
||||
Total: {{ disk.total.toFixed(2) }} GB | Usado: {{ (disk.used).toFixed(2) }} GB ({{
|
||||
disk.percentage }}%)
|
||||
</mat-chip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="disk-card-layout">
|
||||
<div class="disk-table-container">
|
||||
<table mat-table [dataSource]="disk.partitions" class="disk-table">
|
||||
@for (column of columns; track column.columnDef) {
|
||||
<ng-container [matColumnDef]="column.columnDef">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ column.header }} </th>
|
||||
<td mat-cell *matCellDef="let image">
|
||||
@if (column.columnDef !== 'size' && column.columnDef !== 'operativeSystem') {
|
||||
@for (column of columns; track column.columnDef) {
|
||||
<ng-container [matColumnDef]="column.columnDef">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ column.header }} </th>
|
||||
<td mat-cell *matCellDef="let image">
|
||||
@if (column.columnDef !== 'size' && column.columnDef !== 'operativeSystem') {
|
||||
{{ column.cell(image) }}
|
||||
}
|
||||
@if (column.columnDef === 'size') {
|
||||
}
|
||||
@if (column.columnDef === 'size') {
|
||||
<mat-chip color="primary">
|
||||
{{ (image.size / 1024).toFixed(2) }} GB
|
||||
</mat-chip>
|
||||
}
|
||||
}
|
||||
|
||||
@if (column.columnDef === 'operativeSystem') {
|
||||
@if (column.columnDef === 'operativeSystem') {
|
||||
<div class="os-col-wrapper">
|
||||
@if (image.filesystem === 'CACHE' && image.cacheContent) {
|
||||
<span class="cache-title">Contenido:</span>
|
||||
@for (item of parseCacheContent(image.cacheContent); track item.name) {
|
||||
<div class="cache-item">
|
||||
• {{ item.name }} ({{ item.size }})
|
||||
</div>
|
||||
}
|
||||
<span class="cache-title">Contenido:</span>
|
||||
@for (item of parseCacheContent(image.cacheContent); track item.name) {
|
||||
<div class="cache-item">
|
||||
• {{ item.name }} ({{ item.size }})
|
||||
</div>
|
||||
}
|
||||
}
|
||||
@if (image.filesystem !== 'CACHE' || !image.cacheContent) {
|
||||
<span>{{image.operativeSystem?.name }}</span>
|
||||
<span class="image-name">{{ image.image?.name }}</span>
|
||||
@if (image.deployDate) {
|
||||
<span class="image-date">Instalada el {{ image.deployDate | date:'dd/MM/yyyy HH:mm' }}</span>
|
||||
}
|
||||
<span>{{image.operativeSystem?.name }}</span>
|
||||
<span class="image-name">{{ image.image?.name }}</span>
|
||||
@if (image.deployDate) {
|
||||
<span class="image-date">Desplegada: {{ image.deployDate | date:'dd/MM/yyyy HH:mm' }}</span>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</td>
|
||||
</ng-container>
|
||||
}
|
||||
}
|
||||
</td>
|
||||
</ng-container>
|
||||
}
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
|
||||
<ng-container>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;" [hidden]="row.partitionNumber === 0"></tr>
|
||||
</ng-container>
|
||||
</table>
|
||||
<ng-container>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;" [hidden]="row.partitionNumber === 0">
|
||||
</tr>
|
||||
</ng-container>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="charts-container disk-chart-container">
|
||||
<div class="disk-chart-wrapper">
|
||||
<ngx-charts-pie-chart
|
||||
class="chart"
|
||||
[view]="[360, 230]"
|
||||
[results]="disk.chartData"
|
||||
[doughnut]="true"
|
||||
[legend]="true"
|
||||
[legendPosition]="legendPosition"
|
||||
[legendTitle]="''">
|
||||
</ngx-charts-pie-chart>
|
||||
<div class="charts-container disk-chart-container">
|
||||
<div class="disk-chart-wrapper">
|
||||
<ngx-charts-pie-chart class="chart" [view]="[360, 230]" [results]="disk.chartData" [doughnut]="true"
|
||||
[legend]="true" [legendPosition]="legendPosition" [legendTitle]="''">
|
||||
</ngx-charts-pie-chart>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -49,3 +49,32 @@
|
|||
box-shadow: none !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
/* OS Column Classes */
|
||||
.os-col-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.cache-title {
|
||||
font-weight: bold;
|
||||
font-size: 0.8rem;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.cache-item {
|
||||
font-size: 0.75rem;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.image-name {
|
||||
font-size: 0.75rem;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
.image-date {
|
||||
font-size: 0.75rem;
|
||||
color: #555;
|
||||
font-style: italic;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
|
@ -1,53 +1,71 @@
|
|||
<div class="disk-container">
|
||||
@for (disk of diskUsageData; track disk.diskNumber) {
|
||||
<div class="disk-card-wrapper">
|
||||
<mat-card class="disk-mat-card mat-elevation-z4">
|
||||
<div class="disk-card-header">
|
||||
<h3 class="disk-card-title">Disco {{ disk.diskNumber }}</h3>
|
||||
</div>
|
||||
|
||||
<div class="disk-card-layout">
|
||||
<div class="disk-table-container">
|
||||
<table mat-table [dataSource]="disk.partitions" class="disk-table">
|
||||
|
||||
<ng-container matColumnDef="select">
|
||||
<th mat-header-cell *matHeaderCellDef style="text-align: start">Seleccionar</th>
|
||||
<td mat-cell *matCellDef="let row">
|
||||
<mat-radio-group
|
||||
[ngModel]="selectedPartition"
|
||||
(ngModelChange)="onPartitionSelected($event)"
|
||||
name="selectedPartitionGroup"
|
||||
[disabled]="disableCondition(row)">
|
||||
<mat-radio-button [value]="row"></mat-radio-button>
|
||||
</mat-radio-group>
|
||||
</td>
|
||||
</ng-container>
|
||||
<div class="disk-card-wrapper">
|
||||
<mat-card class="disk-mat-card mat-elevation-z4">
|
||||
<div class="disk-card-header">
|
||||
<h3 class="disk-card-title">Disco {{ disk.diskNumber }}</h3>
|
||||
</div>
|
||||
|
||||
@for (column of columns; track column.columnDef) {
|
||||
<ng-container [matColumnDef]="column.columnDef">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ column.header }} </th>
|
||||
<td mat-cell *matCellDef="let partition">
|
||||
@if (column.columnDef === 'size') {
|
||||
<div style="display: flex; flex-direction: column;">
|
||||
<span>{{ partition.size }} MB</span>
|
||||
<span style="font-size: 0.75rem; color: gray;">{{ partition.size / 1024 | number:'1.0-2' }} GB</span>
|
||||
</div>
|
||||
} @else {
|
||||
{{ column.cell ? column.cell(partition) : partition[column.columnDef] }}
|
||||
}
|
||||
</td>
|
||||
</ng-container>
|
||||
}
|
||||
<div class="disk-card-layout">
|
||||
<div class="disk-table-container">
|
||||
<table mat-table [dataSource]="disk.partitions" class="disk-table">
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"
|
||||
(click)="onPartitionSelected(row)"
|
||||
[ngStyle]="{'cursor': disableCondition(row) ? 'default' : 'pointer', 'opacity': disableCondition(row) ? '0.5' : '1'}">
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<ng-container matColumnDef="select">
|
||||
<th mat-header-cell *matHeaderCellDef style="text-align: start">Seleccionar</th>
|
||||
<td mat-cell *matCellDef="let row">
|
||||
<mat-radio-group [ngModel]="selectedPartition" (ngModelChange)="onPartitionSelected($event)"
|
||||
name="selectedPartitionGroup" [disabled]="readonly || disableCondition(row)">
|
||||
<mat-radio-button [value]="row"></mat-radio-button>
|
||||
</mat-radio-group>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
@for (column of columns; track column.columnDef) {
|
||||
<ng-container [matColumnDef]="column.columnDef">
|
||||
<th mat-header-cell *matHeaderCellDef> {{ column.header }} </th>
|
||||
<td mat-cell *matCellDef="let partition">
|
||||
@if (column.columnDef === 'size') {
|
||||
<div style="display: flex; flex-direction: column;">
|
||||
<span>{{ partition.size }} MB</span>
|
||||
<span style="font-size: 0.75rem; color: gray;">{{ partition.size / 1024 | number:'1.0-2' }} GB</span>
|
||||
</div>
|
||||
}
|
||||
@else if (column.columnDef === 'operativeSystem') {
|
||||
<div class="os-col-wrapper">
|
||||
@if (partition.filesystem === 'CACHE' && partition.cacheContent) {
|
||||
<span class="cache-title">Contenido:</span>
|
||||
@for (item of parseCacheContent(partition.cacheContent); track item.name) {
|
||||
<div class="cache-item">
|
||||
• {{ item.name }} ({{ item.size }})
|
||||
</div>
|
||||
}
|
||||
}
|
||||
@if (partition.filesystem !== 'CACHE' || !partition.cacheContent) {
|
||||
<span>{{partition.operativeSystem?.name }}</span>
|
||||
<span class="image-name">{{ partition.image?.name }}</span>
|
||||
@if (partition.deployDate) {
|
||||
<span class="image-date">Desplegada: {{ partition.deployDate | date:'dd/MM/yyyy HH:mm'
|
||||
}}</span>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@else {
|
||||
{{ column.cell ? column.cell(partition) : partition[column.columnDef] }}
|
||||
}
|
||||
</td>
|
||||
</ng-container>
|
||||
}
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;" (click)="!readonly && onPartitionSelected(row)"
|
||||
[ngStyle]="{'cursor': (readonly || disableCondition(row)) ? 'default' : 'pointer', 'opacity': (readonly || disableCondition(row)) ? '0.5' : '1'}">
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</mat-card>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -18,7 +18,8 @@ export class DiskPartitionSelectorComponent implements OnChanges {
|
|||
@Input() displayedColumns: string[] = [];
|
||||
@Input() selectedPartition: any;
|
||||
@Input() disableCondition: (row: any) => boolean = () => false;
|
||||
|
||||
@Input() readonly: boolean = false;
|
||||
|
||||
@Output() selectedPartitionChange = new EventEmitter<any>();
|
||||
|
||||
diskUsageData: any[] = [];
|
||||
|
|
@ -55,4 +56,12 @@ export class DiskPartitionSelectorComponent implements OnChanges {
|
|||
this.selectedPartitionChange.emit(this.selectedPartition);
|
||||
}
|
||||
}
|
||||
parseCacheContent(content: string): { name: string, size: string }[] {
|
||||
if (!content) return [];
|
||||
return content.split(',').map(item => {
|
||||
const [name, bytes] = item.split(':');
|
||||
const sizeMB = (parseInt(bytes) / (1024 * 1024)).toFixed(2) + ' MB';
|
||||
return { name, size: sizeMB };
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
<<<<<<< Updated upstream
|
||||
<?xml version="1.0"?>
|
||||
<testsuite name="Chrome Headless 131.0.0.0 (Linux x86_64)" package="" timestamp="2024-12-09T10:14:38" id="0" hostname="alvaro-Latitude-3420" tests="80" errors="0" failures="0" time="2.257">
|
||||
<properties>
|
||||
|
|
@ -92,4 +93,6 @@
|
|||
]]>
|
||||
</system-out>
|
||||
<system-err/>
|
||||
</testsuite>
|
||||
</testsuite>
|
||||
=======
|
||||
>>>>>>> Stashed changes
|
||||
|
|
|
|||
Loading…
Reference in New Issue