diff --git a/src/app/admin/admin-routing.module.ts b/src/app/admin/admin-routing.module.ts new file mode 100644 index 0000000..e49a121 --- /dev/null +++ b/src/app/admin/admin-routing.module.ts @@ -0,0 +1,14 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { BookCreateComponent } from './book-create/book-create.component'; + +const routes: Routes = [ + { path: 'admin', redirectTo: 'admin/create' }, + { path: 'admin/create', component: BookCreateComponent }, +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], +}) +export class AdminRoutingModule {} diff --git a/src/app/admin/admin.module.ts b/src/app/admin/admin.module.ts new file mode 100644 index 0000000..8914a58 --- /dev/null +++ b/src/app/admin/admin.module.ts @@ -0,0 +1,16 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { AdminRoutingModule } from './admin-routing.module'; +import { FormsModule } from '@angular/forms'; +import { BookFormComponent } from './book-form/book-form.component'; +import { BookCreateComponent } from './book-create/book-create.component'; + +@NgModule({ + declarations: [ + BookFormComponent, + BookCreateComponent + ], + imports: [CommonModule, AdminRoutingModule, FormsModule], +}) +export class AdminModule {} diff --git a/src/app/admin/book-create/book-create.component.css b/src/app/admin/book-create/book-create.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/admin/book-create/book-create.component.html b/src/app/admin/book-create/book-create.component.html new file mode 100644 index 0000000..8b7d594 --- /dev/null +++ b/src/app/admin/book-create/book-create.component.html @@ -0,0 +1,3 @@ +

Create Book

+ + diff --git a/src/app/admin/book-create/book-create.component.spec.ts b/src/app/admin/book-create/book-create.component.spec.ts new file mode 100644 index 0000000..c24f07a --- /dev/null +++ b/src/app/admin/book-create/book-create.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { BookCreateComponent } from './book-create.component'; + +describe('BookCreateComponent', () => { + let component: BookCreateComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [BookCreateComponent] + }); + fixture = TestBed.createComponent(BookCreateComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/admin/book-create/book-create.component.ts b/src/app/admin/book-create/book-create.component.ts new file mode 100644 index 0000000..c4bae86 --- /dev/null +++ b/src/app/admin/book-create/book-create.component.ts @@ -0,0 +1,22 @@ +import { Component } from '@angular/core'; +import { Router } from '@angular/router'; +import { BookStoreService } from 'src/app/shared/book-store.service'; +import { Book } from 'src/shared/book'; + +@Component({ + selector: 'bm-book-create', + templateUrl: './book-create.component.html', + styleUrls: ['./book-create.component.css'], +}) +export class BookCreateComponent { + constructor( + private service: BookStoreService, + private router: Router, + ) {} + + create(book: Book) { + this.service.create(book).subscribe((createdBook) => { + this.router.navigate(['/books', createdBook.isbn]); + }); + } +} diff --git a/src/app/admin/book-form/book-form.component.css b/src/app/admin/book-form/book-form.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/admin/book-form/book-form.component.html b/src/app/admin/book-form/book-form.component.html new file mode 100644 index 0000000..b084601 --- /dev/null +++ b/src/app/admin/book-form/book-form.component.html @@ -0,0 +1,19 @@ +
+ + + + + + + + + + +
diff --git a/src/app/admin/book-form/book-form.component.spec.ts b/src/app/admin/book-form/book-form.component.spec.ts new file mode 100644 index 0000000..15f2e4a --- /dev/null +++ b/src/app/admin/book-form/book-form.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { BookFormComponent } from './book-form.component'; + +describe('BookFormComponent', () => { + let component: BookFormComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [BookFormComponent] + }); + fixture = TestBed.createComponent(BookFormComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/admin/book-form/book-form.component.ts b/src/app/admin/book-form/book-form.component.ts new file mode 100644 index 0000000..25afafa --- /dev/null +++ b/src/app/admin/book-form/book-form.component.ts @@ -0,0 +1,21 @@ +import { Component, Output, EventEmitter } from '@angular/core'; +import { Book } from 'src/shared/book'; + +@Component({ + selector: 'bm-book-form', + templateUrl: './book-form.component.html', + styleUrls: ['./book-form.component.css'], +}) +export class BookFormComponent { + @Output() submitBook = new EventEmitter(); + + submitForm() { + this.submitBook.emit(this.book); + } + + book: Book = { + isbn: '', + title: '', + authors: [''], + }; +} diff --git a/src/app/app.component.html b/src/app/app.component.html index 635289f..3c6e160 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,10 +1,21 @@
diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 97d0803..06bca4a 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,19 +1,12 @@ import { Component } from '@angular/core'; -import { Book } from 'src/shared/book'; - +import { AuthService } from './shared/auth.service'; @Component({ selector: 'bm-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'], }) export class AppComponent { - title = 'book-monkey'; - book: Book | null = null; + constructor(public auth: AuthService) {} - showList() { - this.book = null; - } - showDetails(book: Book) { - this.book = book; - } + title = 'book-monkey'; } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 1237d76..450cbff 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -5,12 +5,17 @@ import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { BooksModule } from './books/books.module'; import { HomeComponent } from './home/home.component'; -import { HttpClientModule } from '@angular/common/http'; +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], - imports: [BrowserModule, AppRoutingModule, BooksModule, HttpClientModule], - providers: [], + declarations: [AppComponent, HomeComponent, SearchComponent], + imports: [BrowserModule, AppRoutingModule, BooksModule, HttpClientModule, AdminModule], + providers: [ + { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }, + ], bootstrap: [AppComponent], }) export class AppModule {} diff --git a/src/app/books/book-list/book-list.component.ts b/src/app/books/book-list/book-list.component.ts index 2d8147e..f33a0c7 100644 --- a/src/app/books/book-list/book-list.component.ts +++ b/src/app/books/book-list/book-list.component.ts @@ -15,8 +15,4 @@ export class BookListComponent { constructor(private service: BookStoreService) { this.books$ = this.service.getAll(); } - - doSelect(book: Book) { - this.selectBook.emit(book); - } } diff --git a/src/app/home/home.component.html b/src/app/home/home.component.html index d872ed6..649a058 100644 --- a/src/app/home/home.component.html +++ b/src/app/home/home.component.html @@ -1,3 +1,6 @@

