diff --git a/booklore-api/src/main/java/com/adityachandel/booklore/model/websocket/LogNotification.java b/booklore-api/src/main/java/com/adityachandel/booklore/model/websocket/LogNotification.java index 6f18642bb..1e75287dc 100644 --- a/booklore-api/src/main/java/com/adityachandel/booklore/model/websocket/LogNotification.java +++ b/booklore-api/src/main/java/com/adityachandel/booklore/model/websocket/LogNotification.java @@ -10,12 +10,26 @@ public class LogNotification { private final Instant timestamp = Instant.now(); private final String message; + private final Severity severity; - public LogNotification(String message) { + public LogNotification(String message, Severity severity) { this.message = message; + this.severity = severity; } - public static LogNotification createLogNotification(String message) { - return new LogNotification(message); + public static LogNotification createLogNotification(String message, Severity severity) { + return new LogNotification(message, severity); + } + + public static LogNotification info(String message) { + return new LogNotification(message, Severity.INFO); + } + + public static LogNotification warn(String message) { + return new LogNotification(message, Severity.WARN); + } + + public static LogNotification error(String message) { + return new LogNotification(message, Severity.ERROR); } } diff --git a/booklore-api/src/main/java/com/adityachandel/booklore/model/websocket/Severity.java b/booklore-api/src/main/java/com/adityachandel/booklore/model/websocket/Severity.java new file mode 100644 index 000000000..54e96bd7a --- /dev/null +++ b/booklore-api/src/main/java/com/adityachandel/booklore/model/websocket/Severity.java @@ -0,0 +1,8 @@ +package com.adityachandel.booklore.model.websocket; + + +public enum Severity { + INFO, + WARN, + ERROR +} diff --git a/booklore-api/src/main/java/com/adityachandel/booklore/service/bookdrop/BookdropEventHandlerService.java b/booklore-api/src/main/java/com/adityachandel/booklore/service/bookdrop/BookdropEventHandlerService.java index da6ffb810..95555f9ba 100644 --- a/booklore-api/src/main/java/com/adityachandel/booklore/service/bookdrop/BookdropEventHandlerService.java +++ b/booklore-api/src/main/java/com/adityachandel/booklore/service/bookdrop/BookdropEventHandlerService.java @@ -20,7 +20,6 @@ import java.nio.file.Path; import java.nio.file.StandardWatchEventKinds; import java.nio.file.WatchEvent; import java.time.Instant; -import java.util.List; import java.util.Set; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; @@ -106,7 +105,7 @@ public class BookdropEventHandlerService { int queueSize = fileQueue.size(); notificationService.sendMessageToPermissions( Topic.LOG, - new LogNotification("Processing bookdrop file: " + fileName + " (" + queueSize + " files remaining)"), + LogNotification.info("Processing bookdrop file: " + fileName + " (" + queueSize + " files remaining)"), Set.of(PermissionType.ADMIN, PermissionType.MANIPULATE_LIBRARY) ); @@ -134,13 +133,13 @@ public class BookdropEventHandlerService { if (fileQueue.isEmpty()) { notificationService.sendMessageToPermissions( Topic.LOG, - new LogNotification("All bookdrop files have finished processing"), + LogNotification.info("All bookdrop files have finished processing"), Set.of(PermissionType.ADMIN, PermissionType.MANIPULATE_LIBRARY) ); } else { notificationService.sendMessageToPermissions( Topic.LOG, - new LogNotification("Finished processing bookdrop file: " + fileName + " (" + fileQueue.size() + " files remaining)"), + LogNotification.info("Finished processing bookdrop file: " + fileName + " (" + fileQueue.size() + " files remaining)"), Set.of(PermissionType.ADMIN, PermissionType.MANIPULATE_LIBRARY) ); } diff --git a/booklore-api/src/main/java/com/adityachandel/booklore/service/email/EmailService.java b/booklore-api/src/main/java/com/adityachandel/booklore/service/email/EmailService.java index 561258300..f82cedde6 100644 --- a/booklore-api/src/main/java/com/adityachandel/booklore/service/email/EmailService.java +++ b/booklore-api/src/main/java/com/adityachandel/booklore/service/email/EmailService.java @@ -5,6 +5,8 @@ import com.adityachandel.booklore.model.dto.request.SendBookByEmailRequest; import com.adityachandel.booklore.model.entity.BookEntity; import com.adityachandel.booklore.model.entity.EmailProviderEntity; import com.adityachandel.booklore.model.entity.EmailRecipientEntity; +import com.adityachandel.booklore.model.websocket.LogNotification; +import com.adityachandel.booklore.model.websocket.Severity; import com.adityachandel.booklore.model.websocket.Topic; import com.adityachandel.booklore.repository.BookRepository; import com.adityachandel.booklore.repository.EmailProviderRepository; @@ -54,17 +56,17 @@ public class EmailService { private void sendEmailInVirtualThread(EmailProviderEntity emailProvider, String recipientEmail, BookEntity book) { String bookTitle = book.getMetadata().getTitle(); String logMessage = "Email dispatch initiated for book: " + bookTitle + " to " + recipientEmail; - notificationService.sendMessage(Topic.LOG, createLogNotification(logMessage)); + notificationService.sendMessage(Topic.LOG, LogNotification.info(logMessage)); log.info(logMessage); SecurityContextVirtualThread.runWithSecurityContext(() -> { try { sendEmail(emailProvider, recipientEmail, book); String successMessage = "The book: " + bookTitle + " has been successfully sent to " + recipientEmail; - notificationService.sendMessage(Topic.LOG, createLogNotification(successMessage)); + notificationService.sendMessage(Topic.LOG, LogNotification.info(successMessage)); log.info(successMessage); } catch (Exception e) { String errorMessage = "An error occurred while sending the book: " + bookTitle + " to " + recipientEmail + ". Error: " + e.getMessage(); - notificationService.sendMessage(Topic.LOG, createLogNotification(errorMessage)); + notificationService.sendMessage(Topic.LOG, LogNotification.error(errorMessage)); log.error(errorMessage, e); } }); diff --git a/booklore-api/src/main/java/com/adityachandel/booklore/service/email/SendEmailV2Service.java b/booklore-api/src/main/java/com/adityachandel/booklore/service/email/SendEmailV2Service.java index 81cf80fe7..009e19be6 100644 --- a/booklore-api/src/main/java/com/adityachandel/booklore/service/email/SendEmailV2Service.java +++ b/booklore-api/src/main/java/com/adityachandel/booklore/service/email/SendEmailV2Service.java @@ -7,6 +7,7 @@ import com.adityachandel.booklore.model.dto.request.SendBookByEmailRequest; import com.adityachandel.booklore.model.entity.BookEntity; import com.adityachandel.booklore.model.entity.EmailProviderV2Entity; import com.adityachandel.booklore.model.entity.EmailRecipientV2Entity; +import com.adityachandel.booklore.model.websocket.LogNotification; import com.adityachandel.booklore.model.websocket.Topic; import com.adityachandel.booklore.repository.BookRepository; import com.adityachandel.booklore.repository.EmailProviderV2Repository; @@ -62,17 +63,17 @@ public class SendEmailV2Service { private void sendEmailInVirtualThread(EmailProviderV2Entity emailProvider, String recipientEmail, BookEntity book) { String bookTitle = book.getMetadata().getTitle(); String logMessage = "Email dispatch initiated for book: " + bookTitle + " to " + recipientEmail; - notificationService.sendMessage(Topic.LOG, createLogNotification(logMessage)); + notificationService.sendMessage(Topic.LOG, LogNotification.info(logMessage)); log.info(logMessage); SecurityContextVirtualThread.runWithSecurityContext(() -> { try { sendEmail(emailProvider, recipientEmail, book); String successMessage = "The book: " + bookTitle + " has been successfully sent to " + recipientEmail; - notificationService.sendMessage(Topic.LOG, createLogNotification(successMessage)); + notificationService.sendMessage(Topic.LOG, LogNotification.info(successMessage)); log.info(successMessage); } catch (Exception e) { String errorMessage = "An error occurred while sending the book: " + bookTitle + " to " + recipientEmail + ". Error: " + e.getMessage(); - notificationService.sendMessage(Topic.LOG, createLogNotification(errorMessage)); + notificationService.sendMessage(Topic.LOG, LogNotification.error(errorMessage)); log.error(errorMessage, e); } }); diff --git a/booklore-api/src/main/java/com/adityachandel/booklore/service/event/AdminEventBroadcaster.java b/booklore-api/src/main/java/com/adityachandel/booklore/service/event/AdminEventBroadcaster.java index c70ed37fa..1415846ec 100644 --- a/booklore-api/src/main/java/com/adityachandel/booklore/service/event/AdminEventBroadcaster.java +++ b/booklore-api/src/main/java/com/adityachandel/booklore/service/event/AdminEventBroadcaster.java @@ -1,6 +1,7 @@ package com.adityachandel.booklore.service.event; import com.adityachandel.booklore.model.dto.BookLoreUser; +import com.adityachandel.booklore.model.websocket.LogNotification; import com.adityachandel.booklore.model.websocket.Topic; import com.adityachandel.booklore.service.user.UserService; import lombok.AllArgsConstructor; @@ -24,7 +25,7 @@ public class AdminEventBroadcaster { .filter(u -> u.getPermissions().isAdmin()) .toList(); for (BookLoreUser admin : admins) { - messagingTemplate.convertAndSendToUser(admin.getUsername(), Topic.LOG.getPath(), createLogNotification(message)); + messagingTemplate.convertAndSendToUser(admin.getUsername(), Topic.LOG.getPath(), LogNotification.info(message)); } } } diff --git a/booklore-api/src/main/java/com/adityachandel/booklore/service/event/BookEventBroadcaster.java b/booklore-api/src/main/java/com/adityachandel/booklore/service/event/BookEventBroadcaster.java index 9e35cacd2..28e4819ce 100644 --- a/booklore-api/src/main/java/com/adityachandel/booklore/service/event/BookEventBroadcaster.java +++ b/booklore-api/src/main/java/com/adityachandel/booklore/service/event/BookEventBroadcaster.java @@ -1,6 +1,7 @@ package com.adityachandel.booklore.service.event; import com.adityachandel.booklore.model.dto.Book; +import com.adityachandel.booklore.model.websocket.LogNotification; import com.adityachandel.booklore.model.websocket.Topic; import com.adityachandel.booklore.service.user.UserService; import lombok.AllArgsConstructor; @@ -26,7 +27,7 @@ public class BookEventBroadcaster { .forEach(u -> { String username = u.getUsername(); messagingTemplate.convertAndSendToUser(username, Topic.BOOK_ADD.getPath(), book); - messagingTemplate.convertAndSendToUser(username, Topic.LOG.getPath(), createLogNotification("Book added: " + book.getFileName())); + messagingTemplate.convertAndSendToUser(username, Topic.LOG.getPath(), LogNotification.info("Book added: " + book.getFileName())); }); } } diff --git a/booklore-api/src/main/java/com/adityachandel/booklore/service/library/LibraryProcessingService.java b/booklore-api/src/main/java/com/adityachandel/booklore/service/library/LibraryProcessingService.java index 88e0a6e4e..fa62e6881 100644 --- a/booklore-api/src/main/java/com/adityachandel/booklore/service/library/LibraryProcessingService.java +++ b/booklore-api/src/main/java/com/adityachandel/booklore/service/library/LibraryProcessingService.java @@ -5,6 +5,7 @@ import com.adityachandel.booklore.model.dto.settings.LibraryFile; import com.adityachandel.booklore.model.entity.BookAdditionalFileEntity; import com.adityachandel.booklore.model.entity.BookEntity; import com.adityachandel.booklore.model.entity.LibraryEntity; +import com.adityachandel.booklore.model.websocket.LogNotification; import com.adityachandel.booklore.model.websocket.Topic; import com.adityachandel.booklore.repository.BookAdditionalFileRepository; import com.adityachandel.booklore.repository.LibraryRepository; @@ -42,17 +43,17 @@ public class LibraryProcessingService { @Transactional public void processLibrary(long libraryId) throws IOException { LibraryEntity libraryEntity = libraryRepository.findById(libraryId).orElseThrow(() -> ApiError.LIBRARY_NOT_FOUND.createException(libraryId)); - notificationService.sendMessage(Topic.LOG, createLogNotification("Started processing library: " + libraryEntity.getName())); + notificationService.sendMessage(Topic.LOG, LogNotification.info("Started processing library: " + libraryEntity.getName())); LibraryFileProcessor processor = fileProcessorRegistry.getProcessor(libraryEntity); List libraryFiles = libraryFileHelper.getLibraryFiles(libraryEntity, processor); processor.processLibraryFiles(libraryFiles, libraryEntity); - notificationService.sendMessage(Topic.LOG, createLogNotification("Finished processing library: " + libraryEntity.getName())); + notificationService.sendMessage(Topic.LOG, LogNotification.info("Finished processing library: " + libraryEntity.getName())); } @Transactional public void rescanLibrary(RescanLibraryContext context) throws IOException { LibraryEntity libraryEntity = libraryRepository.findById(context.getLibraryId()).orElseThrow(() -> ApiError.LIBRARY_NOT_FOUND.createException(context.getLibraryId())); - notificationService.sendMessage(Topic.LOG, createLogNotification("Started refreshing library: " + libraryEntity.getName())); + notificationService.sendMessage(Topic.LOG, LogNotification.info("Started refreshing library: " + libraryEntity.getName())); LibraryFileProcessor processor = fileProcessorRegistry.getProcessor(libraryEntity); List libraryFiles = libraryFileHelper.getLibraryFiles(libraryEntity, processor); List additionalFileIds = detectDeletedAdditionalFiles(libraryFiles, libraryEntity); @@ -69,7 +70,7 @@ public class LibraryProcessingService { entityManager.clear(); processor.processLibraryFiles(detectNewBookPaths(libraryFiles, libraryEntity), libraryEntity); - notificationService.sendMessage(Topic.LOG, createLogNotification("Finished refreshing library: " + libraryEntity.getName())); + notificationService.sendMessage(Topic.LOG, LogNotification.info("Finished refreshing library: " + libraryEntity.getName())); } public void processLibraryFiles(List libraryFiles, LibraryEntity libraryEntity) { diff --git a/booklore-api/src/main/java/com/adityachandel/booklore/service/metadata/BookMetadataService.java b/booklore-api/src/main/java/com/adityachandel/booklore/service/metadata/BookMetadataService.java index 725024c10..435437a93 100644 --- a/booklore-api/src/main/java/com/adityachandel/booklore/service/metadata/BookMetadataService.java +++ b/booklore-api/src/main/java/com/adityachandel/booklore/service/metadata/BookMetadataService.java @@ -18,6 +18,7 @@ import com.adityachandel.booklore.model.entity.BookMetadataEntity; import com.adityachandel.booklore.model.enums.BookFileType; import com.adityachandel.booklore.model.enums.Lock; import com.adityachandel.booklore.model.enums.MetadataProvider; +import com.adityachandel.booklore.model.websocket.LogNotification; import com.adityachandel.booklore.model.websocket.Topic; import com.adityachandel.booklore.repository.BookMetadataRepository; import com.adityachandel.booklore.repository.BookRepository; @@ -192,7 +193,7 @@ public class BookMetadataService { .filter(book -> book.getMetadata().getCoverLocked() == null || !book.getMetadata().getCoverLocked()) .toList(); int total = books.size(); - notificationService.sendMessage(Topic.LOG, createLogNotification("Started regenerating covers for " + total + " books")); + notificationService.sendMessage(Topic.LOG, LogNotification.info("Started regenerating covers for " + total + " books")); int[] current = {1}; for (BookEntity book : books) { @@ -204,11 +205,10 @@ public class BookMetadataService { } current[0]++; } - - notificationService.sendMessage(Topic.LOG, createLogNotification("Finished regenerating covers")); + notificationService.sendMessage(Topic.LOG, LogNotification.info("Finished regenerating covers")); } catch (Exception e) { log.error("Error during cover regeneration: {}", e.getMessage(), e); - notificationService.sendMessage(Topic.LOG, createLogNotification("Error during cover regeneration: " + e.getMessage())); + notificationService.sendMessage(Topic.LOG, LogNotification.error("Error occurred during cover regeneration")); } }); } @@ -216,7 +216,7 @@ public class BookMetadataService { private void regenerateCoverForBook(BookEntity book, String progress) { String title = book.getMetadata().getTitle(); String message = progress + "Regenerating cover for: " + title; - notificationService.sendMessage(Topic.LOG, createLogNotification(message)); + notificationService.sendMessage(Topic.LOG, LogNotification.info(message)); BookFileProcessor processor = processorRegistry.getProcessorOrThrow(book.getBookType()); processor.generateCover(book); diff --git a/booklore-api/src/main/java/com/adityachandel/booklore/service/watcher/BookFileTransactionalHandler.java b/booklore-api/src/main/java/com/adityachandel/booklore/service/watcher/BookFileTransactionalHandler.java index 52f012565..83865c6c4 100644 --- a/booklore-api/src/main/java/com/adityachandel/booklore/service/watcher/BookFileTransactionalHandler.java +++ b/booklore-api/src/main/java/com/adityachandel/booklore/service/watcher/BookFileTransactionalHandler.java @@ -6,8 +6,7 @@ import com.adityachandel.booklore.model.entity.BookEntity; import com.adityachandel.booklore.model.entity.LibraryEntity; import com.adityachandel.booklore.model.entity.LibraryPathEntity; import com.adityachandel.booklore.model.enums.BookFileExtension; -import com.adityachandel.booklore.model.enums.BookFileType; -import com.adityachandel.booklore.model.enums.PermissionType; +import com.adityachandel.booklore.model.websocket.LogNotification; import com.adityachandel.booklore.model.websocket.Topic; import com.adityachandel.booklore.repository.LibraryRepository; import com.adityachandel.booklore.service.NotificationService; @@ -25,7 +24,6 @@ import java.util.Set; import static com.adityachandel.booklore.model.enums.PermissionType.ADMIN; import static com.adityachandel.booklore.model.enums.PermissionType.MANIPULATE_LIBRARY; -import static com.adityachandel.booklore.model.websocket.LogNotification.createLogNotification; @Slf4j @Service @@ -52,7 +50,7 @@ public class BookFileTransactionalHandler { String fileName = path.getFileName().toString(); String libraryPath = bookFilePersistenceService.findMatchingLibraryPath(libraryEntity, path); - notificationService.sendMessageToPermissions(Topic.LOG, createLogNotification("Started processing file: " + filePath), Set.of(ADMIN, MANIPULATE_LIBRARY)); + notificationService.sendMessageToPermissions(Topic.LOG, LogNotification.info("Started processing file: " + filePath), Set.of(ADMIN, MANIPULATE_LIBRARY)); LibraryPathEntity libraryPathEntity = bookFilePersistenceService.getLibraryPathEntityForFile(libraryEntity, libraryPath); @@ -68,7 +66,7 @@ public class BookFileTransactionalHandler { libraryProcessingService.processLibraryFiles(List.of(libraryFile), libraryEntity); - notificationService.sendMessageToPermissions(Topic.LOG, createLogNotification("Finished processing file: " + filePath), Set.of(ADMIN, MANIPULATE_LIBRARY)); + notificationService.sendMessageToPermissions(Topic.LOG, LogNotification.info("Finished processing file: " + filePath), Set.of(ADMIN, MANIPULATE_LIBRARY)); log.info("[CREATE] Completed processing for file '{}'", filePath); } } diff --git a/booklore-ui/src/app/features/metadata/component/book-metadata-center/metadata-viewer/metadata-viewer.component.ts b/booklore-ui/src/app/features/metadata/component/book-metadata-center/metadata-viewer/metadata-viewer.component.ts index ab10f9c11..a497c9535 100644 --- a/booklore-ui/src/app/features/metadata/component/book-metadata-center/metadata-viewer/metadata-viewer.component.ts +++ b/booklore-ui/src/app/features/metadata/component/book-metadata-center/metadata-viewer/metadata-viewer.component.ts @@ -19,7 +19,6 @@ import {takeUntilDestroyed} from '@angular/core/rxjs-interop'; import {Editor} from 'primeng/editor'; import {ProgressBar} from 'primeng/progressbar'; import {MetadataRefreshType} from '../../../model/request/metadata-refresh-type.enum'; -import {MetadataRefreshRequest} from '../../../model/request/metadata-refresh-request.model'; import {Router} from '@angular/router'; import {filter, map, switchMap, take, tap} from 'rxjs/operators'; import {Menu} from 'primeng/menu'; @@ -38,7 +37,6 @@ import {BookDialogHelperService} from '../../../../book/components/book-browser/ import {TagColor, TagComponent} from '../../../../../shared/components/tag/tag.component'; import {MetadataFetchOptionsComponent} from '../../metadata-options-dialog/metadata-fetch-options/metadata-fetch-options.component'; import {BookNotesComponent} from '../../../../book/components/book-notes/book-notes-component'; -import {TaskCreateRequest, TaskService, TaskType} from '../../../../settings/task-management/task.service'; import {TaskHelperService} from '../../../../settings/task-management/task-helper.service'; @Component({ diff --git a/booklore-ui/src/app/shared/components/live-notification-box/live-notification-box.component.html b/booklore-ui/src/app/shared/components/live-notification-box/live-notification-box.component.html index 175e51877..18b4564ee 100644 --- a/booklore-ui/src/app/shared/components/live-notification-box/live-notification-box.component.html +++ b/booklore-ui/src/app/shared/components/live-notification-box/live-notification-box.component.html @@ -1,4 +1,11 @@
-

{{ latestNotification.timestamp }}

+
+ @if (latestNotification.severity) { + + {{ latestNotification.severity }} + + } + {{ latestNotification.timestamp }} +

{{ latestNotification.message }}

diff --git a/booklore-ui/src/app/shared/components/live-notification-box/live-notification-box.component.ts b/booklore-ui/src/app/shared/components/live-notification-box/live-notification-box.component.ts index b8175583c..e12879288 100644 --- a/booklore-ui/src/app/shared/components/live-notification-box/live-notification-box.component.ts +++ b/booklore-ui/src/app/shared/components/live-notification-box/live-notification-box.component.ts @@ -1,6 +1,9 @@ import {Component, inject} from '@angular/core'; import {NotificationEventService} from '../../websocket/notification-event.service'; import {LogNotification} from '../../websocket/model/log-notification.model'; +import {Tag} from 'primeng/tag'; +import {NgIf} from '@angular/common'; +import {TagComponent} from '../tag/tag.component'; @Component({ selector: 'app-live-notification-box', @@ -10,6 +13,9 @@ import {LogNotification} from '../../websocket/model/log-notification.model'; host: { class: 'config-panel' }, + imports: [ + TagComponent + ] }) export class LiveNotificationBoxComponent { latestNotification: LogNotification = {message: 'No recent notifications...'}; @@ -21,4 +27,17 @@ export class LiveNotificationBoxComponent { this.latestNotification = notification; }); } + + getSeverityColor(severity?: string): 'red' | 'amber' | 'green' | 'gray' { + switch (severity) { + case 'ERROR': + return 'red'; + case 'WARN': + return 'amber'; + case 'INFO': + return 'green'; + default: + return 'gray'; + } + } } diff --git a/booklore-ui/src/app/shared/layout/component/layout-topbar/app.topbar.component.ts b/booklore-ui/src/app/shared/layout/component/layout-topbar/app.topbar.component.ts index e8dbaf6e5..eda1bf33d 100644 --- a/booklore-ui/src/app/shared/layout/component/layout-topbar/app.topbar.component.ts +++ b/booklore-ui/src/app/shared/layout/component/layout-topbar/app.topbar.component.ts @@ -24,6 +24,7 @@ import {BookdropFileService} from '../../../../features/bookdrop/service/bookdro import {DialogLauncherService} from '../../../services/dialog-launcher.service'; import {DuplicateFileService} from '../../../websocket/duplicate-file.service'; import {UnifiedNotificationBoxComponent} from '../../../components/unified-notification-popover/unified-notification-popover-component'; +import {Severity, LogNotification} from '../../../websocket/model/log-notification.model'; @Component({ selector: 'app-topbar', @@ -70,6 +71,7 @@ export class AppTopBarComponent implements OnDestroy { private latestTasks: { [taskId: string]: MetadataBatchProgressNotification } = {}; private latestHasPendingFiles = false; private latestHasDuplicateFiles = false; + private latestNotificationSeverity?: Severity; constructor( public layoutService: LayoutService, @@ -174,7 +176,8 @@ export class AppTopBarComponent implements OnDestroy { private subscribeToNotifications() { this.notificationService.latestNotification$ .pipe(takeUntil(this.destroy$)) - .subscribe(() => { + .subscribe((notification: LogNotification) => { + this.latestNotificationSeverity = notification.severity; this.triggerPulseEffect(); }); } @@ -227,9 +230,21 @@ export class AppTopBarComponent implements OnDestroy { } get iconColor(): string { - if (this.progressHighlight) return 'yellow'; - if (this.showPulse) return 'orange'; - if (this.completedTaskCount > 0 || this.hasPendingBookdropFiles || this.hasDuplicateFiles) return 'yellowgreen'; + if (this.progressHighlight) return 'gold'; + if (this.showPulse) { + switch (this.latestNotificationSeverity) { + case Severity.ERROR: + return 'crimson'; + case Severity.INFO: + return 'aqua'; + case Severity.WARN: + return 'orange'; + default: + return 'orange'; + } + } + if (this.completedTaskCount > 0 || this.hasPendingBookdropFiles || this.hasDuplicateFiles) + return 'limegreen'; return 'inherit'; } diff --git a/booklore-ui/src/app/shared/websocket/model/log-notification.model.ts b/booklore-ui/src/app/shared/websocket/model/log-notification.model.ts index d85506e74..28bdfaaa1 100644 --- a/booklore-ui/src/app/shared/websocket/model/log-notification.model.ts +++ b/booklore-ui/src/app/shared/websocket/model/log-notification.model.ts @@ -1,13 +1,13 @@ -export enum TaskStatus { - IN_PROGRESS = 'IN_PROGRESS', - CANCELLED = 'CANCELLED', - COMPLETED = 'COMPLETED', - FAILED = 'FAILED' +export enum Severity { + INFO = 'INFO', + WARN = 'WARN', + ERROR = 'ERROR' } export interface LogNotification { timestamp?: string; message: string; + severity?: Severity; } export function parseLogNotification(messageBody: string): LogNotification { @@ -15,22 +15,6 @@ export function parseLogNotification(messageBody: string): LogNotification { return { timestamp: raw.timestamp ? new Date(raw.timestamp).toLocaleTimeString() : undefined, message: raw.message, - }; -} - -export interface TaskMessage { - taskId: string; - timestamp: string; - title?: string; - message: string; - cancellable: boolean; - status: TaskStatus; -} - -export function parseTaskMessage(messageBody: string): TaskMessage { - const raw = JSON.parse(messageBody) as TaskMessage; - return { - ...raw, - timestamp: new Date(raw.timestamp).toLocaleTimeString() + severity: raw.severity ? Severity[raw.severity as keyof typeof Severity] : undefined }; }