File

projects/cobbler-frontend/src/app/items/image/overview/image-overview.component.ts

Implements

OnInit OnDestroy AfterViewInit

Metadata

Index

Properties
Methods

Methods

addImage
addImage()
Returns : void
applyFilter
applyFilter(event: Event)
Parameters :
Name Type Optional
event Event No
Returns : void
deleteImage
deleteImage(uid: string, name: string)
Parameters :
Name Type Optional
uid string No
name string No
Returns : void
ngAfterViewInit
ngAfterViewInit()
Returns : void
ngOnDestroy
ngOnDestroy()
Returns : void
ngOnInit
ngOnInit()
Returns : void
renameImage
renameImage(uid: string, name: string)
Parameters :
Name Type Optional
uid string No
name string No
Returns : void
Private retrieveImages
retrieveImages()
Returns : void
showImage
showImage(uid: string, name: string)
Parameters :
Name Type Optional
uid string No
name string No
Returns : void

Properties

Private _snackBar
Type : unknown
Default value : inject(MatSnackBar)
Private cobblerApiService
Type : unknown
Default value : inject(CobblerApiService)
dataSource
Type : unknown
Default value : new MatTableDataSource<Image>([])
Readonly dialog
Type : unknown
Default value : inject<MatDialog>(MatDialog)
displayedColumns
Type : string[]
Default value : [ 'name', 'arch', 'breed', 'os_version', 'actions', ]
filterField
Type : ElementRef<HTMLInputElement>
Decorators :
@ViewChild('input')
Private ngUnsubscribe
Type : unknown
Default value : new Subject<void>()
paginator
Type : MatPaginator
Decorators :
@ViewChild(MatPaginator)
Private route
Type : unknown
Default value : inject(ActivatedRoute)
Private router
Type : unknown
Default value : inject(Router)
sort
Type : MatSort
Decorators :
@ViewChild(MatSort)
table
Type : MatTable<Image>
Decorators :
@ViewChild(MatTable)
Private targetImage
Type : string | null
Default value : null
userService
Type : unknown
Default value : inject(UserService)
import {
  AfterViewInit,
  Component,
  ElementRef,
  OnDestroy,
  OnInit,
  ViewChild,
  inject,
} from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatDialog } from '@angular/material/dialog';
import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { MatSnackBar } from '@angular/material/snack-bar';
import {
  MatTable,
  MatTableDataSource,
  MatTableModule,
} from '@angular/material/table';
import { MatTooltipModule } from '@angular/material/tooltip';
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
import { CobblerApiService, Image } from 'cobbler-api';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { DialogItemRenameComponent } from '../../../common/dialog-item-rename/dialog-item-rename.component';
import { UserService } from '../../../services/user.service';
import Utils from '../../../utils';
import { ImageCreateComponent } from '../create/image-create.component';
import { MatPaginator, MatPaginatorModule } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { MatInputModule } from '@angular/material/input';
import { MatFormFieldModule } from '@angular/material/form-field';

