Merge branch 'develop' of ssh://ognproject.evlt.uma.es:21987/opengnsys/oggui into develop
commit
31e8e0c3c7
|
@ -69,7 +69,7 @@
|
|||
"development": {
|
||||
"optimization": false,
|
||||
"extractLicenses": false,
|
||||
"sourceMap": true
|
||||
"sourceMap": false
|
||||
},
|
||||
"es": {
|
||||
"localize": [
|
||||
|
|
|
@ -1,158 +1,177 @@
|
|||
.client-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
.client-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.client-icon {
|
||||
flex-shrink: 0;
|
||||
margin-right: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 120px;
|
||||
min-height: 120px;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.info-section {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.two-column-table {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 10px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.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 {
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.info-section h2 {
|
||||
font-size: 1.4rem;
|
||||
margin-bottom: 10px;
|
||||
color: #0056b3;
|
||||
}
|
||||
|
||||
.info-section p {
|
||||
font-size: 1rem;
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.disk-usage {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.circular-chart {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
max-width: 100%;
|
||||
max-height: 150px;
|
||||
}
|
||||
|
||||
.circle-bg {
|
||||
fill: none;
|
||||
stroke: #eee;
|
||||
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.5rem;
|
||||
text-anchor: middle;
|
||||
}
|
||||
.client-icon {
|
||||
flex-shrink: 0;
|
||||
margin-right: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 120px;
|
||||
min-height: 120px;
|
||||
}
|
||||
|
||||
.assistants-container{
|
||||
background-color: #fff;
|
||||
margin-top: 10px;
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.icon-pc {
|
||||
font-size: 25px;
|
||||
color: #3b82f6;
|
||||
}
|
||||
|
||||
.buttons-row{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
@keyframes progress {
|
||||
0% {
|
||||
stroke-dasharray: 0, 100;
|
||||
}
|
||||
.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;
|
||||
}
|
||||
|
||||
.info-section {
|
||||
margin-bottom: 30px;
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.two-column-table {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 10px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.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;
|
||||
grid-template-columns: 1fr 3fr;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.buttons-row {
|
||||
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;
|
||||
}
|
||||
|
||||
.buttons-row button {
|
||||
margin-bottom: 10px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.disk-usage {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
margin-bottom: 20px;
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.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: #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);
|
||||
}
|
||||
|
|
|
@ -27,35 +27,41 @@
|
|||
</mat-tab>
|
||||
</mat-tab-group>
|
||||
</div>
|
||||
<div class="info-section">
|
||||
<h2 *ngIf="isDiskUsageVisible">Disco</h2>
|
||||
<div class="disk-usage" *ngIf="isDiskUsageVisible">
|
||||
<div class="chart-container">
|
||||
<svg viewBox="0 0 36 36" class="circular-chart green">
|
||||
<path class="circle-bg"
|
||||
d="M18 2.0845
|
||||
a 15.9155 15.9155 0 0 1 0 31.831
|
||||
a 15.9155 15.9155 0 0 1 0 -31.831" />
|
||||
<path class="circle"
|
||||
stroke-dasharray="75, 100"
|
||||
d="M18 2.0845
|
||||
a 15.9155 15.9155 0 0 1 0 31.831
|
||||
a 15.9155 15.9155 0 0 1 0 -31.831" />
|
||||
<text x="18" y="20.35" class="percentage">75%</text>
|
||||
</svg>
|
||||
</div>
|
||||
<p>Usado: 75% (375GB)</p>
|
||||
<p>Total: 500GB</p>
|
||||
|
||||
<div class="second-section">
|
||||
<div class="buttons-row">
|
||||
<button mat-flat-button color="primary" (click)="togglePartitionAssistant()">Asistente a particionado</button>
|
||||
<button mat-flat-button color="primary" (click)="showBootImage()">Restaurar imagen</button>
|
||||
</div>
|
||||
|
||||
<div class="buttons-row">
|
||||
<button mat-flat-button color="primary" (click)="togglePartitionAssistant()">Asistente a particionado</button>
|
||||
<button mat-flat-button color="primary" (click)="showBootImage()">Restaurar imagen</button>
|
||||
</div>
|
||||
<div class="info-section">
|
||||
<h2 *ngIf="isDiskUsageVisible">Disco</h2>
|
||||
<ng-container *ngIf="diskUsageData && diskUsageData.length > 0">
|
||||
<div *ngFor="let disk of diskUsageData" class="disk-usage">
|
||||
<h3>Disco {{ disk.diskNumber }}</h3>
|
||||
<div class="chart-container">
|
||||
<svg viewBox="0 0 36 36" class="circular-chart green">
|
||||
<path class="circle-bg"
|
||||
d="M18 2.0845
|
||||
a 15.9155 15.9155 0 0 1 0 31.831
|
||||
a 15.9155 15.9155 0 0 1 0 -31.831" />
|
||||
<path class="circle"
|
||||
[attr.stroke-dasharray]="disk.percentage + ', 100'"
|
||||
d="M18 2.0845
|
||||
a 15.9155 15.9155 0 0 1 0 31.831
|
||||
a 15.9155 15.9155 0 0 1 0 -31.831" />
|
||||
<text x="18" y="20.35" class="percentage">{{ disk.percentage }}%</text>
|
||||
</svg>
|
||||
</div>
|
||||
<p>Usado: {{ disk.used }} GB ({{ disk.percentage }}%)</p>
|
||||
<p>Total: {{ disk.total }} GB</p>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="assistants-container" *ngIf="isPartitionAssistantVisible">
|
||||
<app-partition-assistant [data]="clientData"></app-partition-assistant>
|
||||
</div>
|
||||
|
|
|
@ -16,14 +16,12 @@ export class ClientMainViewComponent implements OnInit {
|
|||
clientData: any = {};
|
||||
isPartitionAssistantVisible: boolean = false;
|
||||
isBootImageVisible: boolean = false;
|
||||
isGeneralInfoVisible: boolean = true;
|
||||
isDiskUsageVisible: boolean = true;
|
||||
|
||||
displayedColumns: string[] = ['property', 'value'];
|
||||
|
||||
generalData: ClientInfo[] = [];
|
||||
networkData: ClientInfo[] = [];
|
||||
classroomData: ClientInfo[] = [];
|
||||
diskUsageData: any[] = [];
|
||||
|
||||
constructor(private http: HttpClient) {
|
||||
const url = window.location.href;
|
||||
|
@ -32,10 +30,11 @@ export class ClientMainViewComponent implements OnInit {
|
|||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.clientData = history.state.clientData
|
||||
this.clientData = history.state.clientData;
|
||||
this.updateGeneralData();
|
||||
this.updateNetworkData();
|
||||
this.updateClassroomData();
|
||||
this.calculateDiskUsage();
|
||||
}
|
||||
|
||||
updateGeneralData() {
|
||||
|
@ -82,6 +81,29 @@ export class ClientMainViewComponent implements OnInit {
|
|||
];
|
||||
}
|
||||
|
||||
calculateDiskUsage() {
|
||||
const diskUsageMap = new Map<number, { total: number, used: number }>();
|
||||
|
||||
this.clientData.partitions.forEach((partition: any) => {
|
||||
const diskNumber = partition.diskNumber;
|
||||
if (!diskUsageMap.has(diskNumber)) {
|
||||
diskUsageMap.set(diskNumber, { total: 0, used: 0 });
|
||||
}
|
||||
|
||||
const diskData = diskUsageMap.get(diskNumber);
|
||||
if (partition.partitionNumber === 0) {
|
||||
diskData!.total = partition.size / (1024 * 1024);
|
||||
} else {
|
||||
diskData!.used += partition.size / (1024 * 1024);
|
||||
}
|
||||
});
|
||||
|
||||
this.diskUsageData = Array.from(diskUsageMap.entries()).map(([diskNumber, { total, used }]) => {
|
||||
const percentage = total > 0 ? Math.round((used / total) * 100) : 0;
|
||||
return { diskNumber, total, used, percentage };
|
||||
});
|
||||
}
|
||||
|
||||
togglePartitionAssistant() {
|
||||
this.isPartitionAssistantVisible = !this.isPartitionAssistantVisible;
|
||||
this.isBootImageVisible = false;
|
||||
|
|
|
@ -1,53 +1,134 @@
|
|||
.partition-assistant {
|
||||
font-family: Arial, sans-serif;
|
||||
padding: 10px;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
background-color: #f9f9f9;
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
margin: 20px auto;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 15px;
|
||||
padding: 10px;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.header label {
|
||||
font-weight: 500;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.disk-size {
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.partition-bar {
|
||||
display: flex;
|
||||
margin: 10px 0;
|
||||
height: 30px;
|
||||
border: 1px solid #000000;
|
||||
margin: 20px 0;
|
||||
height: 40px;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
background-color: #e0e0e0;
|
||||
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.partition-segment {
|
||||
text-align: center;
|
||||
color: white;
|
||||
line-height: 30px;
|
||||
line-height: 40px;
|
||||
font-weight: 500;
|
||||
font-size: 0.9rem;
|
||||
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.partition-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-bottom: 10px;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
|
||||
overflow: hidden;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.partition-table th, .partition-table td {
|
||||
border: 1px solid #ccc;
|
||||
padding: 5px;
|
||||
.partition-table th {
|
||||
background-color: #f5f5f5;
|
||||
color: #333;
|
||||
padding: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.partition-table td {
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.partition-table select,
|
||||
.partition-table input[type="number"],
|
||||
.partition-table input[type="checkbox"] {
|
||||
padding: 5px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #ccc;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.remove-btn {
|
||||
background-color: red;
|
||||
color: white;
|
||||
button {
|
||||
border: none;
|
||||
padding: 5px;
|
||||
padding: 10px 20px;
|
||||
border-radius: 4px;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
button.mat-button {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
}
|
||||
|
||||
button.mat-button:hover {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
|
||||
button.mat-flat-button {
|
||||
background-color: #28a745;
|
||||
color: white;
|
||||
}
|
||||
|
||||
button.mat-flat-button:hover {
|
||||
background-color: #218838;
|
||||
}
|
||||
|
||||
button.remove-btn {
|
||||
background-color: #dc3545;
|
||||
color: white;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
button.remove-btn:hover {
|
||||
background-color: #c82333;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
color: red;
|
||||
color: #dc3545;
|
||||
font-size: 0.9rem;
|
||||
padding: 10px;
|
||||
background-color: #f8d7da;
|
||||
border-radius: 4px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +1,12 @@
|
|||
<div class="partition-assistant">
|
||||
<div class="partition-assistant" *ngFor="let disk of disks; let i = index">
|
||||
<div class="header">
|
||||
<label for="disk-number">Num. disco:</label>
|
||||
<input id="disk-number" type="number" [(ngModel)]="diskNumber" />
|
||||
<label for="partition-table">Tabla particiones:</label>
|
||||
<select id="partition-table" [(ngModel)]="partitionTable">
|
||||
<option value="MSDOS">MSDOS</option>
|
||||
<option value="GPT">GPT</option>
|
||||
</select>
|
||||
<span class="disk-size">Tamaño: {{ totalDiskSize }} GB</span>
|
||||
<label for="disk-number-{{i}}">Disco {{ disk.diskNumber }}:</label>
|
||||
<span class="disk-size">Tamaño: {{ disk.totalDiskSize }} GB</span>
|
||||
</div>
|
||||
|
||||
<div class="partition-bar">
|
||||
<div
|
||||
*ngFor="let partition of partitions"
|
||||
*ngFor="let partition of disk.partitions"
|
||||
[ngStyle]="{'width': partition.percentage + '%', 'background-color': partition.color}"
|
||||
class="partition-segment"
|
||||
>
|
||||
|
@ -20,7 +14,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<button mat-button (click)="addPartition()">Añadir +</button>
|
||||
<button mat-button (click)="addPartition(disk.diskNumber)">Añadir +</button>
|
||||
|
||||
<table class="partition-table">
|
||||
<thead>
|
||||
|
@ -33,8 +27,8 @@
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let partition of partitions; let i = index">
|
||||
<td>{{ i + 1 }}</td>
|
||||
<tr *ngFor="let partition of disk.partitions; let j = index">
|
||||
<td>{{ j + 1 }}</td>
|
||||
<td>
|
||||
<select [(ngModel)]="partition.type">
|
||||
<option value="NTFS">NTFS</option>
|
||||
|
@ -46,21 +40,22 @@
|
|||
<input
|
||||
type="number"
|
||||
[(ngModel)]="partition.size"
|
||||
(input)="updatePartitionSize(i, 'gb')"
|
||||
(input)="updatePartitionSize(disk.diskNumber, j, partition.size)"
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<input type="checkbox" [(ngModel)]="partition.format" />
|
||||
</td>
|
||||
<td>
|
||||
<button (click)="removePartition(i)" class="remove-btn">X</button>
|
||||
<button (click)="removePartition(disk.diskNumber, j)" class="remove-btn">X</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div *ngIf="errorMessage" class="error-message">{{ errorMessage }}</div>
|
||||
|
||||
<div class="actions">
|
||||
<button mat-flat-button color="primary" (click)="save()">Guardar</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="errorMessage" class="error-message">{{ errorMessage }}</div>
|
||||
|
||||
<div class="actions">
|
||||
<button mat-flat-button color="primary" (click)="save()">Guardar</button>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { Component, Input } from '@angular/core';
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
|
||||
interface Partition {
|
||||
size: number;
|
||||
|
@ -14,98 +15,176 @@ interface Partition {
|
|||
templateUrl: './partition-assistant.component.html',
|
||||
styleUrls: ['./partition-assistant.component.css']
|
||||
})
|
||||
export class PartitionAssistantComponent {
|
||||
export class PartitionAssistantComponent implements OnInit {
|
||||
@Input() data: any;
|
||||
diskNumber = 1;
|
||||
partitionTable = 'MSDOS';
|
||||
totalDiskSize = 100;
|
||||
@Input() clientUuid: string | undefined; // El clientUuid que necesitas
|
||||
|
||||
errorMessage = '';
|
||||
originalPartitions: any[] = [];
|
||||
disks: { diskNumber: number, totalDiskSize: number, partitions: Partition[] }[] = [];
|
||||
|
||||
private apiUrl = 'http://127.0.0.1:8001/partitions';
|
||||
|
||||
ngOnInit() { console.log('Data:', this.data); }
|
||||
constructor(private http: HttpClient) {}
|
||||
|
||||
partitions: Partition[] = [
|
||||
{ size: 17.96, type: 'NTFS', sizeBytes: 17.96 * 1024 * 1024 * 1024, format: false, color: '#00a2e8', percentage: 17.96 },
|
||||
{ size: 17.29, type: 'LINUX', sizeBytes: 17.29 * 1024 * 1024 * 1024, format: false, color: '#6a5acd', percentage: 17.29 },
|
||||
{ size: 12.64, type: 'CACHE', sizeBytes: 12.64 * 1024 * 1024 * 1024, format: false, color: '#ff0000', percentage: 12.64 }
|
||||
];
|
||||
|
||||
addPartition() {
|
||||
const remainingGB = this.getRemainingGB();
|
||||
if (remainingGB > 0) {
|
||||
this.partitions.push({
|
||||
size: 0,
|
||||
type: 'NTFS',
|
||||
sizeBytes: 0,
|
||||
format: false,
|
||||
color: '#' + Math.floor(Math.random() * 16777215).toString(16),
|
||||
percentage: 0
|
||||
});
|
||||
} else {
|
||||
this.errorMessage = 'No hay suficiente espacio libre en el disco para crear una nueva partición.';
|
||||
}
|
||||
ngOnInit() {
|
||||
this.initializeDisks();
|
||||
}
|
||||
|
||||
removePartition(index: number) {
|
||||
this.partitions.splice(index, 1);
|
||||
this.updatePartitionPercentages();
|
||||
}
|
||||
initializeDisks() {
|
||||
const partitionsFromData = this.data.partitions;
|
||||
this.originalPartitions = JSON.parse(JSON.stringify(partitionsFromData)); // Guardar una copia de las particiones originales
|
||||
|
||||
updatePartitionSize(index: number, type: 'gb') {
|
||||
const partition = this.partitions[index];
|
||||
const remainingGB = this.getRemainingGB() + partition.size;
|
||||
|
||||
if (partition.size > remainingGB) {
|
||||
this.errorMessage = `El tamaño de la partición no puede superar el espacio libre (${remainingGB.toFixed(2)} GB).`;
|
||||
partition.size = 0;
|
||||
} else {
|
||||
this.errorMessage = '';
|
||||
partition.sizeBytes = partition.size * 1024 * 1024 * 1024;
|
||||
this.updatePartitionPercentages();
|
||||
}
|
||||
}
|
||||
const disksMap = new Map<number, { totalDiskSize: number, partitions: Partition[] }>();
|
||||
|
||||
updatePartitionPercentages() {
|
||||
const totalUsedGB = this.partitions.reduce((acc, partition) => acc + partition.size, 0);
|
||||
const remainingGB = this.totalDiskSize - totalUsedGB;
|
||||
partitionsFromData.forEach((partition: any) => {
|
||||
if (!disksMap.has(partition.diskNumber)) {
|
||||
disksMap.set(partition.diskNumber, { totalDiskSize: 0, partitions: [] });
|
||||
}
|
||||
|
||||
this.partitions.forEach(partition => {
|
||||
partition.percentage = (partition.size / this.totalDiskSize) * 100;
|
||||
const disk = disksMap.get(partition.diskNumber);
|
||||
if (partition.partitionNumber === 0) {
|
||||
disk!.totalDiskSize = this.convertBytesToGB(partition.size);
|
||||
} else {
|
||||
disk!.partitions.push({
|
||||
size: this.convertBytesToGB(partition.size),
|
||||
type: 'NTFS', // Puedes cambiar el tipo según sea necesario
|
||||
sizeBytes: partition.size,
|
||||
format: false,
|
||||
color: '#' + Math.floor(Math.random() * 16777215).toString(16),
|
||||
percentage: 0
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (remainingGB > 0) {
|
||||
const freeSpaceIndex = this.partitions.findIndex(partition => partition.type === 'LIBRE');
|
||||
|
||||
if (freeSpaceIndex > -1) {
|
||||
this.partitions[freeSpaceIndex].size = remainingGB;
|
||||
this.partitions[freeSpaceIndex].percentage = (remainingGB / this.totalDiskSize) * 100;
|
||||
}
|
||||
}
|
||||
disksMap.forEach((disk, diskNumber) => {
|
||||
this.updatePartitionPercentages(disk.partitions, disk.totalDiskSize);
|
||||
this.disks.push({
|
||||
diskNumber: diskNumber,
|
||||
totalDiskSize: disk.totalDiskSize,
|
||||
partitions: disk.partitions
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getRemainingGB(): number {
|
||||
const totalUsedGB = this.partitions.reduce((acc, partition) => acc + partition.size, 0);
|
||||
return Math.max(0, this.totalDiskSize - totalUsedGB);
|
||||
convertBytesToGB(bytes: number): number {
|
||||
return bytes / (1024 * 1024 * 1024);
|
||||
}
|
||||
|
||||
updatePartitionPercentages(partitions: Partition[], totalDiskSize: number) {
|
||||
partitions.forEach(partition => {
|
||||
partition.percentage = (partition.size / totalDiskSize) * 100;
|
||||
});
|
||||
}
|
||||
|
||||
// Añadir una nueva partición
|
||||
addPartition(diskNumber: number) {
|
||||
const disk = this.disks.find(d => d.diskNumber === diskNumber);
|
||||
|
||||
if (disk) {
|
||||
const remainingGB = this.getRemainingGB(disk.partitions, disk.totalDiskSize);
|
||||
if (remainingGB > 0) {
|
||||
disk.partitions.push({
|
||||
size: 0,
|
||||
type: 'NTFS', // Tipo por defecto, puede ser cambiado por el usuario
|
||||
sizeBytes: 0,
|
||||
format: false,
|
||||
color: '#' + Math.floor(Math.random() * 16777215).toString(16), // Color aleatorio
|
||||
percentage: 0
|
||||
});
|
||||
this.updatePartitionPercentages(disk.partitions, disk.totalDiskSize);
|
||||
} else {
|
||||
this.errorMessage = 'No hay suficiente espacio libre en el disco para crear una nueva partición.';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Actualizar el tamaño de una partición
|
||||
updatePartitionSize(diskNumber: number, index: number, size: number) {
|
||||
const disk = this.disks.find(d => d.diskNumber === diskNumber);
|
||||
if (disk) {
|
||||
const partition = disk.partitions[index];
|
||||
const remainingGB = this.getRemainingGB(disk.partitions, disk.totalDiskSize) + partition.size;
|
||||
|
||||
if (size > remainingGB) {
|
||||
this.errorMessage = `El tamaño de la partición no puede superar el espacio libre (${remainingGB.toFixed(2)} GB).`;
|
||||
} else {
|
||||
this.errorMessage = '';
|
||||
partition.size = size;
|
||||
partition.sizeBytes = size * 1024 * 1024 * 1024;
|
||||
this.updatePartitionPercentages(disk.partitions, disk.totalDiskSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getRemainingGB(partitions: Partition[], totalDiskSize: number): number {
|
||||
const totalUsedGB = partitions.reduce((acc, partition) => acc + partition.size, 0);
|
||||
return Math.max(0, totalDiskSize - totalUsedGB);
|
||||
}
|
||||
|
||||
isPartitionModified(original: any, current: Partition): boolean {
|
||||
return (
|
||||
this.convertBytesToGB(original.size) !== current.size ||
|
||||
original.type !== current.type
|
||||
);
|
||||
}
|
||||
|
||||
getModifiedOrNewPartitions() {
|
||||
const modifiedPartitions: any[] = [];
|
||||
|
||||
this.disks.forEach(disk => {
|
||||
disk.partitions.forEach((partition, index) => {
|
||||
const originalPartition = this.originalPartitions.find(
|
||||
p => p.diskNumber === disk.diskNumber && p.partitionNumber === index + 1
|
||||
);
|
||||
|
||||
// Si no existe en las originales, es nueva
|
||||
if (!originalPartition) {
|
||||
modifiedPartitions.push({ partition, diskNumber: disk.diskNumber, partitionNumber: index + 1 });
|
||||
} else if (this.isPartitionModified(originalPartition, partition)) {
|
||||
modifiedPartitions.push({ partition, diskNumber: disk.diskNumber, partitionNumber: index + 1 });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return modifiedPartitions;
|
||||
}
|
||||
|
||||
save() {
|
||||
const totalSize = this.partitions.reduce((acc, partition) => acc + partition.size, 0);
|
||||
|
||||
if (totalSize > this.totalDiskSize) {
|
||||
this.errorMessage = `El tamaño total de las particiones (${totalSize} GB) supera el tamaño total del disco (${this.totalDiskSize} GB).`;
|
||||
const modifiedPartitions = this.getModifiedOrNewPartitions();
|
||||
|
||||
if (modifiedPartitions.length === 0) {
|
||||
this.errorMessage = 'No hay cambios para guardar.';
|
||||
return;
|
||||
} else {
|
||||
this.errorMessage = '';
|
||||
}
|
||||
|
||||
const payload = this.partitions.map((partition, index) => ({
|
||||
diskNumber: this.diskNumber,
|
||||
partitionNumber: index + 1,
|
||||
size: partition.size,
|
||||
filesystem: partition.type,
|
||||
format: partition.format
|
||||
}));
|
||||
|
||||
console.log('Payload:', JSON.stringify(payload, null, 2));
|
||||
|
||||
modifiedPartitions.forEach(({ partition, diskNumber, partitionNumber }) => {
|
||||
const payload = {
|
||||
diskNumber: diskNumber,
|
||||
partitionNumber: partitionNumber,
|
||||
size: partition.size,
|
||||
filesystem: partition.type,
|
||||
client: `https://example.com/${this.clientUuid}`
|
||||
};
|
||||
|
||||
this.http.post(this.apiUrl, payload).subscribe(
|
||||
response => {
|
||||
console.log('Partición guardada exitosamente:', response);
|
||||
},
|
||||
error => {
|
||||
console.error('Error al guardar la partición:', error);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// Eliminar partición de un disco
|
||||
removePartition(diskNumber: number, index: number) {
|
||||
const disk = this.disks.find(d => d.diskNumber === diskNumber);
|
||||
|
||||
if (disk) {
|
||||
disk.partitions.splice(index, 1);
|
||||
this.updatePartitionPercentages(disk.partitions, disk.totalDiskSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
.dialog-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px; /* Espacio entre los elementos del formulario */
|
||||
}
|
||||
|
||||
.image-form {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.form-field {
|
||||
width: 100%;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
/* Botones alineados al final, con margen superior */
|
||||
.dialog-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
button {
|
||||
margin-left: 8px; /* Espacio entre los botones */
|
||||
}
|
||||
|
||||
/* Responsividad para pantallas pequeñas */
|
||||
@media (max-width: 600px) {
|
||||
.form-field {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Alineación vertical para botones en pantallas pequeñas */
|
||||
.dialog-actions {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
button {
|
||||
width: 100%;
|
||||
margin-left: 0;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,29 +1,30 @@
|
|||
<h2 mat-dialog-title>Añadir nueva imagen</h2>
|
||||
<mat-dialog-content>
|
||||
<form>
|
||||
<mat-form-field appearance="fill">
|
||||
|
||||
<mat-dialog-content class="dialog-content">
|
||||
<form class="image-form">
|
||||
<mat-form-field appearance="fill" class="form-field">
|
||||
<mat-label>Nombre de la imagen</mat-label>
|
||||
<input matInput [(ngModel)]="imagePayload.name" name="name" required>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-form-field appearance="fill" class="form-field">
|
||||
<mat-label>Descripción</mat-label>
|
||||
<input matInput [(ngModel)]="imagePayload.description" name="description">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-form-field appearance="fill" class="form-field">
|
||||
<mat-label>Comentarios</mat-label>
|
||||
<input matInput [(ngModel)]="imagePayload.comments" name="comments">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-form-field appearance="fill" class="form-field">
|
||||
<mat-label>Perfil de software</mat-label>
|
||||
<input matInput [(ngModel)]="imagePayload.softwareProfile" name="softwareProfile">
|
||||
</mat-form-field>
|
||||
</form>
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions>
|
||||
<mat-dialog-actions align="end" class="dialog-actions">
|
||||
<button mat-button (click)="close()">Cancelar</button>
|
||||
<button mat-button color="primary" (click)="saveImage()">Guardar</button>
|
||||
</mat-dialog-actions>
|
||||
|
|
Loading…
Reference in New Issue