mirror of
https://github.com/booklore-app/booklore.git
synced 2025-12-23 22:28:11 -05:00
Icon support for library and shelf
This commit is contained in:
@@ -11,7 +11,6 @@ import lombok.AllArgsConstructor;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ public class LibraryDTO {
|
||||
private Long id;
|
||||
private String name;
|
||||
private Sort sort;
|
||||
private String icon;
|
||||
private List<String> paths;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,5 +13,6 @@ import java.time.Instant;
|
||||
public class ShelfDTO {
|
||||
private Long id;
|
||||
private String name;
|
||||
private String icon;
|
||||
private Sort sort;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.adityachandel.booklore.model.dto.request;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
@@ -10,6 +12,10 @@ import java.util.List;
|
||||
@Builder
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class CreateLibraryRequest {
|
||||
@NotBlank
|
||||
private String name;
|
||||
@NotBlank
|
||||
private String icon;
|
||||
@NotEmpty
|
||||
private List<String> paths;
|
||||
}
|
||||
|
||||
@@ -12,4 +12,7 @@ public class ShelfCreateRequest {
|
||||
|
||||
@NotBlank(message = "Shelf name must not be empty.")
|
||||
private String name;
|
||||
|
||||
@NotBlank(message = "Shelf icon must not be empty.")
|
||||
private String icon;
|
||||
}
|
||||
|
||||
@@ -31,4 +31,6 @@ public class Library {
|
||||
|
||||
@OneToMany(mappedBy = "library", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
private List<Book> books;
|
||||
|
||||
private String icon;
|
||||
}
|
||||
|
||||
@@ -28,4 +28,6 @@ public class Shelf {
|
||||
|
||||
@ManyToMany(mappedBy = "shelves", fetch = FetchType.LAZY)
|
||||
private Set<Book> books = new HashSet<>();
|
||||
|
||||
private String icon;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,11 @@ public class LibraryService {
|
||||
private final LibraryProcessingService libraryProcessingService;
|
||||
|
||||
public LibraryDTO createLibrary(CreateLibraryRequest request) {
|
||||
Library library = Library.builder().name(request.getName()).paths(request.getPaths()).build();
|
||||
Library library = Library.builder()
|
||||
.name(request.getName())
|
||||
.paths(request.getPaths())
|
||||
.icon(request.getIcon())
|
||||
.build();
|
||||
library = libraryRepository.save(library);
|
||||
Long libraryId = library.getId();
|
||||
Thread.startVirtualThread(() -> {
|
||||
|
||||
@@ -28,7 +28,7 @@ public class ShelfService {
|
||||
if (exists) {
|
||||
throw ApiError.SHELF_ALREADY_EXISTS.createException(request.getName());
|
||||
}
|
||||
Shelf shelf = Shelf.builder().name(request.getName()).build();
|
||||
Shelf shelf = Shelf.builder().icon(request.getIcon()).name(request.getName()).build();
|
||||
return ShelfTransformer.convertToShelfDTO(shelfRepository.save(shelf));
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ public class LibraryTransformer {
|
||||
.id(library.getId())
|
||||
.name(library.getName())
|
||||
.sort(library.getSort())
|
||||
.icon(library.getIcon())
|
||||
.paths(library.getPaths())
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ public class ShelfTransformer {
|
||||
.id(shelf.getId())
|
||||
.name(shelf.getName())
|
||||
.sort(shelf.getSort())
|
||||
.icon(shelf.getIcon())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,9 @@ CREATE TABLE IF NOT EXISTS library
|
||||
(
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(255) UNIQUE NOT NULL,
|
||||
paths TEXT,
|
||||
sort VARCHAR(255) NULL
|
||||
sort VARCHAR(255) NULL,
|
||||
icon VARCHAR(64) NOT NULL,
|
||||
paths TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS book
|
||||
@@ -85,7 +86,8 @@ CREATE TABLE IF NOT EXISTS shelf
|
||||
(
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL UNIQUE,
|
||||
sort VARCHAR(255) NULL
|
||||
sort VARCHAR(255) NULL,
|
||||
icon VARCHAR(64) NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS book_shelf_mapping
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
INSERT INTO booklore.shelf (name) VALUES ('Favorites');
|
||||
INSERT INTO booklore.shelf (name) VALUES ('Read Later');
|
||||
INSERT INTO booklore.shelf (name, icon) VALUES ('Favorites', 'heart');
|
||||
@@ -36,6 +36,7 @@ import {SpeedDialModule} from 'primeng/speeddial';
|
||||
import {RouteReuseStrategy} from '@angular/router';
|
||||
import {CustomReuseStrategy} from './custom-reuse-strategy';
|
||||
import {MenuModule} from 'primeng/menu';
|
||||
import {IconPickerComponent} from './book/component/icon-picker/icon-picker.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@@ -71,6 +72,7 @@ import {MenuModule} from 'primeng/menu';
|
||||
BookCardComponent,
|
||||
SpeedDialModule,
|
||||
MenuModule,
|
||||
IconPickerComponent,
|
||||
],
|
||||
providers: [
|
||||
DialogService,
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
label="Add a Library"
|
||||
icon="pi pi-plus"
|
||||
styleClass="p-button-rounded p-button-outlined"
|
||||
(click)="createNewLibrary($event)">
|
||||
(click)="createNewLibrary()">
|
||||
</p-button>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {Component} from '@angular/core';
|
||||
import {Button, ButtonDirective} from 'primeng/button';
|
||||
import {Button} from 'primeng/button';
|
||||
import {AsyncPipe, NgIf} from '@angular/common';
|
||||
import {LibraryCreatorComponent} from '../library-creator/library-creator.component';
|
||||
import {DialogService, DynamicDialogRef} from 'primeng/dynamicdialog';
|
||||
@@ -7,7 +7,6 @@ import {DashboardScrollerComponent} from '../dashboard-scroller/dashboard-scroll
|
||||
import {LibraryService} from '../../service/library.service';
|
||||
import {Observable} from 'rxjs';
|
||||
import {map} from 'rxjs/operators';
|
||||
import {IconPickerComponent} from '../icon-picker/icon-picker.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-home-page',
|
||||
@@ -17,8 +16,7 @@ import {IconPickerComponent} from '../icon-picker/icon-picker.component';
|
||||
Button,
|
||||
NgIf,
|
||||
DashboardScrollerComponent,
|
||||
AsyncPipe,
|
||||
IconPickerComponent
|
||||
AsyncPipe
|
||||
],
|
||||
providers: [DialogService],
|
||||
})
|
||||
@@ -32,21 +30,13 @@ export class DashboardComponent {
|
||||
);
|
||||
}
|
||||
|
||||
createNewLibrary(event: MouseEvent) {
|
||||
const buttonRect = (event.target as HTMLElement).getBoundingClientRect();
|
||||
const dialogWidthPercentage = 50;
|
||||
const viewportWidth = window.innerWidth;
|
||||
const dialogWidth = (dialogWidthPercentage / 100) * viewportWidth;
|
||||
const leftPosition = buttonRect.left + (buttonRect.width / 2) - (dialogWidth / 2);
|
||||
createNewLibrary() {
|
||||
this.ref = this.dialogService.open(LibraryCreatorComponent, {
|
||||
header: 'Create New Library',
|
||||
modal: true,
|
||||
width: `${dialogWidthPercentage}%`,
|
||||
height: 'auto',
|
||||
style: {
|
||||
position: 'absolute',
|
||||
top: `${buttonRect.bottom + 10}px`,
|
||||
left: `${Math.max(leftPosition, 0)}px`
|
||||
},
|
||||
width: '50%',
|
||||
height: '50%',
|
||||
style: {bottom: '15%'}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
font-size: 36px;
|
||||
}
|
||||
|
||||
.icon-search {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {Component} from '@angular/core';
|
||||
import {Component, EventEmitter, Output} from '@angular/core';
|
||||
import {DialogModule} from 'primeng/dialog';
|
||||
import {NgForOf} from '@angular/common';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
@@ -14,10 +14,9 @@ import {FormsModule} from '@angular/forms';
|
||||
],
|
||||
})
|
||||
export class IconPickerComponent {
|
||||
iconDialogVisible: boolean = true;
|
||||
iconDialogVisible: boolean = false;
|
||||
selectedIcon: string | null = null;
|
||||
searchText: string = '';
|
||||
|
||||
iconCategories: string[] = [
|
||||
"address-book", "align-center", "align-justify", "align-left", "align-right", "android",
|
||||
"angle-double-down", "angle-double-left", "angle-double-right", "angle-double-up", "angle-down", "angle-left",
|
||||
@@ -53,6 +52,8 @@ export class IconPickerComponent {
|
||||
|
||||
icons: string[] = this.createIconList(this.iconCategories);
|
||||
|
||||
@Output() iconSelected = new EventEmitter<string>();
|
||||
|
||||
createIconList(categories: string[]): string[] {
|
||||
return categories.map(iconName => `pi pi-${iconName}`);
|
||||
}
|
||||
@@ -64,14 +65,14 @@ export class IconPickerComponent {
|
||||
return this.icons.filter(icon => icon.toLowerCase().includes(this.searchText.toLowerCase()));
|
||||
}
|
||||
|
||||
openIconDialog() {
|
||||
open() {
|
||||
this.iconDialogVisible = true;
|
||||
}
|
||||
|
||||
selectIcon(icon: string) {
|
||||
this.selectedIcon = icon;
|
||||
this.iconDialogVisible = false;
|
||||
console.log('Selected Icon:', icon);
|
||||
this.iconSelected.emit(icon);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ export class AppMenuComponent implements OnInit {
|
||||
separator: false,
|
||||
items: libraries.map((library) => ({
|
||||
label: library.name,
|
||||
icon: 'pi pi-fw pi-book',
|
||||
icon: 'pi pi-' + library.icon,
|
||||
routerLink: [`/library/${library.id}/books`],
|
||||
})),
|
||||
},
|
||||
@@ -42,7 +42,7 @@ export class AppMenuComponent implements OnInit {
|
||||
separator: false,
|
||||
items: shelves.map((shelf) => ({
|
||||
label: shelf.name,
|
||||
icon: 'pi pi-fw pi-heart',
|
||||
icon: 'pi pi-' + shelf.icon,
|
||||
routerLink: [`/shelf/${shelf.id}/books`],
|
||||
})),
|
||||
},
|
||||
|
||||
@@ -40,7 +40,7 @@ export class AppTopBarComponent implements OnDestroy {
|
||||
this.ref = this.dialogService.open(LibraryCreatorComponent, {
|
||||
header: 'Create New Library',
|
||||
modal: true,
|
||||
width: '50%',
|
||||
width: '40%',
|
||||
height: '50%',
|
||||
style: {bottom: '15%'}
|
||||
});
|
||||
|
||||
@@ -2,27 +2,35 @@
|
||||
<p-stepperPanel header="Library Name">
|
||||
<ng-template pTemplate="content" let-nextCallback="nextCallback" let-index="index">
|
||||
<div class="flex flex-column h-21rem">
|
||||
<div
|
||||
class="border-2 border-dashed surface-border border-round surface-ground flex-auto flex justify-content-center align-items-center font-medium">
|
||||
<input
|
||||
type="text"
|
||||
pInputText
|
||||
[(ngModel)]="value"
|
||||
placeholder="Enter library name..."/>
|
||||
<div class="border-2 border-dashed surface-border border-round surface-ground flex-auto flex justify-content-center align-items-center font-medium">
|
||||
|
||||
<div class="library-name-icon-parent">
|
||||
<div class="library-name-div">
|
||||
<div>Library Name: </div>
|
||||
<input type="text" pInputText [(ngModel)]="value" placeholder="Enter library name..."/>
|
||||
</div>
|
||||
<div class="library-icon-div">
|
||||
<div>Library Icon:</div>
|
||||
<div *ngIf="!selectedIcon">
|
||||
<p-button label="Select Icon" icon="pi pi-search" (onClick)="openIconPicker()"></p-button>
|
||||
</div>
|
||||
<div *ngIf="selectedIcon" class="selected-icon-container">
|
||||
<i [class]="selectedIcon" style="font-size: 1.5rem; margin-right: 0.5rem;"></i>
|
||||
<p-button icon="pi pi-times" (onClick)="clearSelectedIcon()" [rounded]="true" [text]="true" [outlined]="true" class="remove-icon-button" severity="danger"></p-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<app-icon-picker (iconSelected)="onIconSelected($event)"></app-icon-picker>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex pt-4 justify-content-end">
|
||||
<p-button
|
||||
label="Next"
|
||||
icon="pi pi-arrow-right"
|
||||
iconPos="right"
|
||||
(onClick)="nextCallback.emit()"/>
|
||||
<p-button label="Next" icon="pi pi-arrow-right" iconPos="right" (onClick)="nextCallback.emit()"/>
|
||||
</div>
|
||||
</ng-template>
|
||||
</p-stepperPanel>
|
||||
<p-stepperPanel header="Media Location">
|
||||
<ng-template pTemplate="content" let-prevCallback="prevCallback" let-index="index">
|
||||
<div class="flex flex-column">
|
||||
<div class="flex flex-column xxx">
|
||||
<div
|
||||
class="border-2 border-dashed surface-border border-round surface-ground flex-auto flex align-items-start"
|
||||
style="padding-top: 2.5rem;">
|
||||
|
||||
@@ -1,26 +1,51 @@
|
||||
.container {
|
||||
.library-name-icon-parent {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.library-name-div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.textbox {
|
||||
padding: 10px;
|
||||
margin-bottom: 20px;
|
||||
.library-icon-div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.button {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
left: 10px;
|
||||
.library-name-div label,
|
||||
.library-icon-div label {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.tr-custom {
|
||||
padding: 0.25rem 0.5rem !important;
|
||||
border: 0px !important;
|
||||
.library-name-div input {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.library-icon-div p-button {
|
||||
margin-left: 0.8rem;
|
||||
}
|
||||
|
||||
.p-stepperPanel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.p-stepperPanel .flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.p-stepperPanel .justify-content-end {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.p-stepperPanel .mt-3 {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.p-table-custom {
|
||||
@@ -30,15 +55,37 @@
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
.td-x-custom {
|
||||
text-align: right;
|
||||
.tr-custom {
|
||||
padding: 0.25rem 0.5rem !important;
|
||||
border: 0px !important;
|
||||
}
|
||||
|
||||
.mt-3 {
|
||||
margin-top: 1rem;
|
||||
.td-x-custom {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.w-100 {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.p-button {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
.selected-icon-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: 1rem;
|
||||
|
||||
i {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.remove-icon-button {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.xxx {
|
||||
min-height: 295px;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import {Component} from '@angular/core';
|
||||
import {Component, ViewChild} from '@angular/core';
|
||||
import {DialogService, DynamicDialogRef} from 'primeng/dynamicdialog';
|
||||
import {DirectoryPickerComponent} from '../directory-picker/directory-picker.component';
|
||||
import {MessageService} from 'primeng/api';
|
||||
import {Router} from '@angular/router';
|
||||
import {LibraryService} from '../../service/library.service';
|
||||
import {IconPickerComponent} from '../icon-picker/icon-picker.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-library-creator',
|
||||
@@ -16,13 +17,15 @@ export class LibraryCreatorComponent {
|
||||
value: string = '';
|
||||
folders: string[] = [];
|
||||
ref: DynamicDialogRef | undefined;
|
||||
@ViewChild(IconPickerComponent) iconPicker: IconPickerComponent | undefined;
|
||||
|
||||
selectedIcon: string | null = null;
|
||||
|
||||
constructor(
|
||||
private dialogService: DialogService,
|
||||
private dynamicDialogRef: DynamicDialogRef,
|
||||
private libraryService: LibraryService,
|
||||
private router: Router,
|
||||
) {
|
||||
private router: Router) {
|
||||
}
|
||||
|
||||
show() {
|
||||
@@ -50,10 +53,25 @@ export class LibraryCreatorComponent {
|
||||
this.folders.splice(index, 1);
|
||||
}
|
||||
|
||||
openIconPicker() {
|
||||
if (this.iconPicker) {
|
||||
this.iconPicker.open();
|
||||
}
|
||||
}
|
||||
|
||||
onIconSelected(icon: string) {
|
||||
this.selectedIcon = icon;
|
||||
}
|
||||
|
||||
clearSelectedIcon() {
|
||||
this.selectedIcon = null;
|
||||
}
|
||||
|
||||
addLibrary() {
|
||||
const newLibrary = {
|
||||
name: this.value,
|
||||
paths: this.folders,
|
||||
icon: this.selectedIcon ? this.selectedIcon.replace('pi pi-', '') : 'heart'
|
||||
};
|
||||
this.libraryService.createLibrary(newLibrary).subscribe({
|
||||
next: (createdLibrary) => {
|
||||
@@ -93,4 +111,5 @@ export class LibraryCreatorComponent {
|
||||
);
|
||||
}
|
||||
}*/
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<div class="dialog-container">
|
||||
<div class="checkbox-list">
|
||||
<div *ngFor="let shelf of shelves$ | async" class="flex items-center">
|
||||
<p-checkbox [inputId]="shelf.name" name="group" [value]="shelf" [(ngModel)]="selectedShelves" />
|
||||
<p-checkbox [inputId]="shelf.name" name="group" [value]="shelf" [(ngModel)]="selectedShelves"/>
|
||||
<label [for]="shelf.id" class="ml-2">{{ shelf.name }}</label>
|
||||
</div>
|
||||
</div>
|
||||
@@ -17,13 +17,23 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p-dialog [(visible)]="displayShelfDialog" header="Create Shelf" [modal]="true" [closable]="true" (onHide)="closeShelfDialog()" [style]="{ width: '400px', height: 'auto' }">
|
||||
<app-icon-picker (iconSelected)="onIconSelected($event)"></app-icon-picker>
|
||||
<p-dialog [(visible)]="displayShelfDialog" header="Create Shelf" [modal]="true" [draggable]="false" [closable]="true" (onHide)="closeShelfDialog()" [style]="{ width: '400px', height: 'auto' }">
|
||||
<div class="p-fluid">
|
||||
<div class="field">
|
||||
<label for="shelfName">Shelf Name</label>
|
||||
<input id="shelfName" type="text" pInputText [(ngModel)]="shelfName"/>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Shelf Icon</label>
|
||||
<div *ngIf="!selectedIcon">
|
||||
<p-button label="Select Icon" icon="pi pi-search" (onClick)="openIconPicker()"></p-button>
|
||||
</div>
|
||||
<div *ngIf="selectedIcon" class="mt-2 flex align-items-center">
|
||||
<i [class]="selectedIcon" class="mr-2"></i>
|
||||
<p-button label="Remove" icon="pi pi-times" [text]="true" [outlined]="true" severity="warning" (onClick)="clearSelectedIcon()"></p-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ng-template pTemplate="footer">
|
||||
<p-button label="Cancel" severity="secondary" (onClick)="closeShelfDialog()" class="p-button-text"></p-button>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {Component, OnInit, ViewChild} from '@angular/core';
|
||||
import {DynamicDialogConfig, DynamicDialogRef} from 'primeng/dynamicdialog';
|
||||
import {Book} from '../../model/book.model';
|
||||
import {MessageService} from 'primeng/api';
|
||||
@@ -7,6 +7,7 @@ import {Observable} from 'rxjs';
|
||||
import {BookService} from '../../service/book.service';
|
||||
import {map, tap} from 'rxjs/operators';
|
||||
import {Shelf} from '../../model/shelf.model';
|
||||
import {IconPickerComponent} from '../icon-picker/icon-picker.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-shelf-assigner',
|
||||
@@ -22,6 +23,8 @@ export class ShelfAssignerComponent implements OnInit {
|
||||
shelfName: string = '';
|
||||
bookIds: Set<number> = new Set();
|
||||
isMultiBooks: boolean = false;
|
||||
selectedIcon: string | null = null;
|
||||
@ViewChild(IconPickerComponent) iconPicker: IconPickerComponent | undefined;
|
||||
|
||||
constructor(
|
||||
private shelfService: ShelfService,
|
||||
@@ -46,7 +49,11 @@ export class ShelfAssignerComponent implements OnInit {
|
||||
}
|
||||
|
||||
saveNewShelf(): void {
|
||||
this.shelfService.createShelf(this.shelfName).subscribe(
|
||||
const newShelf = {
|
||||
name: this.shelfName,
|
||||
icon: this.selectedIcon ? this.selectedIcon.replace('pi pi-', '') : 'heart'
|
||||
};
|
||||
this.shelfService.createShelf(newShelf).subscribe(
|
||||
() => {
|
||||
this.messageService.add({severity: 'info', summary: 'Success', detail: 'Shelf created: ' + this.shelfName});
|
||||
this.displayShelfDialog = false;
|
||||
@@ -99,4 +106,19 @@ export class ShelfAssignerComponent implements OnInit {
|
||||
closeDialog(): void {
|
||||
this.dynamicDialogRef.close();
|
||||
}
|
||||
|
||||
openIconPicker() {
|
||||
if (this.iconPicker) {
|
||||
this.iconPicker.open();
|
||||
}
|
||||
}
|
||||
|
||||
clearSelectedIcon() {
|
||||
this.selectedIcon = null;
|
||||
}
|
||||
|
||||
onIconSelected(icon: string) {
|
||||
this.selectedIcon = icon;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import {SortOption} from './sort.model';
|
||||
export interface Library {
|
||||
id?: number;
|
||||
name: string;
|
||||
icon: string;
|
||||
sort?: SortOption;
|
||||
paths: string[];
|
||||
}
|
||||
|
||||
@@ -3,5 +3,6 @@ import {SortOption} from './sort.model';
|
||||
export interface Shelf {
|
||||
id?: number;
|
||||
name: string;
|
||||
icon: string;
|
||||
sort?: SortOption;
|
||||
}
|
||||
|
||||
@@ -25,8 +25,8 @@ export class ShelfService {
|
||||
});
|
||||
}
|
||||
|
||||
createShelf(name: string): Observable<Shelf> {
|
||||
return this.http.post<Shelf>(this.url, {name}).pipe(
|
||||
createShelf(shelf: Shelf): Observable<Shelf> {
|
||||
return this.http.post<Shelf>(this.url, shelf).pipe(
|
||||
map(newShelf => {
|
||||
this.shelves.next([...this.shelves.value, newShelf])
|
||||
return newShelf;
|
||||
@@ -56,14 +56,14 @@ export class ShelfService {
|
||||
|
||||
deleteShelf(shelfId: number) {
|
||||
return this.http.delete<void>(`${this.url}/${shelfId}`).pipe(
|
||||
tap(() => {
|
||||
this.bookService.removeBooksByLibraryId(shelfId);
|
||||
let shelves = this.shelves.value.filter(shelf => shelf.id !== shelfId);
|
||||
this.shelves.next(shelves);
|
||||
}),
|
||||
catchError(error => {
|
||||
return of();
|
||||
})
|
||||
tap(() => {
|
||||
this.bookService.removeBooksByLibraryId(shelfId);
|
||||
let shelves = this.shelves.value.filter(shelf => shelf.id !== shelfId);
|
||||
this.shelves.next(shelves);
|
||||
}),
|
||||
catchError(error => {
|
||||
return of();
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user