diff --git a/src/Client/angular.json b/src/Client/angular.json index d78fba2..f29e931 100644 --- a/src/Client/angular.json +++ b/src/Client/angular.json @@ -16,9 +16,14 @@ "outputPath": "dist/cbc", "index": "src/index.html", "browser": "src/main.ts", - "polyfills": ["zone.js"], + "polyfills": [ + "zone.js" + ], "tsConfig": "tsconfig.app.json", - "assets": ["src/favicon.ico", "src/assets"], + "assets": [ + "src/favicon.png", + "src/assets" + ], "styles": [ "node_modules/bootstrap/dist/css/bootstrap.min.css", "src/styles.css" @@ -100,24 +105,37 @@ "test": { "builder": "@angular-devkit/build-angular:karma", "options": { - "polyfills": ["zone.js", "zone.js/testing"], + "polyfills": [ + "zone.js", + "zone.js/testing" + ], "tsConfig": "tsconfig.spec.json", - "assets": ["src/favicon.ico", "src/assets"], - "styles": ["src/styles.css"], + "assets": [ + "src/favicon.png", + "src/assets" + ], + "styles": [ + "src/styles.css" + ], "scripts": [] } }, "lint": { "builder": "@angular-eslint/builder:lint", "options": { - "lintFilePatterns": ["src/**/*.ts", "src/**/*.html"] + "lintFilePatterns": [ + "src/**/*.ts", + "src/**/*.html" + ] } } } } }, "cli": { - "schematicCollections": ["@angular-eslint/schematics"], + "schematicCollections": [ + "@angular-eslint/schematics" + ], "analytics": false } } diff --git a/src/Client/package-lock.json b/src/Client/package-lock.json index fc87843..3dcdc10 100644 --- a/src/Client/package-lock.json +++ b/src/Client/package-lock.json @@ -20,6 +20,8 @@ "@ng-bootstrap/ng-bootstrap": "^16.0.0", "@popperjs/core": "^2.11.8", "bootstrap": "^5.3.2", + "bootstrap-icons": "^1.11.2", + "lodash": "^4.17.21", "moment": "^2.29.4", "rxjs": "~7.8.0", "tslib": "^2.3.0", @@ -36,6 +38,7 @@ "@angular/compiler-cli": "^17.0.7", "@angular/localize": "^17.0.7", "@types/jasmine": "~5.1.0", + "@types/lodash": "^4.14.202", "@typescript-eslint/eslint-plugin": "6.13.1", "@typescript-eslint/parser": "6.13.1", "eslint": "^8.54.0", @@ -3902,6 +3905,12 @@ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, + "node_modules/@types/lodash": { + "version": "4.14.202", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.202.tgz", + "integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==", + "dev": true + }, "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", @@ -4989,6 +4998,21 @@ "@popperjs/core": "^2.11.8" } }, + "node_modules/bootstrap-icons": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.11.2.tgz", + "integrity": "sha512-TgdiPv+IM9tgDb+dsxrnGIyocsk85d2M7T0qIgkvPedZeoZfyeG/j+yiAE4uHCEayKef2RP05ahQ0/e9Sv75Wg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ] + }, "node_modules/bplist-parser": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", @@ -10149,8 +10173,7 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "node_modules/lodash.debounce": { "version": "4.0.8", @@ -18317,6 +18340,12 @@ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, + "@types/lodash": { + "version": "4.14.202", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.202.tgz", + "integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==", + "dev": true + }, "@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", @@ -19144,6 +19173,11 @@ "integrity": "sha512-D32nmNWiQHo94BKHLmOrdjlL05q1c8oxbtBphQFb9Z5to6eGRDCm0QgeaZ4zFBHzfg2++rqa2JkqCcxDy0sH0g==", "requires": {} }, + "bootstrap-icons": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.11.2.tgz", + "integrity": "sha512-TgdiPv+IM9tgDb+dsxrnGIyocsk85d2M7T0qIgkvPedZeoZfyeG/j+yiAE4uHCEayKef2RP05ahQ0/e9Sv75Wg==" + }, "bplist-parser": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", @@ -22954,8 +22988,7 @@ "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "lodash.debounce": { "version": "4.0.8", diff --git a/src/Client/package.json b/src/Client/package.json index 403125a..35a5f44 100644 --- a/src/Client/package.json +++ b/src/Client/package.json @@ -28,6 +28,8 @@ "@ng-bootstrap/ng-bootstrap": "^16.0.0", "@popperjs/core": "^2.11.8", "bootstrap": "^5.3.2", + "bootstrap-icons": "^1.11.2", + "lodash": "^4.17.21", "moment": "^2.29.4", "rxjs": "~7.8.0", "tslib": "^2.3.0", @@ -44,6 +46,7 @@ "@angular/compiler-cli": "^17.0.7", "@angular/localize": "^17.0.7", "@types/jasmine": "~5.1.0", + "@types/lodash": "^4.14.202", "@typescript-eslint/eslint-plugin": "6.13.1", "@typescript-eslint/parser": "6.13.1", "eslint": "^8.54.0", diff --git a/src/Client/src/app/api/fn/books/get-users-books-plain.ts b/src/Client/src/app/api/fn/books/get-users-books-plain.ts new file mode 100644 index 0000000..cf2eece --- /dev/null +++ b/src/Client/src/app/api/fn/books/get-users-books-plain.ts @@ -0,0 +1,29 @@ +/* tslint:disable */ +/* eslint-disable */ +import { HttpClient, HttpContext, HttpResponse } from '@angular/common/http'; +import { Observable } from 'rxjs'; +import { filter, map } from 'rxjs/operators'; +import { StrictHttpResponse } from '../../strict-http-response'; +import { RequestBuilder } from '../../request-builder'; + +import { BookDto } from '../../models/book-dto'; + +export interface GetUsersBooks$Plain$Params { +} + +export function getUsersBooks$Plain(http: HttpClient, rootUrl: string, params?: GetUsersBooks$Plain$Params, context?: HttpContext): Observable>> { + const rb = new RequestBuilder(rootUrl, getUsersBooks$Plain.PATH, 'get'); + if (params) { + } + + return http.request( + rb.build({ responseType: 'text', accept: 'text/plain', context }) + ).pipe( + filter((r: any): r is HttpResponse => r instanceof HttpResponse), + map((r: HttpResponse) => { + return r as StrictHttpResponse>; + }) + ); +} + +getUsersBooks$Plain.PATH = '/api/Books/mine'; diff --git a/src/Client/src/app/api/fn/books/get-users-books.ts b/src/Client/src/app/api/fn/books/get-users-books.ts new file mode 100644 index 0000000..2002431 --- /dev/null +++ b/src/Client/src/app/api/fn/books/get-users-books.ts @@ -0,0 +1,29 @@ +/* tslint:disable */ +/* eslint-disable */ +import { HttpClient, HttpContext, HttpResponse } from '@angular/common/http'; +import { Observable } from 'rxjs'; +import { filter, map } from 'rxjs/operators'; +import { StrictHttpResponse } from '../../strict-http-response'; +import { RequestBuilder } from '../../request-builder'; + +import { BookDto } from '../../models/book-dto'; + +export interface GetUsersBooks$Params { +} + +export function getUsersBooks(http: HttpClient, rootUrl: string, params?: GetUsersBooks$Params, context?: HttpContext): Observable>> { + const rb = new RequestBuilder(rootUrl, getUsersBooks.PATH, 'get'); + if (params) { + } + + return http.request( + rb.build({ responseType: 'json', accept: 'text/json', context }) + ).pipe( + filter((r: any): r is HttpResponse => r instanceof HttpResponse), + map((r: HttpResponse) => { + return r as StrictHttpResponse>; + }) + ); +} + +getUsersBooks.PATH = '/api/Books/mine'; diff --git a/src/Client/src/app/api/services/books.service.ts b/src/Client/src/app/api/services/books.service.ts index 5c897e7..32d5602 100644 --- a/src/Client/src/app/api/services/books.service.ts +++ b/src/Client/src/app/api/services/books.service.ts @@ -22,6 +22,10 @@ import { getBooks } from '../fn/books/get-books'; import { GetBooks$Params } from '../fn/books/get-books'; import { getBooks$Plain } from '../fn/books/get-books-plain'; import { GetBooks$Plain$Params } from '../fn/books/get-books-plain'; +import { getUsersBooks } from '../fn/books/get-users-books'; +import { GetUsersBooks$Params } from '../fn/books/get-users-books'; +import { getUsersBooks$Plain } from '../fn/books/get-users-books-plain'; +import { GetUsersBooks$Plain$Params } from '../fn/books/get-users-books-plain'; @Injectable({ providedIn: 'root' }) export class BooksService extends BaseService { @@ -123,6 +127,53 @@ export class BooksService extends BaseService { ); } + /** Path part for operation `getUsersBooks()` */ + static readonly GetUsersBooksPath = '/api/Books/mine'; + + /** + * This method provides access to the full `HttpResponse`, allowing access to response headers. + * To access only the response body, use `getUsersBooks$Plain()` instead. + * + * This method doesn't expect any request body. + */ + getUsersBooks$Plain$Response(params?: GetUsersBooks$Plain$Params, context?: HttpContext): Observable>> { + return getUsersBooks$Plain(this.http, this.rootUrl, params, context); + } + + /** + * This method provides access only to the response body. + * To access the full response (for headers, for example), `getUsersBooks$Plain$Response()` instead. + * + * This method doesn't expect any request body. + */ + getUsersBooks$Plain(params?: GetUsersBooks$Plain$Params, context?: HttpContext): Observable> { + return this.getUsersBooks$Plain$Response(params, context).pipe( + map((r: StrictHttpResponse>): Array => r.body) + ); + } + + /** + * This method provides access to the full `HttpResponse`, allowing access to response headers. + * To access only the response body, use `getUsersBooks()` instead. + * + * This method doesn't expect any request body. + */ + getUsersBooks$Response(params?: GetUsersBooks$Params, context?: HttpContext): Observable>> { + return getUsersBooks(this.http, this.rootUrl, params, context); + } + + /** + * This method provides access only to the response body. + * To access the full response (for headers, for example), `getUsersBooks$Response()` instead. + * + * This method doesn't expect any request body. + */ + getUsersBooks(params?: GetUsersBooks$Params, context?: HttpContext): Observable> { + return this.getUsersBooks$Response(params, context).pipe( + map((r: StrictHttpResponse>): Array => r.body) + ); + } + /** Path part for operation `getBook()` */ static readonly GetBookPath = '/api/Books/{id}'; diff --git a/src/Client/src/app/app.component.css b/src/Client/src/app/app.component.css index 78d5838..bd2c016 100644 --- a/src/Client/src/app/app.component.css +++ b/src/Client/src/app/app.component.css @@ -1,4 +1,16 @@ footer { + position: relative; background-color: #89aed1; width: 100%; + z-index: 1000; +} + +footer a { + color: yellow; +} + +.content { + position: relative; + top: var(--navbar-height); + margin-bottom: var(--navbar-height); } diff --git a/src/Client/src/app/app.component.html b/src/Client/src/app/app.component.html index 1cbe7d2..a552723 100644 --- a/src/Client/src/app/app.component.html +++ b/src/Client/src/app/app.component.html @@ -1,5 +1,6 @@ - -
+ + +
@@ -23,7 +24,9 @@ Hosted with ❤️ from Lancaster, Pennsylvania, USA Copyright © Luke Westfall {{ year }} - - cbc@ljdub.comcbc@ljdub.com
diff --git a/src/Client/src/app/app.component.ts b/src/Client/src/app/app.component.ts index 6d1d7b0..15df139 100644 --- a/src/Client/src/app/app.component.ts +++ b/src/Client/src/app/app.component.ts @@ -1,29 +1,36 @@ import { AsyncPipe } from '@angular/common'; -import { Component } from '@angular/core'; +import { Component, ElementRef } from '@angular/core'; import { ChildrenOutletContexts, RouterOutlet } from '@angular/router'; import { slideInAnimation } from './animations'; import { UserDto } from './api/models'; import { MeetingsModule } from './meetings/meetings.module'; import { NavbarComponent } from './navbar/navbar.component'; import { AuthService } from './services/auth.service'; +import { ToastsContainerComponent } from './toasts-container/toasts-container.component'; @Component({ selector: 'app-root', standalone: true, - imports: [RouterOutlet, NavbarComponent, MeetingsModule, AsyncPipe], + imports: [ + RouterOutlet, + NavbarComponent, + MeetingsModule, + AsyncPipe, + ToastsContainerComponent, + ], templateUrl: './app.component.html', styleUrl: './app.component.css', animations: [slideInAnimation], }) export class AppComponent { - // title = 'cbc'; year = new Date().getFullYear(); user?: UserDto; verified = false; constructor( authService: AuthService, - private contexts: ChildrenOutletContexts + private contexts: ChildrenOutletContexts, + private elementRef: ElementRef ) { authService.apiUser$.subscribe(user => { this.user = user; @@ -36,4 +43,11 @@ export class AppComponent { 'animation' ]; } + + onNavbarHeightChange(height: number) { + this.elementRef.nativeElement.style.setProperty( + '--navbar-height', + `${height}px` + ); + } } diff --git a/src/Client/src/app/app.routes.ts b/src/Client/src/app/app.routes.ts index 832b5af..95268b6 100644 --- a/src/Client/src/app/app.routes.ts +++ b/src/Client/src/app/app.routes.ts @@ -12,6 +12,16 @@ const canActivateVerified: CanActivateFn = () => { return false; }; +const canActivateAdmin: CanActivateFn = () => { + if (inject(AuthService).isAdmin()) { + return true; + } else { + inject(Router).navigate(['/']); + } + + return false; +}; + export const routes: Routes = [ { path: '', @@ -22,12 +32,21 @@ export const routes: Routes = [ { path: 'books', loadComponent: () => - import('./books-page/books-page.component').then( + import('./books/books-page/books-page.component').then( mod => mod.BooksPageComponent ), canActivate: [canActivateVerified], data: { animation: 'BooksPage' }, }, + { + path: 'users', + loadComponent: () => + import('./users/users-page/users-page.component').then( + mod => mod.UsersPageComponent + ), + canActivate: [canActivateAdmin], + data: { animation: 'UsersPage' }, + }, { path: '**', redirectTo: '', diff --git a/src/Client/src/app/books-page/books-page.component.html b/src/Client/src/app/books-page/books-page.component.html deleted file mode 100644 index 853ce6d..0000000 --- a/src/Client/src/app/books-page/books-page.component.html +++ /dev/null @@ -1,3 +0,0 @@ -
- -
diff --git a/src/Client/src/app/books-page/books-page.component.ts b/src/Client/src/app/books-page/books-page.component.ts deleted file mode 100644 index 1a71748..0000000 --- a/src/Client/src/app/books-page/books-page.component.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { BookCreatorComponent } from '../book-creator/book-creator.component'; - -@Component({ - selector: 'app-books-page', - templateUrl: './books-page.component.html', - imports: [BookCreatorComponent], - styleUrls: ['./books-page.component.css'], - standalone: true, -}) -export class BooksPageComponent implements OnInit { - constructor() {} - - ngOnInit() {} -} diff --git a/src/Client/src/app/books/book-card/book-card.component.css b/src/Client/src/app/books/book-card/book-card.component.css new file mode 100644 index 0000000..0891821 --- /dev/null +++ b/src/Client/src/app/books/book-card/book-card.component.css @@ -0,0 +1,9 @@ +.chevron-down { + transform: rotate(0deg); + transition: transform 0.3s ease-in-out; +} + +.chevron-down.expanded { + transform: rotate(180deg); + transition: transform 0.3s ease-in-out; +} diff --git a/src/Client/src/app/books/book-card/book-card.component.html b/src/Client/src/app/books/book-card/book-card.component.html new file mode 100644 index 0000000..f97ef48 --- /dev/null +++ b/src/Client/src/app/books/book-card/book-card.component.html @@ -0,0 +1,41 @@ +
+
+
+
+
{{ book.title }}
+
+ {{ book.author }} +
+
{{ book.pageCount }} pages
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+
+ {{ book.description }} +
+
+ +
+
+
+
diff --git a/src/Client/src/app/books/book-card/book-card.component.spec.ts b/src/Client/src/app/books/book-card/book-card.component.spec.ts new file mode 100644 index 0000000..559841a --- /dev/null +++ b/src/Client/src/app/books/book-card/book-card.component.spec.ts @@ -0,0 +1,28 @@ +/* tslint:disable:no-unused-variable */ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; +import { DebugElement } from '@angular/core'; + +import { BookCardComponent } from './book-card.component'; + +describe('BookCardComponent', () => { + let component: BookCardComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ BookCardComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(BookCardComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/Client/src/app/books/book-card/book-card.component.ts b/src/Client/src/app/books/book-card/book-card.component.ts new file mode 100644 index 0000000..777cfeb --- /dev/null +++ b/src/Client/src/app/books/book-card/book-card.component.ts @@ -0,0 +1,25 @@ +import { CommonModule } from '@angular/common'; +import { Component, Input, OnInit } from '@angular/core'; +import { NgbCollapseModule } from '@ng-bootstrap/ng-bootstrap'; +import { BookDto } from '../../api/models'; +import { BooksService } from '../../api/services'; + +@Component({ + selector: 'app-book-card', + templateUrl: './book-card.component.html', + styleUrls: ['./book-card.component.css'], + imports: [CommonModule, NgbCollapseModule], + standalone: true, +}) +export class BookCardComponent implements OnInit { + @Input({ required: true }) book!: BookDto; + expanded = false; + + constructor(private booksService: BooksService) {} + + ngOnInit() {} + + deleteBook() { + // TODO + } +} diff --git a/src/Client/src/app/book-creator/book-creator.component.css b/src/Client/src/app/books/book-creator/book-creator.component.css similarity index 100% rename from src/Client/src/app/book-creator/book-creator.component.css rename to src/Client/src/app/books/book-creator/book-creator.component.css diff --git a/src/Client/src/app/book-creator/book-creator.component.html b/src/Client/src/app/books/book-creator/book-creator.component.html similarity index 100% rename from src/Client/src/app/book-creator/book-creator.component.html rename to src/Client/src/app/books/book-creator/book-creator.component.html diff --git a/src/Client/src/app/book-creator/book-creator.component.spec.ts b/src/Client/src/app/books/book-creator/book-creator.component.spec.ts similarity index 100% rename from src/Client/src/app/book-creator/book-creator.component.spec.ts rename to src/Client/src/app/books/book-creator/book-creator.component.spec.ts diff --git a/src/Client/src/app/book-creator/book-creator.component.ts b/src/Client/src/app/books/book-creator/book-creator.component.ts similarity index 84% rename from src/Client/src/app/book-creator/book-creator.component.ts rename to src/Client/src/app/books/book-creator/book-creator.component.ts index 763b0d5..12d42f8 100644 --- a/src/Client/src/app/book-creator/book-creator.component.ts +++ b/src/Client/src/app/books/book-creator/book-creator.component.ts @@ -5,9 +5,10 @@ import { ReactiveFormsModule, Validators, } from '@angular/forms'; -import { CreateBookDto } from '../api/models'; -import { BooksService } from '../api/services'; -import { GoogleBooksService } from '../services/google-books.service'; +import { CreateBookDto } from '../../api/models'; +import { BooksService } from '../../api/services'; +import { GoogleBooksService } from '../../services/google-books.service'; +import { ToastsService } from '../../services/toasts.service'; @Component({ selector: 'app-book-creator', @@ -41,6 +42,7 @@ export class BookCreatorComponent implements OnInit { constructor( private booksService: BooksService, private googleBooksSvc: GoogleBooksService, + private toastsService: ToastsService, private fb: NonNullableFormBuilder ) {} @@ -111,11 +113,17 @@ export class BookCreatorComponent implements OnInit { this.booksService.createBook({ body }).subscribe({ next: () => { this.clear(); - // TODO: show success message + this.toastsService.show({ + classname: 'text-bg-success text-light', + header: 'Book Created!', + }); }, error: err => { console.error(err); - // TODO: show error + this.toastsService.show({ + header: 'Uh oh!', + body: 'There was an error creating the book, please try again.', + }); }, complete: () => { this.createPending = false; diff --git a/src/Client/src/app/books-page/books-page.component.css b/src/Client/src/app/books/books-page/books-page.component.css similarity index 100% rename from src/Client/src/app/books-page/books-page.component.css rename to src/Client/src/app/books/books-page/books-page.component.css diff --git a/src/Client/src/app/books/books-page/books-page.component.html b/src/Client/src/app/books/books-page/books-page.component.html new file mode 100644 index 0000000..4164a0d --- /dev/null +++ b/src/Client/src/app/books/books-page/books-page.component.html @@ -0,0 +1,20 @@ +
+
+ +
+ +
+
+
+

My Bookshelf

+
+ +
diff --git a/src/Client/src/app/books-page/books-page.component.spec.ts b/src/Client/src/app/books/books-page/books-page.component.spec.ts similarity index 100% rename from src/Client/src/app/books-page/books-page.component.spec.ts rename to src/Client/src/app/books/books-page/books-page.component.spec.ts diff --git a/src/Client/src/app/books/books-page/books-page.component.ts b/src/Client/src/app/books/books-page/books-page.component.ts new file mode 100644 index 0000000..9870084 --- /dev/null +++ b/src/Client/src/app/books/books-page/books-page.component.ts @@ -0,0 +1,48 @@ +import { CommonModule } from '@angular/common'; +import { Component, OnInit } from '@angular/core'; +import { NgbCollapseModule } from '@ng-bootstrap/ng-bootstrap'; +import { BookDto } from '../../api/models'; +import { BooksService } from '../../api/services'; +import { ToastsService } from '../../services/toasts.service'; +import { BookCardComponent } from '../book-card/book-card.component'; +import { BookCreatorComponent } from '../book-creator/book-creator.component'; + +@Component({ + selector: 'app-books-page', + templateUrl: './books-page.component.html', + imports: [ + BookCreatorComponent, + BookCardComponent, + CommonModule, + NgbCollapseModule, + ], + styleUrls: ['./books-page.component.css'], + standalone: true, +}) +export class BooksPageComponent implements OnInit { + formCollapsed = true; + books: BookDto[] = []; + + constructor( + private booksService: BooksService, + private toastsService: ToastsService + ) {} + + ngOnInit() { + this.fetchMyBooks(); + } + + fetchMyBooks() { + this.booksService.getUsersBooks().subscribe({ + next: books => { + this.books = books; + }, + error: () => { + this.toastsService.show({ + header: 'Ah, nuts!', + body: 'Failed to fetch your books. Try again in a little bit.', + }); + }, + }); + } +} diff --git a/src/Client/src/app/navbar/navbar.component.html b/src/Client/src/app/navbar/navbar.component.html index 56a6cda..ee96137 100644 --- a/src/Client/src/app/navbar/navbar.component.html +++ b/src/Client/src/app/navbar/navbar.component.html @@ -1,5 +1,6 @@