mirror of
https://github.com/booklore-app/booklore.git
synced 2025-12-23 22:28:11 -05:00
Add bulk lock/unlock metadata functionality for books
This commit is contained in:
committed by
Aditya Chandel
parent
5c50a157cd
commit
941206bdf3
@@ -4,6 +4,7 @@ import com.adityachandel.booklore.mapper.BookMetadataMapper;
|
|||||||
import com.adityachandel.booklore.model.dto.BookMetadata;
|
import com.adityachandel.booklore.model.dto.BookMetadata;
|
||||||
import com.adityachandel.booklore.model.dto.request.FieldLockRequest;
|
import com.adityachandel.booklore.model.dto.request.FieldLockRequest;
|
||||||
import com.adityachandel.booklore.model.dto.request.MetadataRefreshRequest;
|
import com.adityachandel.booklore.model.dto.request.MetadataRefreshRequest;
|
||||||
|
import com.adityachandel.booklore.model.dto.request.ToggleAllLockRequest;
|
||||||
import com.adityachandel.booklore.quartz.JobSchedulerService;
|
import com.adityachandel.booklore.quartz.JobSchedulerService;
|
||||||
import com.adityachandel.booklore.service.BookMetadataService;
|
import com.adityachandel.booklore.service.BookMetadataService;
|
||||||
import com.adityachandel.booklore.service.BookMetadataUpdater;
|
import com.adityachandel.booklore.service.BookMetadataUpdater;
|
||||||
@@ -57,7 +58,7 @@ public class MetadataController {
|
|||||||
return ResponseEntity.ok(updatedMetadata);
|
return ResponseEntity.ok(updatedMetadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/{bookId}/metadata/lock")
|
@PutMapping("/{bookId}/metadata/toggle-field-lock")
|
||||||
@PreAuthorize("@securityUtil.canEditMetadata() or @securityUtil.isAdmin()")
|
@PreAuthorize("@securityUtil.canEditMetadata() or @securityUtil.isAdmin()")
|
||||||
public ResponseEntity<BookMetadata> updateFieldLockState(@RequestBody FieldLockRequest request) {
|
public ResponseEntity<BookMetadata> updateFieldLockState(@RequestBody FieldLockRequest request) {
|
||||||
long bookId = request.getBookId();
|
long bookId = request.getBookId();
|
||||||
@@ -66,6 +67,12 @@ public class MetadataController {
|
|||||||
return ResponseEntity.ok(bookMetadataService.updateFieldLockState(bookId, field, isLocked));
|
return ResponseEntity.ok(bookMetadataService.updateFieldLockState(bookId, field, isLocked));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PutMapping("/metadata/toggle-all-lock")
|
||||||
|
@PreAuthorize("@securityUtil.canEditMetadata() or @securityUtil.isAdmin()")
|
||||||
|
public ResponseEntity<List<BookMetadata>> toggleAllMetadata(@RequestBody ToggleAllLockRequest request) {
|
||||||
|
return ResponseEntity.ok(bookMetadataService.toggleAllLock(request));
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping("/regenerate-covers")
|
@PostMapping("/regenerate-covers")
|
||||||
@PreAuthorize("@securityUtil.canEditMetadata() or @securityUtil.isAdmin()")
|
@PreAuthorize("@securityUtil.canEditMetadata() or @securityUtil.isAdmin()")
|
||||||
public void regenerateCovers() {
|
public void regenerateCovers() {
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.adityachandel.booklore.model.dto.request;
|
||||||
|
|
||||||
|
import com.adityachandel.booklore.model.enums.Lock;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ToggleAllLockRequest {
|
||||||
|
private Set<Long> bookIds;
|
||||||
|
private Lock lock;
|
||||||
|
}
|
||||||
@@ -150,4 +150,27 @@ public class BookMetadataEntity {
|
|||||||
|
|
||||||
@OneToMany(fetch = FetchType.LAZY, mappedBy = "book")
|
@OneToMany(fetch = FetchType.LAZY, mappedBy = "book")
|
||||||
private List<BookAwardEntity> awards;
|
private List<BookAwardEntity> awards;
|
||||||
|
|
||||||
|
|
||||||
|
public void applyLockToAllFields(boolean lock) {
|
||||||
|
this.allFieldsLocked = lock;
|
||||||
|
this.titleLocked = lock;
|
||||||
|
this.subtitleLocked = lock;
|
||||||
|
this.publisherLocked = lock;
|
||||||
|
this.publishedDateLocked = lock;
|
||||||
|
this.descriptionLocked = lock;
|
||||||
|
this.isbn13Locked = lock;
|
||||||
|
this.isbn10Locked = lock;
|
||||||
|
this.pageCountLocked = lock;
|
||||||
|
this.thumbnailLocked = lock;
|
||||||
|
this.languageLocked = lock;
|
||||||
|
this.ratingLocked = lock;
|
||||||
|
this.reviewCountLocked = lock;
|
||||||
|
this.coverLocked = lock;
|
||||||
|
this.seriesNameLocked = lock;
|
||||||
|
this.seriesNumberLocked = lock;
|
||||||
|
this.seriesTotalLocked = lock;
|
||||||
|
this.authorsLocked = lock;
|
||||||
|
this.categoriesLocked = lock;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package com.adityachandel.booklore.model.enums;
|
||||||
|
|
||||||
|
public enum Lock {
|
||||||
|
LOCK, UNLOCK
|
||||||
|
}
|
||||||
@@ -7,10 +7,12 @@ import com.adityachandel.booklore.model.dto.Book;
|
|||||||
import com.adityachandel.booklore.model.dto.BookMetadata;
|
import com.adityachandel.booklore.model.dto.BookMetadata;
|
||||||
import com.adityachandel.booklore.model.dto.request.MetadataRefreshOptions;
|
import com.adityachandel.booklore.model.dto.request.MetadataRefreshOptions;
|
||||||
import com.adityachandel.booklore.model.dto.request.MetadataRefreshRequest;
|
import com.adityachandel.booklore.model.dto.request.MetadataRefreshRequest;
|
||||||
|
import com.adityachandel.booklore.model.dto.request.ToggleAllLockRequest;
|
||||||
import com.adityachandel.booklore.model.dto.settings.AppSettings;
|
import com.adityachandel.booklore.model.dto.settings.AppSettings;
|
||||||
import com.adityachandel.booklore.model.entity.BookEntity;
|
import com.adityachandel.booklore.model.entity.BookEntity;
|
||||||
import com.adityachandel.booklore.model.entity.BookMetadataEntity;
|
import com.adityachandel.booklore.model.entity.BookMetadataEntity;
|
||||||
import com.adityachandel.booklore.model.entity.LibraryEntity;
|
import com.adityachandel.booklore.model.entity.LibraryEntity;
|
||||||
|
import com.adityachandel.booklore.model.enums.Lock;
|
||||||
import com.adityachandel.booklore.model.websocket.Topic;
|
import com.adityachandel.booklore.model.websocket.Topic;
|
||||||
import com.adityachandel.booklore.repository.BookMetadataRepository;
|
import com.adityachandel.booklore.repository.BookMetadataRepository;
|
||||||
import com.adityachandel.booklore.repository.BookRepository;
|
import com.adityachandel.booklore.repository.BookRepository;
|
||||||
@@ -441,4 +443,15 @@ public class BookMetadataService {
|
|||||||
}
|
}
|
||||||
log.info("{} Successfully regenerated cover for book ID {} ({})", progress, book.getId(), title);
|
log.info("{} Successfully regenerated cover for book ID {} ({})", progress, book.getId(), title);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public List<BookMetadata> toggleAllLock(ToggleAllLockRequest request) {
|
||||||
|
boolean lock = request.getLock() == Lock.LOCK;
|
||||||
|
List<BookEntity> books = bookRepository.findAllByIdIn(request.getBookIds())
|
||||||
|
.stream()
|
||||||
|
.peek(book -> book.getMetadata().applyLockToAllFields(lock))
|
||||||
|
.toList();
|
||||||
|
bookRepository.saveAll(books);
|
||||||
|
return books.stream().map(b ->bookMetadataMapper.toBookMetadata(b.getMetadata(), false)).collect(Collectors.toList());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -106,8 +106,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="book-card-container" *ngIf="bookState?.books as books">
|
<div class="book-card-container" *ngIf="bookState?.books as books">
|
||||||
<app-book-table *ngIf="gridOrTable === 'table' && books.length > 0" [books]="books" (selectedBooksChange)="onSelectedBooksChange($event)"
|
<app-book-table *ngIf="gridOrTable === 'table' && books.length > 0" [books]="books" (selectedBooksChange)="onSelectedBooksChange($event)" [sortOption]="selectedSort"></app-book-table>
|
||||||
[sortOption]="selectedSort"></app-book-table>
|
|
||||||
|
|
||||||
<virtual-scroller *ngIf="gridOrTable === 'grid'" class="virtual-scroller" #scroll [items]="books">
|
<virtual-scroller *ngIf="gridOrTable === 'grid'" class="virtual-scroller" #scroll [items]="books">
|
||||||
<div class="grid grid-cols-12 gap-4" #container>
|
<div class="grid grid-cols-12 gap-4" #container>
|
||||||
@@ -126,27 +125,50 @@
|
|||||||
<div class="book-browser-footer bg-[var(--card-background)] bg-opacity-10" *ngIf="selectedBooks.size > 0" [@slideInOut]>
|
<div class="book-browser-footer bg-[var(--card-background)] bg-opacity-10" *ngIf="selectedBooks.size > 0" [@slideInOut]>
|
||||||
<div class="flex justify-between gap-8">
|
<div class="flex justify-between gap-8">
|
||||||
<ng-container *ngIf="entityType$ | async as entityType">
|
<ng-container *ngIf="entityType$ | async as entityType">
|
||||||
<p-button *ngIf="entityType === EntityType.LIBRARY || entityType === EntityType.ALL_BOOKS"
|
<p-button
|
||||||
label="Assign Shelf"
|
*ngIf="entityType === EntityType.LIBRARY || entityType === EntityType.ALL_BOOKS"
|
||||||
severity="info"
|
icon="pi pi-bookmark-fill"
|
||||||
(onClick)="openShelfAssigner()">
|
outlined="true"
|
||||||
|
severity="info"
|
||||||
|
(onClick)="openShelfAssigner()"
|
||||||
|
pTooltip="Assign to shelf"
|
||||||
|
tooltipPosition="top">
|
||||||
</p-button>
|
</p-button>
|
||||||
<p-button *ngIf="entityType === EntityType.SHELF"
|
<p-button
|
||||||
label="Unshelf"
|
*ngIf="entityType === EntityType.SHELF"
|
||||||
severity="info"
|
icon="pi pi-bookmark"
|
||||||
(click)="unshelfBooks()">
|
outlined="true"
|
||||||
|
severity="info"
|
||||||
|
(click)="unshelfBooks()"
|
||||||
|
pTooltip="Remove from shelf"
|
||||||
|
tooltipPosition="top">
|
||||||
</p-button>
|
</p-button>
|
||||||
<div *ngIf="userService.userData$ | async as userData">
|
<div *ngIf="userService.userData$ | async as userData">
|
||||||
<p-button *ngIf="userData.permissions.canEditMetadata"
|
<p-button
|
||||||
label="Refresh Metadata"
|
*ngIf="userData.permissions.canEditMetadata"
|
||||||
severity="info"
|
icon="pi pi-database"
|
||||||
(click)="updateMetadata()">
|
outlined="true"
|
||||||
|
severity="info"
|
||||||
|
(click)="updateMetadata()"
|
||||||
|
pTooltip="Update metadata"
|
||||||
|
tooltipPosition="top">
|
||||||
</p-button>
|
</p-button>
|
||||||
</div>
|
</div>
|
||||||
<p-button
|
<p-button
|
||||||
label="Deselect All"
|
outlined="true"
|
||||||
|
icon="pi pi-lock"
|
||||||
|
severity="info"
|
||||||
|
(click)="lockUnlockMetadata()"
|
||||||
|
pTooltip="Lock/Unlock metadata"
|
||||||
|
tooltipPosition="top">
|
||||||
|
</p-button>
|
||||||
|
<p-button
|
||||||
|
outlined="true"
|
||||||
|
icon="pi pi-times"
|
||||||
severity="warn"
|
severity="warn"
|
||||||
(click)="deselectAllBooks()">
|
(click)="deselectAllBooks()"
|
||||||
|
pTooltip="Deselect all books"
|
||||||
|
tooltipPosition="top">
|
||||||
</p-button>
|
</p-button>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -34,8 +34,8 @@
|
|||||||
.book-browser-footer {
|
.book-browser-footer {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 20%;
|
left: 30%;
|
||||||
right: 20%;
|
right: 30%;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 1rem 0.5rem 0.5rem;
|
padding: 1rem 0.5rem 0.5rem;
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import {AfterViewInit, Component, inject, OnInit, ViewChild} from '@angular/core';
|
import {AfterViewInit, Component, inject, OnInit, ViewChild} from '@angular/core';
|
||||||
import {ActivatedRoute} from '@angular/router';
|
import {ActivatedRoute} from '@angular/router';
|
||||||
import {MenuItem, MessageService, PrimeTemplate} from 'primeng/api';
|
import {ConfirmationService, MenuItem, MessageService, PrimeTemplate} from 'primeng/api';
|
||||||
import {LibraryService} from '../../service/library.service';
|
import {LibraryService} from '../../service/library.service';
|
||||||
import {BookService} from '../../service/book.service';
|
import {BookService} from '../../service/book.service';
|
||||||
import {map, switchMap} from 'rxjs/operators';
|
import {map, switchMap} from 'rxjs/operators';
|
||||||
import {BehaviorSubject, combineLatest, Observable, of, Subject} from 'rxjs';
|
import {BehaviorSubject, Observable, of, Subject} from 'rxjs';
|
||||||
import {ShelfService} from '../../service/shelf.service';
|
import {ShelfService} from '../../service/shelf.service';
|
||||||
import {ShelfAssignerComponent} from '../shelf-assigner/shelf-assigner.component';
|
import {ShelfAssignerComponent} from '../shelf-assigner/shelf-assigner.component';
|
||||||
import {DialogService, DynamicDialogRef} from 'primeng/dynamicdialog';
|
import {DialogService, DynamicDialogRef} from 'primeng/dynamicdialog';
|
||||||
@@ -31,6 +31,7 @@ import {BookFilterComponent} from './book-filter/book-filter.component';
|
|||||||
import {Tooltip} from 'primeng/tooltip';
|
import {Tooltip} from 'primeng/tooltip';
|
||||||
import {Fluid} from 'primeng/fluid';
|
import {Fluid} from 'primeng/fluid';
|
||||||
import {UserService} from '../../../user.service';
|
import {UserService} from '../../../user.service';
|
||||||
|
import {LockUnlockMetadataDialogComponent} from './lock-unlock-metadata-dialog/lock-unlock-metadata-dialog.component';
|
||||||
|
|
||||||
export enum EntityType {
|
export enum EntityType {
|
||||||
LIBRARY = 'Library',
|
LIBRARY = 'Library',
|
||||||
@@ -92,6 +93,7 @@ export class BookBrowserComponent implements OnInit, AfterViewInit {
|
|||||||
private dialogService = inject(DialogService);
|
private dialogService = inject(DialogService);
|
||||||
private sortService = inject(SortService);
|
private sortService = inject(SortService);
|
||||||
private libraryShelfMenuService = inject(LibraryShelfMenuService);
|
private libraryShelfMenuService = inject(LibraryShelfMenuService);
|
||||||
|
private confirmationService = inject(ConfirmationService);
|
||||||
|
|
||||||
protected resetFilterSubject = new Subject<void>();
|
protected resetFilterSubject = new Subject<void>();
|
||||||
|
|
||||||
@@ -483,4 +485,18 @@ export class BookBrowserComponent implements OnInit, AfterViewInit {
|
|||||||
this.selectedFilter.next(item);
|
this.selectedFilter.next(item);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lockUnlockMetadata() {
|
||||||
|
this.dynamicDialogRef = this.dialogService.open(LockUnlockMetadataDialogComponent, {
|
||||||
|
header: 'Toggle Metadata Lock',
|
||||||
|
modal: true,
|
||||||
|
closable: true,
|
||||||
|
data: {
|
||||||
|
bookIds: Array.from(this.selectedBooks)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.dynamicDialogRef.onClose.subscribe(() => {
|
||||||
|
this.deselectAllBooks();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,8 +26,17 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="book-info">
|
<div class="book-info">
|
||||||
<div class="book-title-container">
|
<div class="book-title-container flex items-center">
|
||||||
<h4 class="book-title">{{ book.metadata?.title }}</h4>
|
<p class="m-0 pl-2">
|
||||||
|
<i
|
||||||
|
class="pi"
|
||||||
|
[ngClass]="book.metadata?.allFieldsLocked ? 'pi-lock text-red-400' : 'pi-lock-open text-green-400'"
|
||||||
|
[title]="book.metadata?.allFieldsLocked ? 'Locked' : 'Unlocked'"
|
||||||
|
style="font-size: 0.75rem;">
|
||||||
|
</i>
|
||||||
|
</p>
|
||||||
|
<h4 class="book-title m-0 pl-2">{{ book.metadata?.title }}</h4>
|
||||||
|
|
||||||
<p-tieredmenu #menu [model]="items" [popup]="true" appendTo="body"></p-tieredmenu>
|
<p-tieredmenu #menu [model]="items" [popup]="true" appendTo="body"></p-tieredmenu>
|
||||||
<p-button
|
<p-button
|
||||||
size="small"
|
size="small"
|
||||||
|
|||||||
@@ -80,7 +80,6 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding-left: 6px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-btn,
|
.info-btn,
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
<th>
|
<th>
|
||||||
<p-tableHeaderCheckbox/>
|
<p-tableHeaderCheckbox/>
|
||||||
</th>
|
</th>
|
||||||
|
<th pResizableColumn></th>
|
||||||
<th class="max-w-14 min-w-14"></th>
|
<th class="max-w-14 min-w-14"></th>
|
||||||
<th pResizableColumn>Title</th>
|
<th pResizableColumn>Title</th>
|
||||||
<th pResizableColumn>Authors</th>
|
<th pResizableColumn>Authors</th>
|
||||||
@@ -34,6 +35,17 @@
|
|||||||
<td class="max-w-16">
|
<td class="max-w-16">
|
||||||
<p-tableCheckbox [value]="book"></p-tableCheckbox>
|
<p-tableCheckbox [value]="book"></p-tableCheckbox>
|
||||||
</td>
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<p-button
|
||||||
|
[icon]="metadata.allFieldsLocked ? 'pi pi-lock' : 'pi pi-lock-open'"
|
||||||
|
[severity]="metadata.allFieldsLocked ? 'danger' : 'success'"
|
||||||
|
[text]="true"
|
||||||
|
[title]="metadata.allFieldsLocked ? 'Locked' : 'Unlocked'"
|
||||||
|
size="small"
|
||||||
|
[style]="{ width: '1.5rem', height: '1.5rem', padding: '0', fontSize: '0.75rem' }"
|
||||||
|
(click)="toggleMetadataLock(metadata)">
|
||||||
|
</p-button>
|
||||||
|
</td>
|
||||||
<td (click)="openMetadataCenter(book.id)">
|
<td (click)="openMetadataCenter(book.id)">
|
||||||
<img [attr.src]="urlHelper.getCoverUrl(metadata.bookId, metadata?.coverUpdatedOn)" alt="Book Cover" class="size-7"/>
|
<img [attr.src]="urlHelper.getCoverUrl(metadata.bookId, metadata?.coverUpdatedOn)" alt="Book Cover" class="size-7"/>
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
@@ -3,10 +3,13 @@ import {TableModule} from 'primeng/table';
|
|||||||
import {NgIf} from '@angular/common';
|
import {NgIf} from '@angular/common';
|
||||||
import {Rating} from 'primeng/rating';
|
import {Rating} from 'primeng/rating';
|
||||||
import {FormsModule} from '@angular/forms';
|
import {FormsModule} from '@angular/forms';
|
||||||
import {Book} from '../../../model/book.model';
|
import {Book, BookMetadata} from '../../../model/book.model';
|
||||||
import {SortOption} from '../../../model/sort.model';
|
import {SortOption} from '../../../model/sort.model';
|
||||||
import {MetadataDialogService} from '../../../../metadata/service/metadata-dialog.service';
|
import {MetadataDialogService} from '../../../../metadata/service/metadata-dialog.service';
|
||||||
import {UrlHelperService} from '../../../../utilities/service/url-helper.service';
|
import {UrlHelperService} from '../../../../utilities/service/url-helper.service';
|
||||||
|
import {Button} from 'primeng/button';
|
||||||
|
import {BookService} from '../../../service/book.service';
|
||||||
|
import {MessageService} from 'primeng/api';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-book-table',
|
selector: 'app-book-table',
|
||||||
@@ -16,7 +19,8 @@ import {UrlHelperService} from '../../../../utilities/service/url-helper.service
|
|||||||
TableModule,
|
TableModule,
|
||||||
NgIf,
|
NgIf,
|
||||||
Rating,
|
Rating,
|
||||||
FormsModule
|
FormsModule,
|
||||||
|
Button
|
||||||
],
|
],
|
||||||
styleUrl: './book-table.component.scss'
|
styleUrl: './book-table.component.scss'
|
||||||
})
|
})
|
||||||
@@ -30,6 +34,8 @@ export class BookTableComponent implements OnChanges {
|
|||||||
|
|
||||||
protected urlHelper = inject(UrlHelperService);
|
protected urlHelper = inject(UrlHelperService);
|
||||||
private metadataDialogService = inject(MetadataDialogService);
|
private metadataDialogService = inject(MetadataDialogService);
|
||||||
|
private bookService = inject(BookService);
|
||||||
|
private messageService = inject(MessageService);
|
||||||
|
|
||||||
// Hack to set virtual-scroller height
|
// Hack to set virtual-scroller height
|
||||||
ngOnChanges() {
|
ngOnChanges() {
|
||||||
@@ -90,4 +96,24 @@ export class BookTableComponent implements OnChanges {
|
|||||||
getGenres(genres: string[]) {
|
getGenres(genres: string[]) {
|
||||||
return genres?.join(', ') || '';
|
return genres?.join(', ') || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggleMetadataLock(metadata: BookMetadata): void {
|
||||||
|
const lockAction = metadata.allFieldsLocked ? 'UNLOCK' : 'LOCK';
|
||||||
|
this.bookService.toggleAllLock(new Set([metadata.bookId]), lockAction).subscribe({
|
||||||
|
next: () => {
|
||||||
|
this.messageService.add({
|
||||||
|
severity: 'success',
|
||||||
|
summary: `Metadata ${lockAction === 'LOCK' ? 'Locked' : 'Unlocked'}`,
|
||||||
|
detail: `Book metadata has been ${lockAction === 'LOCK' ? 'locked' : 'unlocked'} successfully.`,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
error: () => {
|
||||||
|
this.messageService.add({
|
||||||
|
severity: 'error',
|
||||||
|
summary: `Failed to ${lockAction === 'LOCK' ? 'Lock' : 'Unlock'}`,
|
||||||
|
detail: `An error occurred while ${lockAction === 'LOCK' ? 'locking' : 'unlocking'} the metadata.`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<div class="flex flex-col items-center text-center space-y-6">
|
||||||
|
<p class="pt-6 px-4">Lock or unlock all metadata for all the selected books</p>
|
||||||
|
<div class="flex gap-4 justify-center">
|
||||||
|
<p-button icon="pi pi-lock" label="Lock" outlined="true" severity="danger" (onClick)="toggleLock('LOCK')"></p-button>
|
||||||
|
<p-button icon="pi pi-lock-open" label="Unlock" outlined="true" severity="success" (onClick)="toggleLock('UNLOCK')"></p-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import {Component, inject} from '@angular/core';
|
||||||
|
import {Button} from 'primeng/button';
|
||||||
|
import {DynamicDialogConfig, DynamicDialogRef} from 'primeng/dynamicdialog';
|
||||||
|
import {MessageService} from 'primeng/api';
|
||||||
|
import {BookService} from '../../../service/book.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-lock-unlock-metadata-dialog',
|
||||||
|
imports: [
|
||||||
|
Button
|
||||||
|
],
|
||||||
|
templateUrl: './lock-unlock-metadata-dialog.component.html',
|
||||||
|
styleUrl: './lock-unlock-metadata-dialog.component.scss'
|
||||||
|
})
|
||||||
|
export class LockUnlockMetadataDialogComponent {
|
||||||
|
private bookService = inject(BookService);
|
||||||
|
private dynamicDialogConfig = inject(DynamicDialogConfig);
|
||||||
|
private dialogRef = inject(DynamicDialogRef);
|
||||||
|
private messageService = inject(MessageService);
|
||||||
|
|
||||||
|
bookIds: Set<number> = this.dynamicDialogConfig.data.bookIds;
|
||||||
|
|
||||||
|
toggleLock(action: 'LOCK' | 'UNLOCK'): void {
|
||||||
|
this.bookService.toggleAllLock(this.bookIds, action).subscribe({
|
||||||
|
next: () => {
|
||||||
|
const isLock = action === 'LOCK';
|
||||||
|
this.messageService.add({
|
||||||
|
severity: 'success',
|
||||||
|
summary: `Metadata ${isLock ? 'Locked' : 'Unlocked'}`,
|
||||||
|
detail: `All selected books have been ${isLock ? 'locked' : 'unlocked'} successfully.`,
|
||||||
|
});
|
||||||
|
this.dialogRef.close(action.toLowerCase());
|
||||||
|
},
|
||||||
|
error: () => {
|
||||||
|
this.messageService.add({
|
||||||
|
severity: 'error',
|
||||||
|
summary: `Failed to ${action === 'LOCK' ? 'Lock' : 'Unlock'}`,
|
||||||
|
detail: `An error occurred while ${action === 'LOCK' ? 'locking' : 'unlocking'} metadata.`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -40,6 +40,7 @@ export interface BookMetadata {
|
|||||||
providerBookId?: string;
|
providerBookId?: string;
|
||||||
thumbnailUrl?: string | null;
|
thumbnailUrl?: string | null;
|
||||||
|
|
||||||
|
allFieldsLocked?: boolean;
|
||||||
titleLocked?: boolean;
|
titleLocked?: boolean;
|
||||||
subtitleLocked?: boolean;
|
subtitleLocked?: boolean;
|
||||||
publisherLocked?: boolean;
|
publisherLocked?: boolean;
|
||||||
|
|||||||
@@ -210,7 +210,7 @@ export class BookService {
|
|||||||
|
|
||||||
updateBookMetadata(bookId: number, bookMetadata: BookMetadata, mergeCategories: boolean): Observable<BookMetadata> {
|
updateBookMetadata(bookId: number, bookMetadata: BookMetadata, mergeCategories: boolean): Observable<BookMetadata> {
|
||||||
const params = new HttpParams().set('mergeCategories', mergeCategories.toString());
|
const params = new HttpParams().set('mergeCategories', mergeCategories.toString());
|
||||||
return this.http.put<BookMetadata>(`${this.url}/${bookId}/metadata`, bookMetadata, { params }).pipe(
|
return this.http.put<BookMetadata>(`${this.url}/${bookId}/metadata`, bookMetadata, {params}).pipe(
|
||||||
map(updatedMetadata => {
|
map(updatedMetadata => {
|
||||||
this.handleBookMetadataUpdate(bookId, updatedMetadata);
|
this.handleBookMetadataUpdate(bookId, updatedMetadata);
|
||||||
return updatedMetadata;
|
return updatedMetadata;
|
||||||
@@ -218,6 +218,27 @@ export class BookService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggleAllLock(bookIds: Set<number>, lock: string): Observable<void> {
|
||||||
|
const requestBody = {
|
||||||
|
bookIds: Array.from(bookIds),
|
||||||
|
lock: lock
|
||||||
|
};
|
||||||
|
return this.http.put<BookMetadata[]>(`${this.url}/metadata/toggle-all-lock`, requestBody).pipe(
|
||||||
|
tap((updatedMetadataList) => {
|
||||||
|
const currentState = this.bookStateSubject.value;
|
||||||
|
const updatedBooks = (currentState.books || []).map(book => {
|
||||||
|
const updatedMetadata = updatedMetadataList.find(meta => meta.bookId === book.id);
|
||||||
|
return updatedMetadata ? {...book, metadata: updatedMetadata} : book;
|
||||||
|
});
|
||||||
|
this.bookStateSubject.next({...currentState, books: updatedBooks});
|
||||||
|
}),
|
||||||
|
map(() => void 0),
|
||||||
|
catchError((error) => {
|
||||||
|
throw error;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
autoRefreshMetadata(metadataRefreshRequest: MetadataRefreshRequest): Observable<any> {
|
autoRefreshMetadata(metadataRefreshRequest: MetadataRefreshRequest): Observable<any> {
|
||||||
return this.http.put<void>(`${this.url}/metadata/refresh`, metadataRefreshRequest).pipe(
|
return this.http.put<void>(`${this.url}/metadata/refresh`, metadataRefreshRequest).pipe(
|
||||||
map(() => {
|
map(() => {
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ export class MetadataEditorComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onSave(): void {
|
onSave(): void {
|
||||||
this.bookService.updateBookMetadata(this.currentBookId, this.buildMetadata(), false).subscribe({
|
this.bookService.updateBookMetadata(this.currentBookId, this.buildMetadata(undefined), false).subscribe({
|
||||||
next: (response) => {
|
next: (response) => {
|
||||||
this.messageService.add({severity: 'info', summary: 'Success', detail: 'Book metadata updated'});
|
this.messageService.add({severity: 'info', summary: 'Success', detail: 'Book metadata updated'});
|
||||||
this.metadataCenterService.emit(response);
|
this.metadataCenterService.emit(response);
|
||||||
@@ -171,7 +171,7 @@ export class MetadataEditorComponent implements OnInit {
|
|||||||
} else {
|
} else {
|
||||||
this.metadataForm.get(field)?.enable();
|
this.metadataForm.get(field)?.enable();
|
||||||
}
|
}
|
||||||
this.updateMetadata();
|
this.updateMetadata(undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
lockAll(): void {
|
lockAll(): void {
|
||||||
@@ -182,7 +182,7 @@ export class MetadataEditorComponent implements OnInit {
|
|||||||
this.metadataForm.get(fieldName)?.disable();
|
this.metadataForm.get(fieldName)?.disable();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.updateMetadata();
|
this.updateMetadata(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
unlockAll(): void {
|
unlockAll(): void {
|
||||||
@@ -193,10 +193,10 @@ export class MetadataEditorComponent implements OnInit {
|
|||||||
this.metadataForm.get(fieldName)?.enable();
|
this.metadataForm.get(fieldName)?.enable();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.updateMetadata();
|
this.updateMetadata(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private buildMetadata() {
|
private buildMetadata(shouldLockAllFields: boolean | undefined) {
|
||||||
const updatedBookMetadata: BookMetadata = {
|
const updatedBookMetadata: BookMetadata = {
|
||||||
bookId: this.currentBookId,
|
bookId: this.currentBookId,
|
||||||
title: this.metadataForm.get('title')?.value,
|
title: this.metadataForm.get('title')?.value,
|
||||||
@@ -233,17 +233,33 @@ export class MetadataEditorComponent implements OnInit {
|
|||||||
seriesNumberLocked: this.metadataForm.get('seriesNumberLocked')?.value,
|
seriesNumberLocked: this.metadataForm.get('seriesNumberLocked')?.value,
|
||||||
seriesTotalLocked: this.metadataForm.get('seriesTotalLocked')?.value,
|
seriesTotalLocked: this.metadataForm.get('seriesTotalLocked')?.value,
|
||||||
coverLocked: this.metadataForm.get('thumbnailUrlLocked')?.value,
|
coverLocked: this.metadataForm.get('thumbnailUrlLocked')?.value,
|
||||||
|
|
||||||
|
...(shouldLockAllFields !== undefined && {allFieldsLocked: shouldLockAllFields}),
|
||||||
};
|
};
|
||||||
return updatedBookMetadata;
|
return updatedBookMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateMetadata(): void {
|
private updateMetadata(shouldLockAllFields: boolean | undefined): void {
|
||||||
this.bookService.updateBookMetadata(this.currentBookId, this.buildMetadata(), false).subscribe({
|
this.bookService.updateBookMetadata(this.currentBookId, this.buildMetadata(shouldLockAllFields), false).subscribe({
|
||||||
next: (response) => {
|
next: (response) => {
|
||||||
this.metadataCenterService.emit(response);
|
this.metadataCenterService.emit(response);
|
||||||
|
|
||||||
|
if (shouldLockAllFields !== undefined) {
|
||||||
|
this.messageService.add({
|
||||||
|
severity: 'success',
|
||||||
|
summary: shouldLockAllFields ? 'Metadata Locked' : 'Metadata Unlocked',
|
||||||
|
detail: shouldLockAllFields
|
||||||
|
? 'All fields have been successfully locked.'
|
||||||
|
: 'All fields have been successfully unlocked.',
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
error: () => {
|
error: () => {
|
||||||
this.messageService.add({severity: 'error', summary: 'Error', detail: 'Failed to update lock state'});
|
this.messageService.add({
|
||||||
|
severity: 'error',
|
||||||
|
summary: 'Error',
|
||||||
|
detail: 'Failed to update lock state',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ export class MetadataPickerComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onSave(): void {
|
onSave(): void {
|
||||||
const updatedBookMetadata = this.buildMetadata();
|
const updatedBookMetadata = this.buildMetadata(undefined);
|
||||||
this.bookService.updateBookMetadata(this.currentBookId, updatedBookMetadata, false).subscribe({
|
this.bookService.updateBookMetadata(this.currentBookId, updatedBookMetadata, false).subscribe({
|
||||||
next: (bookMetadata) => {
|
next: (bookMetadata) => {
|
||||||
Object.keys(this.copiedFields).forEach((field) => {
|
Object.keys(this.copiedFields).forEach((field) => {
|
||||||
@@ -197,7 +197,7 @@ export class MetadataPickerComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private buildMetadata() {
|
private buildMetadata(shouldLockAllFields: boolean | undefined) {
|
||||||
const updatedBookMetadata: BookMetadata = {
|
const updatedBookMetadata: BookMetadata = {
|
||||||
bookId: this.currentBookId,
|
bookId: this.currentBookId,
|
||||||
title: this.metadataForm.get('title')?.value || this.copiedFields['title'] ? this.getValueOrCopied('title') : '',
|
title: this.metadataForm.get('title')?.value || this.copiedFields['title'] ? this.getValueOrCopied('title') : '',
|
||||||
@@ -235,6 +235,8 @@ export class MetadataPickerComponent implements OnInit {
|
|||||||
seriesNumberLocked: this.metadataForm.get('seriesNumberLocked')?.value,
|
seriesNumberLocked: this.metadataForm.get('seriesNumberLocked')?.value,
|
||||||
seriesTotalLocked: this.metadataForm.get('seriesTotalLocked')?.value,
|
seriesTotalLocked: this.metadataForm.get('seriesTotalLocked')?.value,
|
||||||
coverLocked: this.metadataForm.get('thumbnailUrlLocked')?.value,
|
coverLocked: this.metadataForm.get('thumbnailUrlLocked')?.value,
|
||||||
|
|
||||||
|
...(shouldLockAllFields !== undefined && {allFieldsLocked: shouldLockAllFields}),
|
||||||
};
|
};
|
||||||
return updatedBookMetadata;
|
return updatedBookMetadata;
|
||||||
}
|
}
|
||||||
@@ -247,13 +249,27 @@ export class MetadataPickerComponent implements OnInit {
|
|||||||
return this.copiedFields['thumbnailUrl'] ? this.getValueOrCopied('thumbnailUrl') : null;
|
return this.copiedFields['thumbnailUrl'] ? this.getValueOrCopied('thumbnailUrl') : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateMetadata(): void {
|
private updateMetadata(shouldLockAllFields: boolean | undefined): void {
|
||||||
this.bookService.updateBookMetadata(this.currentBookId, this.buildMetadata(), false).subscribe({
|
this.bookService.updateBookMetadata(this.currentBookId, this.buildMetadata(shouldLockAllFields), false).subscribe({
|
||||||
next: (response) => {
|
next: (response) => {
|
||||||
this.metadataCenterService.emit(response);
|
this.metadataCenterService.emit(response);
|
||||||
|
|
||||||
|
if (shouldLockAllFields !== undefined) {
|
||||||
|
this.messageService.add({
|
||||||
|
severity: 'success',
|
||||||
|
summary: shouldLockAllFields ? 'Metadata Locked' : 'Metadata Unlocked',
|
||||||
|
detail: shouldLockAllFields
|
||||||
|
? 'All fields have been successfully locked.'
|
||||||
|
: 'All fields have been successfully unlocked.',
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
error: () => {
|
error: () => {
|
||||||
this.messageService.add({severity: 'error', summary: 'Error', detail: 'Failed to update lock state'});
|
this.messageService.add({
|
||||||
|
severity: 'error',
|
||||||
|
summary: 'Error',
|
||||||
|
detail: 'Failed to update lock state',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -267,7 +283,7 @@ export class MetadataPickerComponent implements OnInit {
|
|||||||
} else {
|
} else {
|
||||||
this.metadataForm.get(field)?.enable();
|
this.metadataForm.get(field)?.enable();
|
||||||
}
|
}
|
||||||
this.updateMetadata();
|
this.updateMetadata(undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
copyMissing(): void {
|
copyMissing(): void {
|
||||||
@@ -352,7 +368,7 @@ export class MetadataPickerComponent implements OnInit {
|
|||||||
this.metadataForm.get(fieldName)?.disable();
|
this.metadataForm.get(fieldName)?.disable();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.updateMetadata();
|
this.updateMetadata(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
unlockAll(): void {
|
unlockAll(): void {
|
||||||
@@ -363,7 +379,7 @@ export class MetadataPickerComponent implements OnInit {
|
|||||||
this.metadataForm.get(fieldName)?.enable();
|
this.metadataForm.get(fieldName)?.enable();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.updateMetadata();
|
this.updateMetadata(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
highlightCopiedInput(field: string): void {
|
highlightCopiedInput(field: string): void {
|
||||||
|
|||||||
@@ -13,7 +13,17 @@
|
|||||||
<div class="flex flex-row items-center justify-between">
|
<div class="flex flex-row items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<p *ngIf="metadata.seriesName" class="italic">{{ metadata.seriesName }} #{{ metadata.seriesNumber }} </p>
|
<p *ngIf="metadata.seriesName" class="italic">{{ metadata.seriesName }} #{{ metadata.seriesNumber }} </p>
|
||||||
<p class="text-2xl font-bold">{{ metadata.title }}</p>
|
<div class="flex items-center gap-2">
|
||||||
|
<p class="text-2xl font-bold m-0">{{ metadata.title }}</p>
|
||||||
|
<i
|
||||||
|
class="pi"
|
||||||
|
[ngClass]="metadata.allFieldsLocked ? 'pi-lock text-red-500' : 'pi-lock-open text-green-500'"
|
||||||
|
[title]="metadata.allFieldsLocked ? 'Metadata is locked' : 'Metadata is unlocked'"
|
||||||
|
style="font-size: 1.25rem;"
|
||||||
|
pTooltip="{{ metadata.allFieldsLocked ? 'This book metadata is locked.' : 'This book metadata is unlocked.' }}"
|
||||||
|
tooltipPosition="top">
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
<p class="text-xl">{{ getAuthorNames(metadata?.authors || []) }}</p>
|
<p class="text-xl">{{ getAuthorNames(metadata?.authors || []) }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -45,6 +55,13 @@
|
|||||||
<p-button label="Assign Shelf" icon="pi pi-folder" severity="info" outlined (onClick)="assignShelf(metadata.bookId)"></p-button>
|
<p-button label="Assign Shelf" icon="pi pi-folder" severity="info" outlined (onClick)="assignShelf(metadata.bookId)"></p-button>
|
||||||
<p-button *ngIf="userData.permissions.canDownload" label="Download" icon="pi pi-download" severity="info" outlined (onClick)="download(metadata.bookId)"></p-button>
|
<p-button *ngIf="userData.permissions.canDownload" label="Download" icon="pi pi-download" severity="info" outlined (onClick)="download(metadata.bookId)"></p-button>
|
||||||
<p-splitbutton *ngIf="userData.permissions.canEmailBook" label="Quick Send" icon="pi pi-send" [model]="items" (onClick)="quickSend(metadata.bookId)" outlined severity="info"/>
|
<p-splitbutton *ngIf="userData.permissions.canEmailBook" label="Quick Send" icon="pi pi-send" [model]="items" (onClick)="quickSend(metadata.bookId)" outlined severity="info"/>
|
||||||
|
<!--<p-button
|
||||||
|
outlined
|
||||||
|
[label]="metadata.allFieldsLocked ? 'Unlock Metadata' : 'Lock Metadata'"
|
||||||
|
[icon]="metadata.allFieldsLocked ? 'pi pi-lock' : 'pi pi-lock-open'"
|
||||||
|
[severity]="metadata.allFieldsLocked ? 'danger' : 'success'"
|
||||||
|
(onClick)="toggleMetadataLock(metadata)">
|
||||||
|
</p-button>-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {Component, inject, Input, OnInit} from '@angular/core';
|
import {Component, inject, Input, OnInit} from '@angular/core';
|
||||||
import {Button} from 'primeng/button';
|
import {Button} from 'primeng/button';
|
||||||
import {AsyncPipe, NgForOf, NgIf} from '@angular/common';
|
import {AsyncPipe, NgClass, NgForOf, NgIf} from '@angular/common';
|
||||||
import {first, Observable} from 'rxjs';
|
import {first, Observable} from 'rxjs';
|
||||||
import {BookService} from '../../../book/service/book.service';
|
import {BookService} from '../../../book/service/book.service';
|
||||||
import {BookMetadataCenterService} from '../book-metadata-center.service';
|
import {BookMetadataCenterService} from '../book-metadata-center.service';
|
||||||
@@ -17,13 +17,14 @@ import {BookSenderComponent} from '../../../book/components/book-sender/book-sen
|
|||||||
import {DialogService} from 'primeng/dynamicdialog';
|
import {DialogService} from 'primeng/dynamicdialog';
|
||||||
import {EmailService} from '../../../settings/email/email.service';
|
import {EmailService} from '../../../settings/email/email.service';
|
||||||
import {ShelfAssignerComponent} from '../../../book/components/shelf-assigner/shelf-assigner.component';
|
import {ShelfAssignerComponent} from '../../../book/components/shelf-assigner/shelf-assigner.component';
|
||||||
|
import {Tooltip} from 'primeng/tooltip';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-metadata-viewer',
|
selector: 'app-metadata-viewer',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
templateUrl: './metadata-viewer.component.html',
|
templateUrl: './metadata-viewer.component.html',
|
||||||
styleUrl: './metadata-viewer.component.scss',
|
styleUrl: './metadata-viewer.component.scss',
|
||||||
imports: [Button, NgForOf, NgIf, AsyncPipe, Rating, FormsModule, Tag, Divider, SplitButton]
|
imports: [Button, NgForOf, NgIf, AsyncPipe, Rating, FormsModule, Tag, Divider, SplitButton, NgClass, Tooltip]
|
||||||
})
|
})
|
||||||
export class MetadataViewerComponent implements OnInit {
|
export class MetadataViewerComponent implements OnInit {
|
||||||
|
|
||||||
@@ -119,4 +120,24 @@ export class MetadataViewerComponent implements OnInit {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*toggleMetadataLock(metadata: BookMetadata): void {
|
||||||
|
const lockAction = metadata.allFieldsLocked ? 'UNLOCK' : 'LOCK';
|
||||||
|
this.bookService.toggleAllLock(new Set([metadata.bookId]), lockAction).subscribe({
|
||||||
|
next: () => {
|
||||||
|
this.messageService.add({
|
||||||
|
severity: 'success',
|
||||||
|
summary: `Metadata ${lockAction === 'LOCK' ? 'Locked' : 'Unlocked'}`,
|
||||||
|
detail: `Book metadata has been ${lockAction === 'LOCK' ? 'locked' : 'unlocked'} successfully.`,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
error: () => {
|
||||||
|
this.messageService.add({
|
||||||
|
severity: 'error',
|
||||||
|
summary: `Failed to ${lockAction === 'LOCK' ? 'Lock' : 'Unlock'}`,
|
||||||
|
detail: `An error occurred while ${lockAction === 'LOCK' ? 'locking' : 'unlocking'} the metadata.`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}*/
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user