refs #1931 Refactor ClientDetailsComponent to enhance layout and improve data handling in client details view
	
		
			
	
		
	
	
		
			
				
	
				testing/ogGui-multibranch/pipeline/head This commit looks good
				
					Details
				
			
		
	
				
					
				
			
				
	
				testing/ogGui-multibranch/pipeline/head This commit looks good
				
					Details
				
			
		
	
							parent
							
								
									a40be684b5
								
							
						
					
					
						commit
						70e21c6ca2
					
				|  | @ -1,326 +1,88 @@ | |||
| .header-container { | ||||
|   display: flex; | ||||
|   justify-content: space-between; | ||||
|   align-items: center; | ||||
|   padding: 10px; | ||||
| } | ||||
| 
 | ||||
| .client-container { | ||||
|   flex-grow: 1; | ||||
|   box-sizing: border-box; | ||||
|   overflow: hidden; | ||||
| .modal-content { | ||||
|   max-height: 80vh; | ||||
|   overflow-y: auto; | ||||
|   background-color: #fff; | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|   padding: 0rem 1rem 0rem 0.5rem; | ||||
| } | ||||
| 
 | ||||
| .client-icon { | ||||
|   flex-shrink: 0; | ||||
|   margin-right: 20px; | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
|   justify-content: center; | ||||
|   min-width: 120px; | ||||
|   min-height: 120px; | ||||
| .section-header { | ||||
|   margin-bottom: 8px; | ||||
| } | ||||
| 
 | ||||
| .row-container { | ||||
|   justify-content: space-between; | ||||
|   width: 100%; | ||||
| } | ||||
| 
 | ||||
| .table-container { | ||||
|   padding-right: 10px; | ||||
| } | ||||
| 
 | ||||
| .charts-wrapper { | ||||
|   width: 100%; | ||||
|   margin-top: 20px; | ||||
| } | ||||
| 
 | ||||
| .charts-row { | ||||
|   display: flex; | ||||
|   flex-wrap: wrap; | ||||
|   justify-content: space-between; | ||||
|   gap: 20px; | ||||
| } | ||||
| 
 | ||||
| .disk-usage { | ||||
|   text-align: center; | ||||
|   flex: 1; | ||||
|   min-width: 200px; | ||||
| } | ||||
| 
 | ||||
| .circular-chart { | ||||
|   max-width: 150px; | ||||
|   max-height: 150px; | ||||
|   margin: 0 auto; | ||||
| } | ||||
| 
 | ||||
| .chart { | ||||
|   display: flex; | ||||
|   justify-content: center; | ||||
| } | ||||
| 
 | ||||
| .icon-pc { | ||||
|   font-size: 25px; | ||||
|   color: #3b82f6; | ||||
| } | ||||
| 
 | ||||
| .client-title h1 { | ||||
|   font-size: 2rem; | ||||
|   margin-bottom: 10px; | ||||
| } | ||||
| 
 | ||||
| .client-title p { | ||||
|   margin: 2px 0; | ||||
|   font-size: 1rem; | ||||
|   color: #666; | ||||
| } | ||||
| 
 | ||||
| .client-info { | ||||
|   margin: 20px 0; | ||||
| .client-info-card { | ||||
|   background-color: #e4e4e4; | ||||
|   padding: 24px; | ||||
|   border-radius: 12px; | ||||
|   background-color: #f5f7fa; | ||||
|   padding: 20px; | ||||
|   border: 2px solid #d1d9e6; | ||||
|   box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1); | ||||
|   box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05); | ||||
|   margin-bottom: 2em; | ||||
|   margin-top: 1.5em; | ||||
| } | ||||
| 
 | ||||
| .info-section { | ||||
|   background-color: #fff; | ||||
|   border-radius: 12px; | ||||
| } | ||||
| 
 | ||||
| .two-column-table { | ||||
| .info-columns { | ||||
|   display: grid; | ||||
|   grid-template-columns: 1fr 1fr; | ||||
|   gap: 10px; | ||||
|   margin-top: 15px; | ||||
|   gap: 24px; | ||||
| } | ||||
| 
 | ||||
| .mat-elevation-z8 { | ||||
|   box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.2); | ||||
| } | ||||
| 
 | ||||
| .table-row { | ||||
|   display: flex; | ||||
|   justify-content: space-between; | ||||
|   padding: 10px; | ||||
|   border-bottom: 1px solid #e0e0e0; | ||||
| } | ||||
| 
 | ||||
| .column.property { | ||||
|   font-weight: bold; | ||||
|   text-align: left; | ||||
|   width: 45%; | ||||
| } | ||||
| 
 | ||||
| .column.value { | ||||
|   text-align: right; | ||||
|   width: 45%; | ||||
| } | ||||
| 
 | ||||
| .mat-tab-group { | ||||
|   min-height: 400px; | ||||
| } | ||||
| 
 | ||||
| .mat-tab-body-wrapper { | ||||
|   min-height: inherit; | ||||
| } | ||||
| 
 | ||||
| .info-section h2 { | ||||
|   font-size: 1.4rem; | ||||
|   margin-bottom: 10px; | ||||
|   color: #0056b3; | ||||
| } | ||||
| 
 | ||||
| .info-section p { | ||||
|   font-size: 1rem; | ||||
|   margin: 5px 0; | ||||
| } | ||||
| 
 | ||||
| .second-section { | ||||
|   display: grid; | ||||
|   gap: 20px; | ||||
| } | ||||
| 
 | ||||
| .client-button-row { | ||||
|   display: flex; | ||||
|   flex-wrap: wrap; | ||||
|   justify-content: space-between; | ||||
|   gap: 20px; | ||||
| } | ||||
| 
 | ||||
| .buttons-row { | ||||
| .info-column { | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|   background-color: #fff; | ||||
|   padding: 20px; | ||||
|   border-radius: 12px; | ||||
|   box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); | ||||
|   justify-content: flex-start; | ||||
|   gap: 16px; | ||||
| } | ||||
| 
 | ||||
| .buttons-row button { | ||||
|   margin-bottom: 10px; | ||||
|   width: 100%; | ||||
| .info-pair { | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
| } | ||||
| 
 | ||||
| .circular-chart { | ||||
|   display: block; | ||||
|   margin: 0 auto; | ||||
|   max-width: 100%; | ||||
|   max-height: 150px; | ||||
| .label { | ||||
|   font-size: 0.9rem; | ||||
|   font-weight: 550; | ||||
|   color: #3f51b5; | ||||
| } | ||||
| 
 | ||||
| .circle-bg { | ||||
|   fill: none; | ||||
|   stroke: #f0f0f0; | ||||
|   stroke-width: 3.8; | ||||
| } | ||||
| 
 | ||||
| .circle { | ||||
|   fill: none; | ||||
|   stroke-width: 3.8; | ||||
|   stroke: #00bfa5; | ||||
|   stroke-linecap: round; | ||||
|   animation: progress 1s ease-out forwards; | ||||
| } | ||||
| 
 | ||||
| .percentage { | ||||
|   fill: #333; | ||||
|   font-size: 0.7rem; | ||||
|   text-anchor: middle; | ||||
| } | ||||
| 
 | ||||
| .disk-usage h3 { | ||||
|   margin: 0 0 10px 0; | ||||
|   font-size: 1.2rem; | ||||
|   color: #333; | ||||
| } | ||||
| 
 | ||||
| @keyframes progress { | ||||
|   0% { | ||||
|     stroke-dasharray: 0, 100; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .assistants-container { | ||||
|   background-color: #fff; | ||||
|   margin-top: 10px; | ||||
|   padding: 20px; | ||||
|   border-radius: 12px; | ||||
|   box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); | ||||
| } | ||||
| 
 | ||||
| .circular-chart { | ||||
|   display: block; | ||||
|   margin: 0 auto; | ||||
|   max-width: 100%; | ||||
|   max-height: 150px; | ||||
| } | ||||
| 
 | ||||
| .circle-bg { | ||||
|   fill: none; | ||||
|   stroke: #f0f0f0; | ||||
|   stroke-width: 3.8; | ||||
| } | ||||
| 
 | ||||
| .circle { | ||||
|   fill: none; | ||||
|   stroke-width: 3.8; | ||||
|   stroke-linecap: round; | ||||
|   animation: progress 1s ease-out forwards; | ||||
| } | ||||
| 
 | ||||
| .partition-0 { | ||||
|   stroke: #00bfa5; | ||||
| } | ||||
| 
 | ||||
| .partition-1 { | ||||
|   stroke: #ff6f61; | ||||
| } | ||||
| 
 | ||||
| .partition-2 { | ||||
|   stroke: #ffb400; | ||||
| } | ||||
| 
 | ||||
| .partition-3 { | ||||
|   stroke: #3498db; | ||||
| } | ||||
| 
 | ||||
| .percentage { | ||||
|   fill: #333; | ||||
|   font-size: 0.7rem; | ||||
|   text-anchor: middle; | ||||
| .value { | ||||
|   font-size: 1rem; | ||||
|   color: #222; | ||||
|   word-break: break-word; | ||||
| } | ||||
| 
 | ||||
| .disk-container { | ||||
|   display: flex; | ||||
|   flex-direction: row; | ||||
|   gap: 20px; | ||||
|   background-color: #f5f7fa; | ||||
|   border: 2px solid #d1d9e6; | ||||
|   border-radius: 10px; | ||||
|   padding: 20px; | ||||
|   margin-top: 20px; | ||||
|   box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1); | ||||
|   flex-wrap: wrap; | ||||
|   justify-content: center; | ||||
|   align-items: stretch; | ||||
|   margin-bottom: 20px; | ||||
|   flex-direction: column; | ||||
|   gap: 32px; | ||||
| } | ||||
| 
 | ||||
| .table-container { | ||||
|   flex: 3; | ||||
|   display: flex; | ||||
|   justify-content: center; | ||||
|   align-items: center; | ||||
| } | ||||
| 
 | ||||
| table.mat-elevation-z8 { | ||||
|   width: 100%; | ||||
|   max-width: 800px; | ||||
|   background-color: white; | ||||
|   overflow-x: auto; | ||||
|   border-radius: 8px; | ||||
|   overflow: hidden; | ||||
|   background: #fff; | ||||
|   padding: 16px; | ||||
|   box-shadow: 0 2px 6px rgba(0,0,0,0.05); | ||||
| } | ||||
| 
 | ||||
| .mat-header-cell { | ||||
|   background-color: #d1d9e6 !important; | ||||
|   color: #333; | ||||
|   font-weight: bold; | ||||
|   text-align: center; | ||||
| } | ||||
| 
 | ||||
| .mat-cell { | ||||
|   text-align: center; | ||||
| } | ||||
| 
 | ||||
| .mat-chip { | ||||
|   font-weight: bold; | ||||
|   color: white; | ||||
| .mat-elevation-z8 { | ||||
|   width: 100%; | ||||
|   border-radius: 8px; | ||||
| } | ||||
| 
 | ||||
| .charts-container { | ||||
|   flex: 2; | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|   align-items: center; | ||||
|   justify-content: center; | ||||
|   flex-wrap: wrap; | ||||
|   gap: 24px; | ||||
| } | ||||
| 
 | ||||
| .disk-usage { | ||||
|   background-color: white; | ||||
|   padding: 15px; | ||||
|   border-radius: 8px; | ||||
|   justify-self: center; | ||||
|   box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1); | ||||
|   text-align: center; | ||||
|   flex: 1 1 300px; | ||||
|   padding: 16px; | ||||
|   background: #f9f9f9; | ||||
|   border-radius: 12px; | ||||
|   box-shadow: 0 2px 6px rgba(0,0,0,0.05); | ||||
| } | ||||
| 
 | ||||
| .chart { | ||||
|   display: flex; | ||||
|   justify-content: center; | ||||
| } | ||||
|   width: 100%; | ||||
|   height: 200px; | ||||
|   margin-bottom: 12px; | ||||
| } | ||||
|  |  | |||
|  | @ -1,64 +1,64 @@ | |||
| <app-loading [isLoading]="loading"></app-loading> | ||||
| <mat-dialog-content class="modal-content"> | ||||
|   <app-loading [isLoading]="loading"></app-loading> | ||||
| 
 | ||||
| <div class="client-container"> | ||||
|   <div class="header-container"> | ||||
|     <h2 class="title">{{ 'clientDetailsTitle' | translate }} {{ clientData.name }}</h2> | ||||
|   </div> | ||||
|   <div class="client-container" *ngIf="!loading"> | ||||
|     <div class="header-container"> | ||||
|       <h2 class="title">{{ 'clientDetailsTitle' | translate }} {{ clientData.name }}</h2> | ||||
|     </div> | ||||
| 
 | ||||
|   <mat-divider></mat-divider> | ||||
| 
 | ||||
|   <div *ngIf="!loading" class="client-info"> | ||||
|     <div class="info-section"> | ||||
|       <div class="two-column-table"> | ||||
|         <div class="table-row" *ngFor="let clientData of generalData"> | ||||
|           <div class="column property">{{ clientData?.property }}</div> | ||||
|           <div class="column value">{{ clientData?.value }}</div> | ||||
|     <div class="client-info-card"> | ||||
|       <div class="info-columns"> | ||||
|         <div class="info-column"> | ||||
|           <div class="info-pair" *ngFor="let data of generalData"> | ||||
|             <div class="label">{{ data?.property }}</div> | ||||
|             <div class="value">{{ data?.value || '--' }}</div> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|       <div class="two-column-table"> | ||||
|         <div class="table-row" *ngFor="let clientData of networkData"> | ||||
|           <div class="column property">{{ clientData?.property }}</div> | ||||
|           <div class="column value">{{ clientData?.value }}</div> | ||||
|         <div class="info-column"> | ||||
|           <div class="info-pair" *ngFor="let data of networkData"> | ||||
|             <div class="label">{{ data?.property }}</div> | ||||
|             <div class="value">{{ data?.value || '--' }}</div> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
|   <div class="header-container"> | ||||
|     <h2 class="title" i18n="@@adminImagesTitle">Discos/Particiones</h2> | ||||
|   </div> | ||||
| 
 | ||||
|   <div class="disk-container"> | ||||
|     <div class="table-container"> | ||||
|       <table mat-table [dataSource]="dataSource" class="mat-elevation-z8"> | ||||
|         <ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef"> | ||||
|           <th mat-header-cell *matHeaderCellDef> {{ column.header }} </th> | ||||
|           <td mat-cell *matCellDef="let image"> | ||||
|             <ng-container *ngIf="column.columnDef !== 'size'"> | ||||
|               {{ column.cell(image) }} | ||||
|             </ng-container> | ||||
|             <ng-container *ngIf="column.columnDef === 'size'"> | ||||
|               <mat-chip color="primary"> | ||||
|                 {{ (image.size / 1024).toFixed(2) }} GB | ||||
|               </mat-chip> | ||||
|             </ng-container> | ||||
|           </td> | ||||
|     <div class="section-header"> | ||||
|       <h2 class="title" i18n="@@adminImagesTitle">Discos/Particiones</h2> | ||||
|     </div> | ||||
| 
 | ||||
|     <div class="disk-container"> | ||||
|       <div class="table-container"> | ||||
|         <table mat-table [dataSource]="dataSource" class="mat-elevation-z8"> | ||||
|           <ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef"> | ||||
|             <th mat-header-cell *matHeaderCellDef> {{ column.header }} </th> | ||||
|             <td mat-cell *matCellDef="let image"> | ||||
|               <ng-container *ngIf="column.columnDef !== 'size'"> | ||||
|                 {{ column.cell(image) }} | ||||
|               </ng-container> | ||||
|               <ng-container *ngIf="column.columnDef === 'size'"> | ||||
|                 <mat-chip color="primary"> | ||||
|                   {{ (image.size / 1024).toFixed(2) }} GB | ||||
|                 </mat-chip> | ||||
|               </ng-container> | ||||
|             </td> | ||||
|           </ng-container> | ||||
|           <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> | ||||
|           <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr> | ||||
|         </table> | ||||
|       </div> | ||||
| 
 | ||||
|       <div class="charts-container"> | ||||
|         <ng-container *ngIf="diskUsageData && diskUsageData.length > 0"> | ||||
|           <div *ngFor="let disk of chartDisk" class="disk-usage"> | ||||
|             <ngx-charts-pie-chart class="chart" [view]="view" [results]="disk.chartData" [doughnut]="true"> | ||||
|             </ngx-charts-pie-chart> | ||||
|             <h3>Disco {{ disk.diskNumber }}</h3> | ||||
|             <p>Usado: {{ (disk.used).toFixed(2) }} GB ({{ disk.percentage }}%)</p> | ||||
|             <p>Total: {{ disk.total }} GB</p> | ||||
|           </div> | ||||
|         </ng-container> | ||||
|         <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> | ||||
|         <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr> | ||||
|       </table> | ||||
|     </div> | ||||
| 
 | ||||
|     <div class="charts-container"> | ||||
|       <ng-container *ngIf="diskUsageData && diskUsageData.length > 0"> | ||||
|         <div *ngFor="let disk of chartDisk" class="disk-usage"> | ||||
|           <ngx-charts-pie-chart class="chart" [view]="view" [results]="disk.chartData" [doughnut]="true"> | ||||
|           </ngx-charts-pie-chart> | ||||
| 
 | ||||
|           <h3>Disco {{ disk.diskNumber }}</h3> | ||||
|           <p>Usado: {{ (disk.used).toFixed(2) }} GB ({{ disk.percentage }}%)</p> | ||||
|           <p>Total: {{ disk.total }} GB</p> | ||||
|         </div> | ||||
|       </ng-container> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| </div> | ||||
| </mat-dialog-content> | ||||
|  | @ -198,7 +198,6 @@ export class ClientDetailsComponent { | |||
|     }); | ||||
| 
 | ||||
|     this.diskUsageData = this.chartDisk; | ||||
|     console.log(this.chartDisk) | ||||
|     this.isDiskUsageEmpty = this.diskUsageData.length === 0; | ||||
|   } | ||||
| 
 | ||||
|  | @ -216,11 +215,9 @@ export class ClientDetailsComponent { | |||
| 
 | ||||
|     this.http.get<any>(`${this.baseUrl}/partitions?client.id=${this.clientData.id}&order[diskNumber, partitionNumber]=ASC`).subscribe({ | ||||
|       next: data => { | ||||
|         console.log(data) | ||||
|         const filteredPartitions = data['hydra:member'].filter((partition: any) => partition.partitionNumber !== 0); | ||||
|         this.dataSource = filteredPartitions; | ||||
|         this.partitions = filteredPartitions; | ||||
|         console.log(this.partitions) | ||||
|         this.calculateDiskUsage(); | ||||
|       }, | ||||
|       error: error => { | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue