ref 3007. Software profile in versioned image
parent
24517a8649
commit
9797b14038
|
|
@ -32,15 +32,6 @@
|
|||
<input matInput formControlName="comments" name="comments">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="fill" class="form-field">
|
||||
<mat-label>{{ 'softwareProfile' | translate }}</mat-label>
|
||||
<mat-select formControlName="softwareProfile">
|
||||
<mat-option *ngFor="let profile of softwareProfiles" [value]="profile['@id']">
|
||||
{{ profile.description }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-checkbox formControlName="remotePc">
|
||||
{{ 'labelRemotePc' | translate }}
|
||||
</mat-checkbox>
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ export class CreateImageComponent implements OnInit {
|
|||
baseUrl: string;
|
||||
imageForm: FormGroup<any>;
|
||||
imageId: string | null = null;
|
||||
softwareProfiles: any[] = [];
|
||||
repositories: any[] = [];
|
||||
loading: boolean = false;
|
||||
isEditMode: boolean = false;
|
||||
|
|
@ -41,7 +40,6 @@ export class CreateImageComponent implements OnInit {
|
|||
comments: [''],
|
||||
remotePc: [false],
|
||||
isGlobal: [false],
|
||||
softwareProfile: [''],
|
||||
imageRepositories: [[], Validators.required],
|
||||
});
|
||||
}
|
||||
|
|
@ -54,7 +52,6 @@ export class CreateImageComponent implements OnInit {
|
|||
} else {
|
||||
this.loading = false;
|
||||
}
|
||||
this.fetchSoftwareProfiles();
|
||||
this.fetchRepositories();
|
||||
}
|
||||
|
||||
|
|
@ -67,7 +64,6 @@ export class CreateImageComponent implements OnInit {
|
|||
comments: response.comments,
|
||||
remotePc: response.remotePc,
|
||||
isGlobal: response.isGlobal,
|
||||
softwareProfile: response.softwareProfile ? response.softwareProfile['@id'] : null,
|
||||
imageRepositories: response.imageRepositories ? response.imageRepositories.map((r: any) => r.imageRepository['@id']) : [],
|
||||
});
|
||||
this.imageId = response['@id'];
|
||||
|
|
@ -85,18 +81,6 @@ export class CreateImageComponent implements OnInit {
|
|||
this.showWarning = this.imageForm.get('isGlobal')?.value;
|
||||
}
|
||||
|
||||
fetchSoftwareProfiles() {
|
||||
const url = `${this.baseUrl}/software-profiles`;
|
||||
this.http.get(url).subscribe({
|
||||
next: (response: any) => {
|
||||
this.softwareProfiles = response['hydra:member'];
|
||||
},
|
||||
error: (error) => {
|
||||
this.toastService.error(this.translate.instant('errorGettingProfiles'));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fetchRepositories() {
|
||||
const url = `${this.baseUrl}/image-repositories`;
|
||||
this.http.get(url).subscribe({
|
||||
|
|
@ -117,7 +101,6 @@ export class CreateImageComponent implements OnInit {
|
|||
remotePc: this.imageForm.value.remotePc,
|
||||
isGlobal: this.imageForm.value.isGlobal,
|
||||
imageRepositories: this.imageForm.value.imageRepositories,
|
||||
...(this.imageForm.value.softwareProfile ? { softwareProfile: this.imageForm.value.softwareProfile } : {}),
|
||||
};
|
||||
|
||||
if (this.imageId) {
|
||||
|
|
|
|||
|
|
@ -108,3 +108,36 @@ table {
|
|||
margin: 0 auto;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.software-profile-select {
|
||||
width: 150px;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
.software-profile-select ::ng-deep .mat-mdc-text-field-wrapper {
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
.software-profile-select ::ng-deep .mat-mdc-form-field-infix {
|
||||
min-height: unset;
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.software-profile-caret {
|
||||
cursor: pointer;
|
||||
color: rgba(0, 0, 0, 0.54);
|
||||
font-size: 20px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
::ng-deep .software-profile-panel {
|
||||
max-width: 320px;
|
||||
}
|
||||
|
||||
::ng-deep .software-profile-panel .mat-mdc-option {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,8 +83,25 @@
|
|||
{{ getStatusLabel(image[column.columnDef]) }}
|
||||
</mat-chip>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="column.columnDef === 'softwareProfile'">
|
||||
<mat-form-field appearance="outline" class="software-profile-select" subscriptSizing="dynamic">
|
||||
<input matInput #profileInput="matAutocompleteTrigger" [ngModel]="image.profileValue"
|
||||
(ngModelChange)="image.profileValue = $event" [matAutocomplete]="profileAuto"
|
||||
(blur)="resetProfileSearchIfInvalid(image)" [placeholder]="'noSoftwareProfileAssigned' | translate">
|
||||
<mat-icon matSuffix class="software-profile-caret" (click)="profileInput.openPanel()">arrow_drop_down</mat-icon>
|
||||
<mat-autocomplete #profileAuto="matAutocomplete" [displayWith]="displayProfile" panelWidth="auto"
|
||||
class="software-profile-panel" (optionSelected)="onProfileSelected(image, $event.option.value)">
|
||||
<mat-option [value]="null">{{ 'noSoftwareProfileAssigned' | translate }}</mat-option>
|
||||
<mat-option *ngFor="let profile of filterProfiles(image.profileValue)" [value]="profile">
|
||||
{{ profile.description }}
|
||||
</mat-option>
|
||||
</mat-autocomplete>
|
||||
</mat-form-field>
|
||||
</ng-container>
|
||||
|
||||
<ng-container
|
||||
*ngIf="column.columnDef !== 'remotePc' && column.columnDef !== 'status' && column.columnDef !== 'isGlobal' && column.columnDef !== 'name' && column.columnDef !== 'datasize'">
|
||||
*ngIf="column.columnDef !== 'remotePc' && column.columnDef !== 'status' && column.columnDef !== 'isGlobal' && column.columnDef !== 'name' && column.columnDef !== 'datasize' && column.columnDef !== 'softwareProfile'">
|
||||
{{ column.cell(image) }}
|
||||
</ng-container>
|
||||
</td>
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ export class ShowMonoliticImagesComponent implements OnInit {
|
|||
filters: { [key: string]: string } = {};
|
||||
alertMessage: string | null = null;
|
||||
repository: any = {};
|
||||
softwareProfiles: any[] = [];
|
||||
datePipe: DatePipe = new DatePipe('es-ES');
|
||||
columns = [
|
||||
{
|
||||
|
|
@ -70,9 +71,9 @@ export class ShowMonoliticImagesComponent implements OnInit {
|
|||
cell: (image: any) => this.formatSize(image.datasize)
|
||||
},
|
||||
{
|
||||
columnDef: 'description',
|
||||
header: 'labelDescription',
|
||||
cell: (image: any) => image.description
|
||||
columnDef: 'softwareProfile',
|
||||
header: 'softwareProfile',
|
||||
cell: (image: any) => image.softwareProfile ? image.softwareProfile['@id'] : null
|
||||
},
|
||||
{
|
||||
columnDef: 'createdAt',
|
||||
|
|
@ -100,13 +101,63 @@ export class ShowMonoliticImagesComponent implements OnInit {
|
|||
if (this.data) {
|
||||
this.loadData();
|
||||
}
|
||||
this.loadSoftwareProfiles();
|
||||
}
|
||||
|
||||
loadSoftwareProfiles(): void {
|
||||
this.http.get<any>(`${this.baseUrl}/software-profiles?page=1&itemsPerPage=10000`).subscribe({
|
||||
next: (response) => {
|
||||
this.softwareProfiles = response['hydra:member'];
|
||||
},
|
||||
error: (error) => {
|
||||
console.error(this.translate.instant('errorGettingProfiles'), error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
filterProfiles(value: any): any[] {
|
||||
const query = typeof value === 'string' ? value : (value?.description || '');
|
||||
const normalized = query.toLowerCase();
|
||||
return this.softwareProfiles.filter(profile => (profile.description || '').toLowerCase().includes(normalized));
|
||||
}
|
||||
|
||||
displayProfile(value: any): string {
|
||||
return value?.description || '';
|
||||
}
|
||||
|
||||
onProfileSelected(image: any, selectedProfile: any | null): void {
|
||||
const previousProfile = image.softwareProfile;
|
||||
|
||||
// Optimistic update: reflect the choice immediately so the blur/reset
|
||||
// handler (which reads image.softwareProfile) doesn't race the PATCH
|
||||
// response and briefly flash back to the old value.
|
||||
image.softwareProfile = selectedProfile;
|
||||
image.profileValue = selectedProfile;
|
||||
|
||||
this.http.patch(`${this.baseUrl}${image['@id']}`, { softwareProfile: selectedProfile ? selectedProfile['@id'] : null }).subscribe({
|
||||
next: () => {
|
||||
this.toastService.success(this.translate.instant('successfullyEditedSoftware'));
|
||||
},
|
||||
error: (error) => {
|
||||
image.softwareProfile = previousProfile;
|
||||
image.profileValue = previousProfile;
|
||||
this.toastService.error(error.error['hydra:description']);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
resetProfileSearchIfInvalid(image: any): void {
|
||||
image.profileValue = image.softwareProfile || null;
|
||||
}
|
||||
|
||||
loadData(): void {
|
||||
this.loading = true;
|
||||
this.http.get<any>(`${this.apiUrl}?page=${this.page + 1}&itemsPerPage=${this.itemsPerPage}&repository.id=${this.data.repositoryId}`, { params: this.filters }).subscribe(
|
||||
data => {
|
||||
this.dataSource.data = data['hydra:member'];
|
||||
this.dataSource.data = (data['hydra:member'] || []).map((item: any) => ({
|
||||
...item,
|
||||
profileValue: item.softwareProfile || null
|
||||
}));
|
||||
this.length = data['hydra:totalItems'];
|
||||
this.loading = false;
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in New Issue