@Component({
  selector: 'cobbler-image-overview',
  imports: [
    MatTableModule,
    MatIconModule,
    MatButtonModule,
    MatMenuModule,
    MatTooltipModule,
    MatPaginatorModule,
    MatSort,
    MatInputModule,
    MatFormFieldModule,
    RouterLink,
  ],
  templateUrl: './image-overview.component.html',
  styleUrl: './image-overview.component.scss',
})
export class ImageOverviewComponent
  implements OnInit, OnDestroy, AfterViewInit
{
  userService = inject(UserService);
  private cobblerApiService = inject(CobblerApiService);
  private _snackBar = inject(MatSnackBar);
  private router = inject(Router);
  readonly dialog = inject<MatDialog>(MatDialog);
  private route = inject(ActivatedRoute);
  private targetImage: string | null = null;

  // Unsubscribe
  private ngUnsubscribe = new Subject<void>();

  // Table
  displayedColumns: string[] = [
    'name',
    'arch',
    'breed',
    'os_version',
    'actions',
  ];
  dataSource = new MatTableDataSource<Image>([]);

  @ViewChild(MatTable) table!: MatTable<Image>;
  @ViewChild(MatPaginator) paginator!: MatPaginator;
  @ViewChild(MatSort) sort!: MatSort;
  @ViewChild('input') filterField!: ElementRef<HTMLInputElement>;

  ngOnInit(): void {
    this.retrieveImages();
    this.targetImage = this.route.snapshot.queryParamMap.get('image');
  }

  ngAfterViewInit(): void {
    this.dataSource.paginator = this.paginator;
    this.dataSource.sort = this.sort;

    if (this.targetImage) {
      this.filterField.nativeElement.value = this.targetImage;
      this.dataSource.filter = this.targetImage;
    } else {
      this.filterField.nativeElement.value = '';
    }
  }

  ngOnDestroy(): void {
    this.ngUnsubscribe.next();
    this.ngUnsubscribe.complete();
  }

  applyFilter(event: Event) {
    const filterValue = (event.target as HTMLInputElement).value;
    this.dataSource.filter = filterValue.trim().toLowerCase();

    if (this.dataSource.paginator) {
      this.dataSource.paginator.firstPage();
    }

    // Remove query param on URL when filtering
    this.router.navigate([], {
      relativeTo: this.route,
      queryParams: { image: null },
      queryParamsHandling: 'merge',
    });
  }

  private retrieveImages(): void {
    this.cobblerApiService
      .get_images()
      .pipe(takeUntil(this.ngUnsubscribe))
      .subscribe({
        next: (value) => {
          this.dataSource.data = value;
        },
        error: (error) => {
          // HTML encode the error message since it originates from XML
          this._snackBar.open(
            Utils.toHTML(error.message),
            $localize`:@@snackbar.action.close:Close`,
          );
        },
      });
  }

  addImage(): void {
    const dialogRef = this.dialog.open(ImageCreateComponent, { width: '40%' });
    dialogRef.afterClosed().subscribe((result) => {
      if (typeof result === 'string') {
        this.router.navigate(['/items', 'image', result]);
      }
    });
  }

  showImage(uid: string, name: string): void {
    this.router.navigate(['/items', 'image', name]);
  }

  renameImage(uid: string, name: string): void {
    const dialogRef = this.dialog.open(DialogItemRenameComponent, {
      data: {
        itemType: 'Image',
        itemName: name,
        itemUid: uid,
      },
    });

    dialogRef.afterClosed().subscribe((newItemName) => {
      if (newItemName === undefined) {
        // Cancel means we don't need to rename the image
        return;
      }
      this.cobblerApiService
        .get_image_handle(name, this.userService.token)
        .pipe(takeUntil(this.ngUnsubscribe))
        .subscribe({
          next: (imageHandle) => {
            this.cobblerApiService
              .rename_image(imageHandle, newItemName, this.userService.token)
              .pipe(takeUntil(this.ngUnsubscribe))
              .subscribe({
                next: () => {
                  this.retrieveImages();
                },
                error: (error) => {
                  // HTML encode the error message since it originates from XML
                  this._snackBar.open(
                    Utils.toHTML(error.message),
                    $localize`:@@snackbar.action.close:Close`,
                  );
                },
              });
          },
          error: (error) => {
            // HTML encode the error message since it originates from XML
            this._snackBar.open(
              Utils.toHTML(error.message),
              $localize`:@@snackbar.action.close:Close`,
            );
          },
        });
    });
  }

  deleteImage(uid: string, name: string): void {
    this.cobblerApiService
      .remove_image(name, this.userService.token, false)
      .pipe(takeUntil(this.ngUnsubscribe))
      .subscribe({
        next: () => {
          this.retrieveImages();
        },
        error: (error) => {
          // HTML encode the error message since it originates from XML
          this._snackBar.open(
            Utils.toHTML(error.message),
            $localize`:@@snackbar.action.close:Close`,
          );
        },
      });
  }
}
<div class="title-table">
  <div class="title-row">
    <h1 class="title title-cell-text" i18n="@@image.overview.title">IMAGES</h1>
    <span class="title-cell-button">
      <button
        mat-icon-button
        data-testid="item-add-button"
        (click)="this.addImage()"
        matTooltip="Add Image"
        i18n-matTooltip="@@image.overview.add-tooltip"
      >
        <mat-icon>add</mat-icon>
      </button></span
    >
  </div>
