diff --git a/package-lock.json b/package-lock.json
index d5a44ed..a9ebf78 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16,6 +16,7 @@
"@angular/platform-browser": "^16.1.0",
"@angular/platform-browser-dynamic": "^16.1.0",
"@angular/router": "^16.1.0",
+ "angular-date-value-accessor": "^3.0.0",
"book-monkey5-styles": "^1.0.4",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
@@ -4562,6 +4563,18 @@
"ajv": "^8.8.2"
}
},
+ "node_modules/angular-date-value-accessor": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/angular-date-value-accessor/-/angular-date-value-accessor-3.0.0.tgz",
+ "integrity": "sha512-1Oxm2fkKgleIQdZ50BiBSoMz7+qyQLI4rD+qllfJo7gCVIpkXLlWAObFUPVFte+SngdHSycH00bVMb8co8/lhw==",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "@angular/common": ">= 14.0.0",
+ "@angular/core": ">= 14.0.0"
+ }
+ },
"node_modules/ansi-colors": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
diff --git a/package.json b/package.json
index 2cf5e11..09818f5 100644
--- a/package.json
+++ b/package.json
@@ -20,6 +20,7 @@
"@angular/platform-browser": "^16.1.0",
"@angular/platform-browser-dynamic": "^16.1.0",
"@angular/router": "^16.1.0",
+ "angular-date-value-accessor": "^3.0.0",
"book-monkey5-styles": "^1.0.4",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
diff --git a/src/app/admin/admin-routing.module.ts b/src/app/admin/admin-routing.module.ts
index e49a121..38fdfc3 100644
--- a/src/app/admin/admin-routing.module.ts
+++ b/src/app/admin/admin-routing.module.ts
@@ -1,10 +1,12 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { BookCreateComponent } from './book-create/book-create.component';
+import { BookEditComponent } from './book-edit/book-edit.component';
const routes: Routes = [
- { path: 'admin', redirectTo: 'admin/create' },
- { path: 'admin/create', component: BookCreateComponent },
+ { path: '', redirectTo: 'create', pathMatch: 'full' },
+ { path: 'create', component: BookCreateComponent },
+ { path: 'edit/:isbn', component: BookEditComponent },
];
@NgModule({
diff --git a/src/app/admin/admin.module.ts b/src/app/admin/admin.module.ts
index 8914a58..a3425d3 100644
--- a/src/app/admin/admin.module.ts
+++ b/src/app/admin/admin.module.ts
@@ -2,15 +2,20 @@ import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AdminRoutingModule } from './admin-routing.module';
-import { FormsModule } from '@angular/forms';
+import { ReactiveFormsModule } from '@angular/forms';
+import { LocalIsoDateValueAccessor } from 'angular-date-value-accessor';
import { BookFormComponent } from './book-form/book-form.component';
import { BookCreateComponent } from './book-create/book-create.component';
+import { BookEditComponent } from './book-edit/book-edit.component';
+import { FormErrorsComponent } from './form-errors/form-errors.component';
@NgModule({
- declarations: [
- BookFormComponent,
- BookCreateComponent
+ declarations: [BookFormComponent, BookCreateComponent, BookEditComponent, FormErrorsComponent],
+ imports: [
+ CommonModule,
+ AdminRoutingModule,
+ ReactiveFormsModule,
+ LocalIsoDateValueAccessor,
],
- imports: [CommonModule, AdminRoutingModule, FormsModule],
})
export class AdminModule {}
diff --git a/src/app/admin/book-edit/book-edit.component.css b/src/app/admin/book-edit/book-edit.component.css
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/admin/book-edit/book-edit.component.html b/src/app/admin/book-edit/book-edit.component.html
new file mode 100644
index 0000000..b946658
--- /dev/null
+++ b/src/app/admin/book-edit/book-edit.component.html
@@ -0,0 +1,7 @@
+
Edit Book
+
+
diff --git a/src/app/admin/book-edit/book-edit.component.spec.ts b/src/app/admin/book-edit/book-edit.component.spec.ts
new file mode 100644
index 0000000..65c7be8
--- /dev/null
+++ b/src/app/admin/book-edit/book-edit.component.spec.ts
@@ -0,0 +1,21 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { BookEditComponent } from './book-edit.component';
+
+describe('BookEditComponent', () => {
+ let component: BookEditComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ declarations: [BookEditComponent]
+ });
+ fixture = TestBed.createComponent(BookEditComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/admin/book-edit/book-edit.component.ts b/src/app/admin/book-edit/book-edit.component.ts
new file mode 100644
index 0000000..35f8e6c
--- /dev/null
+++ b/src/app/admin/book-edit/book-edit.component.ts
@@ -0,0 +1,31 @@
+import { Component } from '@angular/core';
+import { ActivatedRoute, Router } from '@angular/router';
+import { map, Observable, switchMap } from 'rxjs';
+import { Book } from 'src/shared/book';
+import { BookStoreService } from 'src/app/shared/book-store.service';
+
+@Component({
+ selector: 'bm-book-edit',
+ templateUrl: './book-edit.component.html',
+ styleUrls: ['./book-edit.component.css'],
+})
+export class BookEditComponent {
+ book$: Observable;
+
+ constructor(
+ private service: BookStoreService,
+ private route: ActivatedRoute,
+ private router: Router,
+ ) {
+ this.book$ = this.route.paramMap.pipe(
+ map((params) => params.get('isbn')!),
+ switchMap((isbn) => this.service.getSingle(isbn)),
+ );
+ }
+
+ update(book: Book) {
+ this.service.update(book).subscribe((updatedBook) => {
+ this.router.navigate(['/books', updatedBook.isbn]);
+ });
+ }
+}
diff --git a/src/app/admin/book-form/book-form.component.html b/src/app/admin/book-form/book-form.component.html
index b084601..c6f6c07 100644
--- a/src/app/admin/book-form/book-form.component.html
+++ b/src/app/admin/book-form/book-form.component.html
@@ -1,19 +1,56 @@
-
+
+
diff --git a/src/app/admin/book-form/book-form.component.ts b/src/app/admin/book-form/book-form.component.ts
index 25afafa..c69d558 100644
--- a/src/app/admin/book-form/book-form.component.ts
+++ b/src/app/admin/book-form/book-form.component.ts
@@ -1,21 +1,94 @@
-import { Component, Output, EventEmitter } from '@angular/core';
+import {
+ Component,
+ Output,
+ EventEmitter,
+ OnChanges,
+ Input,
+ inject,
+} from '@angular/core';
import { Book } from 'src/shared/book';
+import { FormControl, FormGroup, Validators } from '@angular/forms';
+import { FormArray } from '@angular/forms';
+import { atLeastOneValue, isbnFormat } from '../shared/validators';
+import { AsyncValidatorsService } from '../shared/async-validators.service';
@Component({
selector: 'bm-book-form',
templateUrl: './book-form.component.html',
styleUrls: ['./book-form.component.css'],
})
-export class BookFormComponent {
+export class BookFormComponent implements OnChanges {
@Output() submitBook = new EventEmitter();
+ @Input() book?: Book;
+
+ form = new FormGroup({
+ title: new FormControl('', {
+ nonNullable: true,
+ validators: Validators.required,
+ }),
+ subtitle: new FormControl('', { nonNullable: true }),
+ isbn: new FormControl('', {
+ nonNullable: true,
+ validators: [Validators.required, isbnFormat],
+ asyncValidators: inject(AsyncValidatorsService).isbnExists(),
+ }),
+ description: new FormControl('', { nonNullable: true }),
+ published: new FormControl('', { nonNullable: true }),
+ thumbnailUrl: new FormControl('', { nonNullable: true }),
+ authors: this.buildAuthorsArray(['']),
+ });
submitForm() {
- this.submitBook.emit(this.book);
+ const formValue = this.form.getRawValue();
+ const authors = formValue.authors.filter((author) => !!author);
+ const newBook: Book = {
+ ...formValue,
+ authors, // TODO:echte Eingabe
+ };
+ this.submitBook.emit(newBook);
}
- book: Book = {
- isbn: '',
- title: '',
- authors: [''],
- };
+ // book: Book = {
+ // isbn: '',
+ // title: '',
+ // authors: [''],
+ // };
+
+ get authors() {
+ return this.form.controls.authors;
+ }
+
+ addAuthorControl() {
+ this.authors.push(new FormControl('', { nonNullable: true }));
+ }
+
+ private buildAuthorsArray(authors: string[]) {
+ return new FormArray(
+ authors.map((v) => new FormControl(v, { nonNullable: true })),
+ atLeastOneValue,
+ );
+ }
+
+ ngOnChanges(): void {
+ if (this.book) {
+ this.setFormValues(this.book);
+ this.setEditMode(true);
+ } else {
+ this.setEditMode(false);
+ }
+ }
+
+ private setEditMode(isEditing: boolean) {
+ const isbnControle = this.form.controls.isbn;
+ if (isEditing) {
+ isbnControle.disable();
+ } else {
+ isbnControle.enable();
+ }
+ }
+
+ private setFormValues(book: Book) {
+ this.form.patchValue(book);
+ this.form.setControl('authors', this.buildAuthorsArray(book.authors));
+ }
}
diff --git a/src/app/admin/form-errors/form-errors.component.css b/src/app/admin/form-errors/form-errors.component.css
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/admin/form-errors/form-errors.component.html b/src/app/admin/form-errors/form-errors.component.html
new file mode 100644
index 0000000..308f3e0
--- /dev/null
+++ b/src/app/admin/form-errors/form-errors.component.html
@@ -0,0 +1 @@
+{{ error }}
diff --git a/src/app/admin/form-errors/form-errors.component.spec.ts b/src/app/admin/form-errors/form-errors.component.spec.ts
new file mode 100644
index 0000000..c337c3f
--- /dev/null
+++ b/src/app/admin/form-errors/form-errors.component.spec.ts
@@ -0,0 +1,21 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { FormErrorsComponent } from './form-errors.component';
+
+describe('FormErrorsComponent', () => {
+ let component: FormErrorsComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ declarations: [FormErrorsComponent]
+ });
+ fixture = TestBed.createComponent(FormErrorsComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/admin/form-errors/form-errors.component.ts b/src/app/admin/form-errors/form-errors.component.ts
new file mode 100644
index 0000000..ba808d0
--- /dev/null
+++ b/src/app/admin/form-errors/form-errors.component.ts
@@ -0,0 +1,30 @@
+import { Component, Input } from '@angular/core';
+import { FormGroupDirective } from '@angular/forms';
+
+@Component({
+ selector: 'bm-form-errors',
+ templateUrl: './form-errors.component.html',
+ styleUrls: ['./form-errors.component.css'],
+})
+export class FormErrorsComponent {
+ @Input() controlName?: string;
+ @Input() messages: { [errorCode: string]: string } = {};
+
+ constructor(private form: FormGroupDirective) {}
+
+ get errors(): string[] {
+ if (!this.controlName) {
+ return [];
+ }
+
+ const control = this.form.control.get(this.controlName);
+
+ if (!control || !control.errors || !control.touched) {
+ return [];
+ }
+
+ return Object.keys(control.errors).map((errorCode) => {
+ return this.messages[errorCode];
+ });
+ }
+}
diff --git a/src/app/admin/shared/async-validators.service.spec.ts b/src/app/admin/shared/async-validators.service.spec.ts
new file mode 100644
index 0000000..9f280e1
--- /dev/null
+++ b/src/app/admin/shared/async-validators.service.spec.ts
@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { AsyncValidatorsService } from './async-validators.service';
+
+describe('AsyncValidatorsService', () => {
+ let service: AsyncValidatorsService;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({});
+ service = TestBed.inject(AsyncValidatorsService);
+ });
+
+ it('should be created', () => {
+ expect(service).toBeTruthy();
+ });
+});
diff --git a/src/app/admin/shared/async-validators.service.ts b/src/app/admin/shared/async-validators.service.ts
new file mode 100644
index 0000000..60615b1
--- /dev/null
+++ b/src/app/admin/shared/async-validators.service.ts
@@ -0,0 +1,19 @@
+import { Injectable } from '@angular/core';
+import { AsyncValidatorFn } from '@angular/forms';
+import { map } from 'rxjs';
+import { BookStoreService } from 'src/app/shared/book-store.service';
+
+@Injectable({
+ providedIn: 'root',
+})
+export class AsyncValidatorsService {
+ constructor(private service: BookStoreService) {}
+
+ isbnExists(): AsyncValidatorFn {
+ return (control) => {
+ return this.service
+ .check(control.value)
+ .pipe(map((exists) => (exists ? { isbnexists: true } : null)));
+ };
+ }
+}
diff --git a/src/app/admin/shared/validators.ts b/src/app/admin/shared/validators.ts
new file mode 100644
index 0000000..08f36b1
--- /dev/null
+++ b/src/app/admin/shared/validators.ts
@@ -0,0 +1,26 @@
+import { isFormArray, ValidatorFn } from '@angular/forms';
+
+export const atLeastOneValue: ValidatorFn = function (control) {
+ if (!isFormArray(control)) {
+ return null;
+ }
+ if (control.controls.some((el) => !!el.value)) {
+ return null;
+ } else {
+ return { atleastonevalue: true };
+ }
+};
+
+export const isbnFormat: ValidatorFn = function (control) {
+ if (!control.value || typeof control.value !== 'string') {
+ return null;
+ }
+ const isbnWithoutDashes = control.value.replace(/-/g, '');
+ const length = isbnWithoutDashes.length;
+
+ if (length === 10 || length === 13) {
+ return null;
+ } else {
+ return { isbnformat: true };
+ }
+};
diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts
index 7506485..a673e6f 100644
--- a/src/app/app-routing.module.ts
+++ b/src/app/app-routing.module.ts
@@ -1,10 +1,22 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HomeComponent } from './home/home.component';
+import { authGuard } from './shared/auth.guard';
const routes: Routes = [
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'home', component: HomeComponent },
+ {
+ path: 'books',
+ loadChildren: () =>
+ import('./books/books.module').then((m) => m.BooksModule),
+ },
+ {
+ path: 'admin',
+ loadChildren: () =>
+ import('./admin/admin.module').then((m) => m.AdminModule),
+ canActivate: [authGuard],
+ },
];
@NgModule({
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 450cbff..c696cf2 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -3,16 +3,14 @@ import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
-import { BooksModule } from './books/books.module';
import { HomeComponent } from './home/home.component';
import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
import { SearchComponent } from './search/search.component';
import { AuthInterceptor } from './shared/auth.interceptor';
-import { AdminModule } from './admin/admin.module';
@NgModule({
declarations: [AppComponent, HomeComponent, SearchComponent],
- imports: [BrowserModule, AppRoutingModule, BooksModule, HttpClientModule, AdminModule],
+ imports: [BrowserModule, AppRoutingModule, HttpClientModule],
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
],
diff --git a/src/app/books/book-details/book-details.component.html b/src/app/books/book-details/book-details.component.html
index 33a4ea3..b9ddbda 100644
--- a/src/app/books/book-details/book-details.component.html
+++ b/src/app/books/book-details/book-details.component.html
@@ -1,6 +1,7 @@
{{ book.title }}
{{ book.subtilte }}
+
+
Description
{{ book.description }}
Back to list
-
+
+
+
+ Edit book
+
diff --git a/src/app/books/book-details/book-details.component.ts b/src/app/books/book-details/book-details.component.ts
index b30955a..e2e6c8e 100644
--- a/src/app/books/book-details/book-details.component.ts
+++ b/src/app/books/book-details/book-details.component.ts
@@ -21,10 +21,8 @@ export class BookDetailsComponent {
this.book$ = this.service.getSingle(isbn);
}
removeBook(isbn: string) {
- if (window.confirm('Remove book?')) {
- this.service.remove(isbn).subscribe(() => {
- this.router.navigateByUrl('/books');
- });
- }
+ this.service.remove(isbn).subscribe(() => {
+ this.router.navigateByUrl('/books');
+ });
}
}
diff --git a/src/app/books/book-list-item/book-list-item.component.html b/src/app/books/book-list-item/book-list-item.component.html
index 6fae28b..cd379a5 100644
--- a/src/app/books/book-list-item/book-list-item.component.html
+++ b/src/app/books/book-list-item/book-list-item.component.html
@@ -9,5 +9,5 @@
{{ author }}
- ISBN {{ book.isbn }}
+ ISBN {{ book.isbn | isbn }}
diff --git a/src/app/books/books-routing.module.ts b/src/app/books/books-routing.module.ts
index feacd0d..f98a14e 100644
--- a/src/app/books/books-routing.module.ts
+++ b/src/app/books/books-routing.module.ts
@@ -4,8 +4,8 @@ import { BookDetailsComponent } from './book-details/book-details.component';
import { BookListComponent } from './book-list/book-list.component';
const routes: Routes = [
- { path: 'books', component: BookListComponent },
- { path: 'books/:isbn', component: BookDetailsComponent },
+ { path: '', component: BookListComponent },
+ { path: ':isbn', component: BookDetailsComponent },
];
@NgModule({
diff --git a/src/app/books/books.module.ts b/src/app/books/books.module.ts
index e27a098..71d6711 100644
--- a/src/app/books/books.module.ts
+++ b/src/app/books/books.module.ts
@@ -5,12 +5,18 @@ import { BooksRoutingModule } from './books-routing.module';
import { BookListComponent } from './book-list/book-list.component';
import { BookListItemComponent } from './book-list-item/book-list-item.component';
import { BookDetailsComponent } from './book-details/book-details.component';
+import { IsbnPipe } from './shared/isbn.pipe';
+import { ConfirmDirective } from './shared/confirm.directive';
+import { LoggedinOnlyDirective } from './shared/loggedin-only.directive';
@NgModule({
declarations: [
BookDetailsComponent,
BookListComponent,
BookListItemComponent,
+ IsbnPipe,
+ ConfirmDirective,
+ LoggedinOnlyDirective,
],
imports: [CommonModule, BooksRoutingModule],
exports: [],
diff --git a/src/app/books/shared/confirm.directive.spec.ts b/src/app/books/shared/confirm.directive.spec.ts
new file mode 100644
index 0000000..665f262
--- /dev/null
+++ b/src/app/books/shared/confirm.directive.spec.ts
@@ -0,0 +1,8 @@
+import { ConfirmDirective } from './confirm.directive';
+
+describe('ConfirmDirective', () => {
+ it('should create an instance', () => {
+ const directive = new ConfirmDirective();
+ expect(directive).toBeTruthy();
+ });
+});
diff --git a/src/app/books/shared/confirm.directive.ts b/src/app/books/shared/confirm.directive.ts
new file mode 100644
index 0000000..1472e3f
--- /dev/null
+++ b/src/app/books/shared/confirm.directive.ts
@@ -0,0 +1,21 @@
+import {
+ Directive,
+ Input,
+ Output,
+ EventEmitter,
+ HostListener,
+} from '@angular/core';
+
+@Directive({
+ selector: '[bmConfirm]',
+})
+export class ConfirmDirective {
+ @Input('bmConform') confirmText?: string;
+ @Output() confirm = new EventEmitter();
+ @HostListener('click') onClick() {
+ if (window.confirm(this.confirmText)) {
+ this.confirm.emit();
+ }
+ }
+ constructor() {}
+}
diff --git a/src/app/books/shared/isbn.pipe.spec.ts b/src/app/books/shared/isbn.pipe.spec.ts
new file mode 100644
index 0000000..54fdf72
--- /dev/null
+++ b/src/app/books/shared/isbn.pipe.spec.ts
@@ -0,0 +1,8 @@
+import { IsbnPipe } from './isbn.pipe';
+
+describe('IsbnPipe', () => {
+ it('create an instance', () => {
+ const pipe = new IsbnPipe();
+ expect(pipe).toBeTruthy();
+ });
+});
diff --git a/src/app/books/shared/isbn.pipe.ts b/src/app/books/shared/isbn.pipe.ts
new file mode 100644
index 0000000..71aa825
--- /dev/null
+++ b/src/app/books/shared/isbn.pipe.ts
@@ -0,0 +1,13 @@
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({
+ name: 'isbn',
+})
+export class IsbnPipe implements PipeTransform {
+ transform(value: string): string {
+ if (!value) {
+ return '';
+ }
+ return `${value.substring(0, 3)}-${value.substring(3)}`;
+ }
+}
diff --git a/src/app/books/shared/loggedin-only.directive.spec.ts b/src/app/books/shared/loggedin-only.directive.spec.ts
new file mode 100644
index 0000000..8c444dc
--- /dev/null
+++ b/src/app/books/shared/loggedin-only.directive.spec.ts
@@ -0,0 +1,8 @@
+import { LoggedinOnlyDirective } from './loggedin-only.directive';
+
+describe('LoggedinOnlyDirective', () => {
+ it('should create an instance', () => {
+ const directive = new LoggedinOnlyDirective();
+ expect(directive).toBeTruthy();
+ });
+});
diff --git a/src/app/books/shared/loggedin-only.directive.ts b/src/app/books/shared/loggedin-only.directive.ts
new file mode 100644
index 0000000..88ea715
--- /dev/null
+++ b/src/app/books/shared/loggedin-only.directive.ts
@@ -0,0 +1,34 @@
+import {
+ Directive,
+ OnDestroy,
+ ViewContainerRef,
+ TemplateRef,
+} from '@angular/core';
+import { Subject, takeUntil } from 'rxjs';
+import { AuthService } from 'src/app/shared/auth.service';
+
+@Directive({
+ selector: '[bmLoggedinOnly]',
+})
+export class LoggedinOnlyDirective implements OnDestroy {
+ private destroy$ = new Subject();
+
+ constructor(
+ private template: TemplateRef,
+ private viewContainer: ViewContainerRef,
+ private authService: AuthService,
+ ) {
+ this.authService.isAuthenticated$
+ .pipe(takeUntil(this.destroy$))
+ .subscribe((isAuthenticated) => {
+ if (isAuthenticated) {
+ this.viewContainer.createEmbeddedView(this.template);
+ } else {
+ this.viewContainer.clear();
+ }
+ });
+ }
+ ngOnDestroy(): void {
+ this.destroy$.next();
+ }
+}
diff --git a/src/app/shared/auth.guard.spec.ts b/src/app/shared/auth.guard.spec.ts
new file mode 100644
index 0000000..4ae275e
--- /dev/null
+++ b/src/app/shared/auth.guard.spec.ts
@@ -0,0 +1,17 @@
+import { TestBed } from '@angular/core/testing';
+import { CanActivateFn } from '@angular/router';
+
+import { authGuard } from './auth.guard';
+
+describe('authGuard', () => {
+ const executeGuard: CanActivateFn = (...guardParameters) =>
+ TestBed.runInInjectionContext(() => authGuard(...guardParameters));
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({});
+ });
+
+ it('should be created', () => {
+ expect(executeGuard).toBeTruthy();
+ });
+});
diff --git a/src/app/shared/auth.guard.ts b/src/app/shared/auth.guard.ts
new file mode 100644
index 0000000..87cebb7
--- /dev/null
+++ b/src/app/shared/auth.guard.ts
@@ -0,0 +1,21 @@
+import { Inject, inject } from '@angular/core';
+import { CanActivateFn, Router } from '@angular/router';
+import { AuthService } from './auth.service';
+import { map, take } from 'rxjs';
+
+export const authGuard: CanActivateFn = (route, state) => {
+ const authService = inject(AuthService);
+ const router = inject(Router);
+
+ return authService.isAuthenticated$.pipe(
+ take(1),
+ map((isAuthenticated) => {
+ if (authService.isAuthenticated) {
+ return true;
+ } else {
+ window.alert('Not logged in!');
+ return router.parseUrl('home');
+ }
+ }),
+ );
+};
diff --git a/src/app/shared/book-store.service.ts b/src/app/shared/book-store.service.ts
index f42e72c..da89a8c 100644
--- a/src/app/shared/book-store.service.ts
+++ b/src/app/shared/book-store.service.ts
@@ -40,4 +40,12 @@ export class BookStoreService {
create(book: Book): Observable {
return this.http.post(`${this.apiUrl}/books`, book);
}
+
+ update(book: Book): Observable {
+ return this.http.put(`${this.apiUrl}/book/${book.isbn}`, book);
+ }
+
+ check(isbn: string): Observable {
+ return this.http.get(`${this.apiUrl}/books/${isbn}/check`);
+ }
}