diff --git a/booklore-api/src/main/java/com/adityachandel/booklore/controller/BookController.java b/booklore-api/src/main/java/com/adityachandel/booklore/controller/BookController.java index 99c26e1f1..94639df66 100644 --- a/booklore-api/src/main/java/com/adityachandel/booklore/controller/BookController.java +++ b/booklore-api/src/main/java/com/adityachandel/booklore/controller/BookController.java @@ -124,4 +124,11 @@ public class BookController { bookService.updateReadStatus(bookId, request.status()); return ResponseEntity.noContent().build(); } + + @PostMapping("/{bookId}/reset-progress") + @CheckBookAccess(bookIdParam = "bookId") + public ResponseEntity resetProgress(@PathVariable long bookId) { + Book book = bookService.resetProgress(bookId); + return ResponseEntity.ok(book); + } } \ No newline at end of file diff --git a/booklore-api/src/main/java/com/adityachandel/booklore/service/BookService.java b/booklore-api/src/main/java/com/adityachandel/booklore/service/BookService.java index 37aa1a0c1..c492a9c3a 100644 --- a/booklore-api/src/main/java/com/adityachandel/booklore/service/BookService.java +++ b/booklore-api/src/main/java/com/adityachandel/booklore/service/BookService.java @@ -277,6 +277,24 @@ public class BookService { return book; } + public Book resetProgress(long bookId) { + BookLoreUser user = authenticationService.getAuthenticatedUser(); + BookEntity bookEntity = bookRepository.findById(bookId).orElseThrow(() -> ApiError.BOOK_NOT_FOUND.createException(bookId)); + + UserBookProgressEntity progress = userBookProgressRepository.findByUserIdAndBookId(user.getId(), bookId).orElse(new UserBookProgressEntity()); + progress.setBook(bookEntity); + progress.setReadStatus(null); + progress.setLastReadTime(null); + progress.setPdfProgress(null); + progress.setPdfProgressPercent(null); + progress.setEpubProgress(null); + progress.setEpubProgressPercent(null); + progress.setCbxProgress(null); + progress.setCbxProgressPercent(null); + userBookProgressRepository.save(progress); + return bookMapper.toBook(bookEntity); + } + @Transactional public List assignShelvesToBooks(Set bookIds, Set shelfIdsToAssign, Set shelfIdsToUnassign) { diff --git a/booklore-ui/src/app/book/metadata/book-metadata-center/metadata-viewer/metadata-viewer.component.html b/booklore-ui/src/app/book/metadata/book-metadata-center/metadata-viewer/metadata-viewer.component.html index e6dfb3f96..e6cc0cf52 100644 --- a/booklore-ui/src/app/book/metadata/book-metadata-center/metadata-viewer/metadata-viewer.component.html +++ b/booklore-ui/src/app/book/metadata/book-metadata-center/metadata-viewer/metadata-viewer.component.html @@ -190,7 +190,28 @@ {{ getFileExtension(book?.filePath) || '-' }}

- +

+ Progress: + + + {{ getProgressPercent(book) !== null ? getProgressPercent(book) + '%' : 'N/A' }} + + @if (getProgressPercent(book) !== null) { + + + } + +

Metadata Score: @if (book?.metadataMatchScore != null) { @@ -203,14 +224,6 @@ - }

-

- Progress: - - {{ getProgressPercent(book) !== null ? getProgressPercent(book) + '%' : 'N/A' }} - -

File Size: {{ getFileSizeInMB(book) }}

ISBN 13: {{ book?.metadata!.isbn13 || '-' }}

diff --git a/booklore-ui/src/app/book/metadata/book-metadata-center/metadata-viewer/metadata-viewer.component.ts b/booklore-ui/src/app/book/metadata/book-metadata-center/metadata-viewer/metadata-viewer.component.ts index 946091189..b2e2e8ffe 100644 --- a/booklore-ui/src/app/book/metadata/book-metadata-center/metadata-viewer/metadata-viewer.component.ts +++ b/booklore-ui/src/app/book/metadata/book-metadata-center/metadata-viewer/metadata-viewer.component.ts @@ -346,14 +346,14 @@ export class MetadataViewerComponent implements OnInit, OnChanges { return 'bg-blue-500'; } - onPersonalRatingChange(book: Book, { value: personalRating }: RatingRateEvent): void { + onPersonalRatingChange(book: Book, {value: personalRating}: RatingRateEvent): void { if (!book?.metadata) return; - const updatedMetadata = { ...book.metadata, personalRating }; + const updatedMetadata = {...book.metadata, personalRating}; this.bookService.updateBookMetadata(book.id, { metadata: updatedMetadata, - clearFlags: { personalRating: false } + clearFlags: {personalRating: false} }, false).subscribe({ next: () => { this.messageService.add({ @@ -461,4 +461,35 @@ export class MetadataViewerComponent implements OnInit, OnChanges { }); }); } + + resetProgress(book: Book): void { + this.confirmationService.confirm({ + message: `Reset reading progress for "${book.metadata?.title}"?`, + header: 'Confirm Reset', + icon: 'pi pi-exclamation-triangle', + acceptLabel: 'Yes', + rejectLabel: 'Cancel', + acceptButtonStyleClass: 'p-button-danger', + accept: () => { + this.bookService.resetProgress(book.id).subscribe({ + next: () => { + this.messageService.add({ + severity: 'success', + summary: 'Progress Reset', + detail: 'Reading progress has been reset.', + life: 1500 + }); + }, + error: () => { + this.messageService.add({ + severity: 'error', + summary: 'Failed', + detail: 'Could not reset progress.', + life: 1500 + }); + } + }); + } + }); + } } diff --git a/booklore-ui/src/app/book/service/book.service.ts b/booklore-ui/src/app/book/service/book.service.ts index a0270ac64..b75631d69 100644 --- a/booklore-ui/src/app/book/service/book.service.ts +++ b/booklore-ui/src/app/book/service/book.service.ts @@ -397,6 +397,12 @@ export class BookService { ); } + resetProgress(bookId: number): Observable { + return this.http.post(`${this.url}/${bookId}/reset-progress`, null).pipe( + tap(updatedBook => this.handleBookUpdate(updatedBook)) + ); + } + /*------------------ All the websocket handlers go below ------------------*/ @@ -436,7 +442,7 @@ export class BookService { updatedMap.has(book.id) ? updatedMap.get(book.id)! : book ); - this.bookStateSubject.next({ ...currentState, books: mergedBooks }); + this.bookStateSubject.next({...currentState, books: mergedBooks}); } handleBookMetadataUpdate(bookId: number, updatedMetadata: BookMetadata) {