</div>
<mat-form-field appearance="fill">
  <mat-label i18n="@@filter.label">Filter</mat-label>
  <input
    matInput
    (keyup)="applyFilter($event)"
    placeholder="Ex. s390x:SLE-Micro-5.1-GM:install-auto"
    i18n-placeholder="@@image.overview.filter-placeholder"
    #input
  />
</mat-form-field>
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8" matSort>
  <!-- Name Column -->
  <ng-container matColumnDef="name">
    <th mat-header-cell *matHeaderCellDef i18n="@@table.col.name">Name</th>
    <td mat-cell *matCellDef="let element">
      <a (click)="showImage(element.uid, element.name)"
        >{{ element.name }}
        <mat-icon class="link-icon" aria-hidden="true"
          >arrow_outward</mat-icon
        ></a
      >
    </td>
  </ng-container>

  <!-- Arch Column -->
  <ng-container matColumnDef="arch">
    <th mat-header-cell *matHeaderCellDef i18n="@@image.overview.col.arch">
      Architecture
    </th>
    <td mat-cell *matCellDef="let element">{{ element.arch }}</td>
  </ng-container>

  <!-- Breed Column -->
  <ng-container matColumnDef="breed">
    <th mat-header-cell *matHeaderCellDef i18n="@@image.overview.col.breed">
      Breed
    </th>
    <td mat-cell *matCellDef="let element">
      <a [routerLink]="['/signatures']" [queryParams]="{ breed: element.breed }"
        >{{ element.breed }}
        <mat-icon class="link-icon" aria-hidden="true"
          >arrow_outward</mat-icon
        ></a
      >
    </td>
  </ng-container>

  <!-- OsVersion Column -->
  <ng-container matColumnDef="os_version">
    <th
      mat-header-cell
      *matHeaderCellDef
      i18n="@@image.overview.col.os-version"
    >
      Operating System Version
    </th>
    <td mat-cell *matCellDef="let element">
      <a
        [routerLink]="['/signatures']"
        [queryParams]="{ breed: element.breed, osVersion: element.os_version }"
        >{{ element.os_version }}
        <mat-icon class="link-icon" aria-hidden="true"
          >arrow_outward</mat-icon
        ></a
      >
    </td>
  </ng-container>

  <ng-container matColumnDef="actions">
    <th mat-header-cell *matHeaderCellDef></th>
    <td mat-cell *matCellDef="let element">
      <button mat-icon-button [matMenuTriggerFor]="menu">
        <mat-icon>more_vert</mat-icon>
      </button>
      <mat-menu #menu="matMenu">
        <button mat-menu-item (click)="showImage(element.uid, element.name)">
          <mat-icon>visibility</mat-icon>
          <span i18n="@@item.action.show-details">Show details</span>
        </button>
        <button mat-menu-item (click)="renameImage(element.uid, element.name)">
          <mat-icon>edit</mat-icon>
          <span i18n="@@item.action.rename">Rename</span>
        </button>
        <button mat-menu-item (click)="deleteImage(element.uid, element.name)">
          <mat-icon>delete</mat-icon>
          <span i18n="@@item.action.delete">Delete</span>
        </button>
      </mat-menu>
    </td>
  </ng-container>

  <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
  <tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</table>
<mat-paginator
  [length]="100"
  [pageSize]="10"
  [pageSizeOptions]="[5, 10, 25, 100]"
  aria-label="Select page"
  i18n-aria-label="@@paginator.select-page"
>
</mat-paginator>
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""