diff --git a/CHANGELOG.md b/CHANGELOG.md index d1a2277..23680f8 100644 --- a/CHANGELOG.md +++ b/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. diff --git a/ogWebconsole/src/app/components/groups/components/client-main-view/deploy-image/deploy-image.component.css b/ogWebconsole/src/app/components/groups/components/client-main-view/deploy-image/deploy-image.component.css index c62d8d3..8726114 100644 --- a/ogWebconsole/src/app/components/groups/components/client-main-view/deploy-image/deploy-image.component.css +++ b/ogWebconsole/src/app/components/groups/components/client-main-view/deploy-image/deploy-image.component.css @@ -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; +} diff --git a/ogWebconsole/src/app/components/groups/components/client-main-view/deploy-image/deploy-image.component.html b/ogWebconsole/src/app/components/groups/components/client-main-view/deploy-image/deploy-image.component.html index 4e6269c..d86dc8d 100644 --- a/ogWebconsole/src/app/components/groups/components/client-main-view/deploy-image/deploy-image.component.html +++ b/ogWebconsole/src/app/components/groups/components/client-main-view/deploy-image/deploy-image.component.html @@ -157,6 +157,36 @@ + + @if (imageType === 'monolithic' && selectedMethod && !isDirectMethod()) { +
+
+ cached + Modo de operación +
+ + +
+ storage +
+ Despliegue completo + Actualiza caché y restaura partición +
+
+
+ +
+ download +
+ Solo actualizar caché + Descarga imagen sin restaurar partición +
+
+
+
+
+ } +
{{ errorMessage }}
@@ -172,36 +202,57 @@ - -
-
- storage - Selección de partición -
- -
-
- - - Instrucciones generadas - - - -
{{ ogInstructions }}
-
-
+ + @if (deployMode !== 'update-cache') { +
+
+ storage + Selección de partición
- - +
+
+ + + Instrucciones generadas + + + +
{{ ogInstructions }}
+
+
+
+ + + +
-
+ } + + + @if (deployMode === 'update-cache') { +
+
+ info + Estado actual de particiones +
+
+ + +
+
+ }
@@ -254,6 +305,7 @@
+ (); @@ -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 ); } diff --git a/ogWebconsole/src/app/components/groups/shared/client-details/client-details.component.html b/ogWebconsole/src/app/components/groups/shared/client-details/client-details.component.html index c8d9c15..d6e94c7 100644 --- a/ogWebconsole/src/app/components/groups/shared/client-details/client-details.component.html +++ b/ogWebconsole/src/app/components/groups/shared/client-details/client-details.component.html @@ -49,73 +49,69 @@

Disco {{ disk.diskNumber }}