Home

Show book list + +

Search

+ diff --git a/src/app/search/search.component.css b/src/app/search/search.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/search/search.component.html b/src/app/search/search.component.html new file mode 100644 index 0000000..acfdeb2 --- /dev/null +++ b/src/app/search/search.component.html @@ -0,0 +1,18 @@ + + + diff --git a/src/app/search/search.component.spec.ts b/src/app/search/search.component.spec.ts new file mode 100644 index 0000000..94a4cfa --- /dev/null +++ b/src/app/search/search.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SearchComponent } from './search.component'; + +describe('SearchComponent', () => { + let component: SearchComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [SearchComponent] + }); + fixture = TestBed.createComponent(SearchComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/search/search.component.ts b/src/app/search/search.component.ts new file mode 100644 index 0000000..3becc34 --- /dev/null +++ b/src/app/search/search.component.ts @@ -0,0 +1,34 @@ +import { Component } from '@angular/core'; +import { + Subject, + debounceTime, + distinctUntilChanged, + filter, + switchMap, + tap, + Observable, +} from 'rxjs'; +import { BookStoreService } from '../shared/book-store.service'; +import { Book } from 'src/shared/book'; + +@Component({ + selector: 'bm-search', + templateUrl: './search.component.html', + styleUrls: ['./search.component.css'], +}) +export class SearchComponent { + input$ = new Subject(); + isLoading = false; + results$: Observable; + + constructor(private service: BookStoreService) { + this.results$ = this.input$.pipe( + filter((term) => term.length >= 3), + debounceTime(500), + distinctUntilChanged(), + tap(() => (this.isLoading = true)), + switchMap((term) => this.service.getAllSearch(term)), + tap(() => (this.isLoading = false)), + ); + } +} diff --git a/src/app/shared/auth.interceptor.spec.ts b/src/app/shared/auth.interceptor.spec.ts new file mode 100644 index 0000000..7ab58db --- /dev/null +++ b/src/app/shared/auth.interceptor.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { AuthInterceptor } from './auth.interceptor'; + +describe('AuthInterceptor', () => { + beforeEach(() => TestBed.configureTestingModule({ + providers: [ + AuthInterceptor + ] + })); + + it('should be created', () => { + const interceptor: AuthInterceptor = TestBed.inject(AuthInterceptor); + expect(interceptor).toBeTruthy(); + }); +}); diff --git a/src/app/shared/auth.interceptor.ts b/src/app/shared/auth.interceptor.ts new file mode 100644 index 0000000..4630512 --- /dev/null +++ b/src/app/shared/auth.interceptor.ts @@ -0,0 +1,30 @@ +import { Injectable } from '@angular/core'; +import { + HttpRequest, + HttpHandler, + HttpEvent, + HttpInterceptor, +} from '@angular/common/http'; +import { Observable } from 'rxjs'; +import { AuthService } from './auth.service'; + +@Injectable() +export class AuthInterceptor implements HttpInterceptor { + constructor(private authService: AuthService) {} + intercept( + request: HttpRequest, + next: HttpHandler, + ): Observable> { + const token = '1234567890'; + if (this.authService.isAuthenticated) { + //token in hearder einfügen + const reqWithToken = request.clone({ + setHeaders: { Authorization: `Bearer${token}` }, + }); + return next.handle(reqWithToken); + } else { + //Request unverändert weitergeben + return next.handle(request); + } + } +} diff --git a/src/app/shared/auth.service.spec.ts b/src/app/shared/auth.service.spec.ts new file mode 100644 index 0000000..f1251ca --- /dev/null +++ b/src/app/shared/auth.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { AuthService } from './auth.service'; + +describe('AuthService', () => { + let service: AuthService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(AuthService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/shared/auth.service.ts b/src/app/shared/auth.service.ts new file mode 100644 index 0000000..e235c84 --- /dev/null +++ b/src/app/shared/auth.service.ts @@ -0,0 +1,24 @@ +import { Injectable } from '@angular/core'; +import { BehaviorSubject } from 'rxjs'; + +@Injectable({ + providedIn: 'root', +}) +export class AuthService { + private _isAuthenticated$ = new BehaviorSubject(true); + readonly isAuthenticated$ = this._isAuthenticated$.asObservable(); + + constructor() {} + + get isAuthenticated() { + return this._isAuthenticated$.value; + } + + login() { + this._isAuthenticated$.next(true); + } + + logout() { + this._isAuthenticated$.next(false); + } +} diff --git a/src/app/shared/book-store.service.ts b/src/app/shared/book-store.service.ts index f054a21..f42e72c 100644 --- a/src/app/shared/book-store.service.ts +++ b/src/app/shared/book-store.service.ts @@ -1,6 +1,6 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { Observable } from 'rxjs'; +import { Observable, catchError, of } from 'rxjs'; import { Book } from 'src/shared/book'; @Injectable({ @@ -12,7 +12,12 @@ export class BookStoreService { constructor(private http: HttpClient) {} getAll(): Observable { - return this.http.get(`${this.apiUrl}/books`); + return this.http.get(`${this.apiUrl}/books`).pipe( + catchError((err) => { + console.error(err); + return of([]); + }), + ); } getSingle(isbn: string): Observable { @@ -22,4 +27,17 @@ export class BookStoreService { remove(isbn: string): Observable { return this.http.delete(`${this.apiUrl}/books/${isbn}`); } + + getAllSearch(term: string): Observable { + return this.http.get(`${this.apiUrl}/books/search/${term}`).pipe( + catchError((err) => { + console.error(err); + return of([]); + }), + ); + } + + create(book: Book): Observable { + return this.http.post(`${this.apiUrl}/books`, book); + } }