- 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 }}%)
- +
- @for (column of columns; track column.columnDef) { - - - + - - } + } + + + } - + - - - -
{{ column.header }} - @if (column.columnDef !== 'size' && column.columnDef !== 'operativeSystem') { + @for (column of columns; track column.columnDef) { + + {{ column.header }} + @if (column.columnDef !== 'size' && column.columnDef !== 'operativeSystem') { {{ column.cell(image) }} - } - @if (column.columnDef === 'size') { + } + @if (column.columnDef === 'size') { {{ (image.size / 1024).toFixed(2) }} GB - } + } - @if (column.columnDef === 'operativeSystem') { + @if (column.columnDef === 'operativeSystem') {
@if (image.filesystem === 'CACHE' && image.cacheContent) { - Contenido: - @for (item of parseCacheContent(image.cacheContent); track item.name) { -
- • {{ item.name }} ({{ item.size }}) -
- } + Contenido: + @for (item of parseCacheContent(image.cacheContent); track item.name) { +
+ • {{ item.name }} ({{ item.size }}) +
+ } } @if (image.filesystem !== 'CACHE' || !image.cacheContent) { - {{image.operativeSystem?.name }} - {{ image.image?.name }} - @if (image.deployDate) { - Instalada el {{ image.deployDate | date:'dd/MM/yyyy HH:mm' }} - } + {{image.operativeSystem?.name }} + {{ image.image?.name }} + @if (image.deployDate) { + Desplegada: {{ image.deployDate | date:'dd/MM/yyyy HH:mm' }} + } }
- } -
+ + + + + -
+
-
-
- - +
+
+ + +
-
diff --git a/ogWebconsole/src/app/components/groups/shared/disk-partition-selector/disk-partition-selector.component.css b/ogWebconsole/src/app/components/groups/shared/disk-partition-selector/disk-partition-selector.component.css index aa3c38d..a0d98c9 100644 --- a/ogWebconsole/src/app/components/groups/shared/disk-partition-selector/disk-partition-selector.component.css +++ b/ogWebconsole/src/app/components/groups/shared/disk-partition-selector/disk-partition-selector.component.css @@ -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; +} \ No newline at end of file diff --git a/ogWebconsole/src/app/components/groups/shared/disk-partition-selector/disk-partition-selector.component.html b/ogWebconsole/src/app/components/groups/shared/disk-partition-selector/disk-partition-selector.component.html index 899e6c7..2a49b55 100644 --- a/ogWebconsole/src/app/components/groups/shared/disk-partition-selector/disk-partition-selector.component.html +++ b/ogWebconsole/src/app/components/groups/shared/disk-partition-selector/disk-partition-selector.component.html @@ -1,53 +1,71 @@
@for (disk of diskUsageData; track disk.diskNumber) { -
- -
-

Disco {{ disk.diskNumber }}

-
- -
-
- - - - - - +
+ +
+

Disco {{ disk.diskNumber }}

+
- @for (column of columns; track column.columnDef) { - -
- - - } +
+
+
Seleccionar - - - - {{ column.header }} - @if (column.columnDef === 'size') { -
- {{ partition.size }} MB - {{ partition.size / 1024 | number:'1.0-2' }} GB -
- } @else { - {{ column.cell ? column.cell(partition) : partition[column.columnDef] }} - } -
- - - -
-
+ + Seleccionar + + + + + + + + @for (column of columns; track column.columnDef) { + + {{ column.header }} + + @if (column.columnDef === 'size') { +
+ {{ partition.size }} MB + {{ partition.size / 1024 | number:'1.0-2' }} GB +
+ } + @else if (column.columnDef === 'operativeSystem') { +
+ @if (partition.filesystem === 'CACHE' && partition.cacheContent) { + Contenido: + @for (item of parseCacheContent(partition.cacheContent); track item.name) { +
+ • {{ item.name }} ({{ item.size }}) +
+ } + } + @if (partition.filesystem !== 'CACHE' || !partition.cacheContent) { + {{partition.operativeSystem?.name }} + {{ partition.image?.name }} + @if (partition.deployDate) { + Desplegada: {{ partition.deployDate | date:'dd/MM/yyyy HH:mm' + }} + } + } +
+ } + + @else { + {{ column.cell ? column.cell(partition) : partition[column.columnDef] }} + } + +
+ } + + + + +
-
-
+
+ + } - + \ No newline at end of file diff --git a/ogWebconsole/src/app/components/groups/shared/disk-partition-selector/disk-partition-selector.component.ts b/ogWebconsole/src/app/components/groups/shared/disk-partition-selector/disk-partition-selector.component.ts index a3bb8ad..493c7d6 100644 --- a/ogWebconsole/src/app/components/groups/shared/disk-partition-selector/disk-partition-selector.component.ts +++ b/ogWebconsole/src/app/components/groups/shared/disk-partition-selector/disk-partition-selector.component.ts @@ -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(); 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 }; + }); + } } diff --git a/ogWebconsole/test-results/ogGui-junit-report.xml b/ogWebconsole/test-results/ogGui-junit-report.xml index eb892a9..08f02de 100644 --- a/ogWebconsole/test-results/ogGui-junit-report.xml +++ b/ogWebconsole/test-results/ogGui-junit-report.xml @@ -1,3 +1,4 @@ +<<<<<<< Updated upstream @@ -92,4 +93,6 @@ ]]> - \ No newline at end of file + +======= +>>>>>>> Stashed changes