From efdbe5eb359d6abd1bc2d27bd94d44689e08405a Mon Sep 17 00:00:00 2001 From: Joseph Milazzo Date: Thu, 31 Dec 2020 13:19:18 -0600 Subject: [PATCH 1/3] Added skeleton code for Library -> Series detail pages. Added a card component to keep a lot of the logic and design consistent between screens. Added ability to see your libraries and open them up. --- src/app/_models/library.ts | 1 + src/app/_models/series.ts | 10 +++++ src/app/_models/volume.ts | 5 +++ src/app/_services/library.service.ts | 5 ++- src/app/_services/series.service.ts | 18 +++++++++ .../manage-users/manage-users.component.html | 1 - .../manage-users/manage-users.component.ts | 5 +-- src/app/app-routing.module.ts | 3 +- src/app/app.module.ts | 4 ++ .../library-detail.component.html | 6 +++ .../library-detail.component.scss | 4 ++ .../library-detail.component.ts | 34 +++++++++++++++++ src/app/library/library.component.html | 8 +++- src/app/library/library.component.ts | 15 ++++++-- .../series-detail.component.html | 20 ++++++++++ .../series-detail.component.scss | 0 .../series-detail/series-detail.component.ts | 15 ++++++++ .../shared/card-item/card-item.component.html | 7 ++++ .../shared/card-item/card-item.component.scss | 9 +++++ .../shared/card-item/card-item.component.ts | 36 ++++++++++++++++++ src/app/shared/shared.module.ts | 6 ++- src/assets/images/image-placeholder.jpg | Bin 0 -> 8285 bytes 22 files changed, 197 insertions(+), 15 deletions(-) create mode 100644 src/app/_models/series.ts create mode 100644 src/app/_models/volume.ts create mode 100644 src/app/_services/series.service.ts create mode 100644 src/app/library-detail/library-detail.component.html create mode 100644 src/app/library-detail/library-detail.component.scss create mode 100644 src/app/library-detail/library-detail.component.ts create mode 100644 src/app/series-detail/series-detail.component.html create mode 100644 src/app/series-detail/series-detail.component.scss create mode 100644 src/app/series-detail/series-detail.component.ts create mode 100644 src/app/shared/card-item/card-item.component.html create mode 100644 src/app/shared/card-item/card-item.component.scss create mode 100644 src/app/shared/card-item/card-item.component.ts create mode 100644 src/assets/images/image-placeholder.jpg diff --git a/src/app/_models/library.ts b/src/app/_models/library.ts index 19fa61c19..a56f2c122 100644 --- a/src/app/_models/library.ts +++ b/src/app/_models/library.ts @@ -1,4 +1,5 @@ export interface Library { + id: number; name: string; coverImage: string; type: any; diff --git a/src/app/_models/series.ts b/src/app/_models/series.ts new file mode 100644 index 000000000..b54ebf135 --- /dev/null +++ b/src/app/_models/series.ts @@ -0,0 +1,10 @@ +import { Volume } from './volume'; + +export interface Series { + id: number; + name: string; + originalName: string; + sortName: string; + summary: string; + volumes: Volume[]; +} diff --git a/src/app/_models/volume.ts b/src/app/_models/volume.ts new file mode 100644 index 000000000..35f390d4d --- /dev/null +++ b/src/app/_models/volume.ts @@ -0,0 +1,5 @@ +export interface Volume { + id: number; + number: string; + files: Array; // In future, we can refactor this to be a type with extra metadata around it +} \ No newline at end of file diff --git a/src/app/_services/library.service.ts b/src/app/_services/library.service.ts index 49399a650..5243bf517 100644 --- a/src/app/_services/library.service.ts +++ b/src/app/_services/library.service.ts @@ -26,12 +26,13 @@ export class LibraryService { } getLibrariesForMember(username: string) { - return this.httpClient.get(this.baseUrl + 'library/' + username); + return this.httpClient.get(this.baseUrl + 'library/libraries-for?username=' + username); } updateLibrariesForMember(username: string, selectedLibraries: Library[]) { - return this.httpClient.post(this.baseUrl + '/library/update-for', {username, selectedLibraries}); + return this.httpClient.post(this.baseUrl + 'library/update-for', {username, selectedLibraries}); } + } diff --git a/src/app/_services/series.service.ts b/src/app/_services/series.service.ts new file mode 100644 index 000000000..4ffd75f2a --- /dev/null +++ b/src/app/_services/series.service.ts @@ -0,0 +1,18 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { environment } from 'src/environments/environment'; +import { Series } from '../_models/series'; + +@Injectable({ + providedIn: 'root' +}) +export class SeriesService { + + baseUrl = environment.apiUrl; + + constructor(private httpClient: HttpClient) { } + + getSeries(libraryId: number) { + return this.httpClient.get(this.baseUrl + 'library/series?libraryId=' + libraryId); + } +} diff --git a/src/app/admin/manage-users/manage-users.component.html b/src/app/admin/manage-users/manage-users.component.html index 678bac385..f247ba2a2 100644 --- a/src/app/admin/manage-users/manage-users.component.html +++ b/src/app/admin/manage-users/manage-users.component.html @@ -13,7 +13,6 @@ {{member.username | titlecase}} Admin
-
diff --git a/src/app/admin/manage-users/manage-users.component.ts b/src/app/admin/manage-users/manage-users.component.ts index 07008a465..4cfa0eb19 100644 --- a/src/app/admin/manage-users/manage-users.component.ts +++ b/src/app/admin/manage-users/manage-users.component.ts @@ -61,11 +61,8 @@ export class ManageUsersComponent implements OnInit { }); } - openChangeRole(member: Member) { - - } - deleteUser(member: Member) { + // TODO: Use a modal for this confirm if (confirm('Are you sure you want to delete this user?')) { this.memberService.deleteMember(member.username).subscribe(() => { this.loadMembers(); diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index e23518db7..c0d3127d9 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -1,8 +1,8 @@ import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { HomeComponent } from './home/home.component'; +import { LibraryDetailComponent } from './library-detail/library-detail.component'; import { LibraryComponent } from './library/library.component'; -import { AdminGuard } from './_guards/admin.guard'; const routes: Routes = [ {path: '', component: HomeComponent}, @@ -11,6 +11,7 @@ const routes: Routes = [ loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule) }, {path: 'library', component: LibraryComponent}, + {path: 'library/:id', component: LibraryDetailComponent}, {path: '**', component: HomeComponent, pathMatch: 'full'} ]; diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 14b772fe4..41ce34cad 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -15,6 +15,8 @@ import { ToastrModule } from 'ngx-toastr'; import { ErrorInterceptor } from './_interceptors/error.interceptor'; import { LibraryComponent } from './library/library.component'; import { SharedModule } from './shared/shared.module'; +import { LibraryDetailComponent } from './library-detail/library-detail.component'; +import { SeriesDetailComponent } from './series-detail/series-detail.component'; @@ -25,6 +27,8 @@ import { SharedModule } from './shared/shared.module'; NavHeaderComponent, UserLoginComponent, LibraryComponent, + LibraryDetailComponent, + SeriesDetailComponent, ], imports: [ HttpClientModule, diff --git a/src/app/library-detail/library-detail.component.html b/src/app/library-detail/library-detail.component.html new file mode 100644 index 000000000..dfb0e8e41 --- /dev/null +++ b/src/app/library-detail/library-detail.component.html @@ -0,0 +1,6 @@ +

Title (Manga/Recently Added)

+
+
+ +
+
\ No newline at end of file diff --git a/src/app/library-detail/library-detail.component.scss b/src/app/library-detail/library-detail.component.scss new file mode 100644 index 000000000..7f7daffd5 --- /dev/null +++ b/src/app/library-detail/library-detail.component.scss @@ -0,0 +1,4 @@ +.card { + height: 400; + width: 200; +} \ No newline at end of file diff --git a/src/app/library-detail/library-detail.component.ts b/src/app/library-detail/library-detail.component.ts new file mode 100644 index 000000000..fd4625b9b --- /dev/null +++ b/src/app/library-detail/library-detail.component.ts @@ -0,0 +1,34 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { Library } from '../_models/library'; +import { Series } from '../_models/series'; +import { SeriesService } from '../_services/series.service'; + +@Component({ + selector: 'app-library-detail', + templateUrl: './library-detail.component.html', + styleUrls: ['./library-detail.component.scss'] +}) +export class LibraryDetailComponent implements OnInit { + + libraryId!: number; + series: Series[] = []; + + + constructor(private route: ActivatedRoute, private router: Router, private seriesService: SeriesService) { + const routeId = this.route.snapshot.paramMap.get('id'); + if (routeId === null) { + console.error('No library id was passed. Redirecting to home'); + this.router.navigateByUrl('/home'); + return; + } + this.libraryId = parseInt(routeId, 10); + this.seriesService.getSeries(this.libraryId).subscribe(series => { + this.series = series; + }); + } + + ngOnInit(): void { + } + +} diff --git a/src/app/library/library.component.html b/src/app/library/library.component.html index cedd8c65f..f618c2e64 100644 --- a/src/app/library/library.component.html +++ b/src/app/library/library.component.html @@ -1 +1,7 @@ -

library works!

+ +

Libraries

+
+
+ +
+
\ No newline at end of file diff --git a/src/app/library/library.component.ts b/src/app/library/library.component.ts index b33f10b9c..4b2a454ef 100644 --- a/src/app/library/library.component.ts +++ b/src/app/library/library.component.ts @@ -1,4 +1,5 @@ import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; import { take } from 'rxjs/operators'; import { Library } from '../_models/library'; import { User } from '../_models/user'; @@ -16,15 +17,21 @@ export class LibraryComponent implements OnInit { user: User | undefined; libraries: Library[] = []; - constructor(public accountService: AccountService, private memberService: MemberService, private libraryService: LibraryService) { } + constructor(public accountService: AccountService, private libraryService: LibraryService, private router: Router) { } ngOnInit(): void { this.accountService.currentUser$.pipe(take(1)).subscribe(user => { this.user = user; - // this.libraryService.getLibrariesForUser(this.user.username).subscribe(libraries => { - // this.libraries = libraries; - // }); + console.log('user: ', this.user); + this.libraryService.getLibrariesForMember(this.user.username).subscribe(libraries => { + this.libraries = libraries; + console.log('Libraries: ', this.libraries); + }); }); } + handleNavigation(event: any, library: Library) { + this.router.navigateByUrl('/library/' + library.id); + } + } diff --git a/src/app/series-detail/series-detail.component.html b/src/app/series-detail/series-detail.component.html new file mode 100644 index 000000000..2416b8c0a --- /dev/null +++ b/src/app/series-detail/series-detail.component.html @@ -0,0 +1,20 @@ + \ No newline at end of file diff --git a/src/app/series-detail/series-detail.component.scss b/src/app/series-detail/series-detail.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/series-detail/series-detail.component.ts b/src/app/series-detail/series-detail.component.ts new file mode 100644 index 000000000..54603840e --- /dev/null +++ b/src/app/series-detail/series-detail.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-series-detail', + templateUrl: './series-detail.component.html', + styleUrls: ['./series-detail.component.scss'] +}) +export class SeriesDetailComponent implements OnInit { + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/src/app/shared/card-item/card-item.component.html b/src/app/shared/card-item/card-item.component.html new file mode 100644 index 000000000..bd8c0e237 --- /dev/null +++ b/src/app/shared/card-item/card-item.component.html @@ -0,0 +1,7 @@ +
+ {{title}} + +
+
{{title}}
+
+
\ No newline at end of file diff --git a/src/app/shared/card-item/card-item.component.scss b/src/app/shared/card-item/card-item.component.scss new file mode 100644 index 000000000..a6b9c3588 --- /dev/null +++ b/src/app/shared/card-item/card-item.component.scss @@ -0,0 +1,9 @@ +.card { + margin: 5px; + max-width: 130px; + max-height: 195px; +} + +.card-body { + padding: 5px !important; +} \ No newline at end of file diff --git a/src/app/shared/card-item/card-item.component.ts b/src/app/shared/card-item/card-item.component.ts new file mode 100644 index 000000000..479765d76 --- /dev/null +++ b/src/app/shared/card-item/card-item.component.ts @@ -0,0 +1,36 @@ +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; + +export interface CardItemTitle { + title: string; + linkUrl: string; +} + +@Component({ + selector: 'app-card-item', + templateUrl: './card-item.component.html', + styleUrls: ['./card-item.component.scss'] +}) +export class CardItemComponent implements OnInit { + + @Input() imageUrl = ''; + @Input() title = ''; + @Output() clicked = new EventEmitter(); + + placeholderImage = 'assets/images/image-placeholder.jpg'; //../../.. + + constructor() { } + + ngOnInit(): void { + console.log('card item'); + console.log('imageUrl: ', this.imageUrl); + } + + handleClick() { + this.clicked.emit(this.title); + } + + isNullOrEmpty(val: string) { + return val === null || val === undefined || val === ''; + } + +} diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index d3b94d520..f8cc3fc44 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -2,17 +2,19 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { RegisterMemberComponent } from './register-member/register-member.component'; import { ReactiveFormsModule } from '@angular/forms'; +import { CardItemComponent } from './card-item/card-item.component'; @NgModule({ - declarations: [RegisterMemberComponent], + declarations: [RegisterMemberComponent, CardItemComponent], imports: [ CommonModule, ReactiveFormsModule ], exports: [ - RegisterMemberComponent + RegisterMemberComponent, + CardItemComponent ] }) export class SharedModule { } diff --git a/src/assets/images/image-placeholder.jpg b/src/assets/images/image-placeholder.jpg new file mode 100644 index 0000000000000000000000000000000000000000..872022b56e41bc4b5c0d444ac4b4b130089ec8b6 GIT binary patch literal 8285 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 AQ~&?~ literal 0 HcmV?d00001 From 8865c121d229f80bb092fde989ec7c4f90169634 Mon Sep 17 00:00:00 2001 From: Joseph Milazzo Date: Thu, 31 Dec 2020 15:14:32 -0600 Subject: [PATCH 2/3] Added ability to have actions on cards. --- src/app/_guards/admin.guard.ts | 2 +- src/app/_services/account.service.ts | 4 ++++ src/app/_services/member.service.ts | 2 +- src/app/app-routing.module.ts | 4 +++- .../library-detail.component.html | 4 ++-- .../library-detail/library-detail.component.ts | 4 ++++ src/app/library/library.component.html | 2 +- src/app/library/library.component.ts | 10 +++++++++- .../series-detail/series-detail.component.html | 6 +++--- .../series-detail/series-detail.component.ts | 3 +++ .../shared/card-item/card-item.component.html | 16 +++++++++++++--- .../shared/card-item/card-item.component.scss | 4 ++++ .../shared/card-item/card-item.component.ts | 18 +++++++++++++----- src/app/shared/shared.module.ts | 4 +++- 14 files changed, 64 insertions(+), 19 deletions(-) diff --git a/src/app/_guards/admin.guard.ts b/src/app/_guards/admin.guard.ts index 2297e381c..d59b8643c 100644 --- a/src/app/_guards/admin.guard.ts +++ b/src/app/_guards/admin.guard.ts @@ -16,7 +16,7 @@ export class AdminGuard implements CanActivate { // this automaticallys subs due to being router guard return this.accountService.currentUser$.pipe( map((user: User) => { - if (user && user.roles.includes('Admin')) { + if (this.accountService.hasAdminRole(user)) { return true; } diff --git a/src/app/_services/account.service.ts b/src/app/_services/account.service.ts index dc6449c6e..9da467d08 100644 --- a/src/app/_services/account.service.ts +++ b/src/app/_services/account.service.ts @@ -21,6 +21,10 @@ export class AccountService { constructor(private httpClient: HttpClient) { } + hasAdminRole(user: User) { + return user && user.roles.includes('Admin'); + } + login(model: any): Observable { return this.httpClient.post(this.baseUrl + 'account/login', model).pipe( map((response: User) => { diff --git a/src/app/_services/member.service.ts b/src/app/_services/member.service.ts index 08eee8bfe..32d219920 100644 --- a/src/app/_services/member.service.ts +++ b/src/app/_services/member.service.ts @@ -21,7 +21,7 @@ export class MemberService { } updatePassword(newPassword: string) { - // TODO: Implement update password (use JWT to assume role) + // TODO: Implement update password } deleteMember(username: string) { diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index c0d3127d9..9b1705767 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -3,6 +3,7 @@ import { Routes, RouterModule } from '@angular/router'; import { HomeComponent } from './home/home.component'; import { LibraryDetailComponent } from './library-detail/library-detail.component'; import { LibraryComponent } from './library/library.component'; +import { SeriesDetailComponent } from './series-detail/series-detail.component'; const routes: Routes = [ {path: '', component: HomeComponent}, @@ -11,7 +12,8 @@ const routes: Routes = [ loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule) }, {path: 'library', component: LibraryComponent}, - {path: 'library/:id', component: LibraryDetailComponent}, + {path: 'library/:id', component: LibraryDetailComponent}, // NOTE: Should I put a guard up to prevent unauthorized access to libraries and series? + {path: 'series/:id', component: SeriesDetailComponent}, {path: '**', component: HomeComponent, pathMatch: 'full'} ]; diff --git a/src/app/library-detail/library-detail.component.html b/src/app/library-detail/library-detail.component.html index dfb0e8e41..237312e99 100644 --- a/src/app/library-detail/library-detail.component.html +++ b/src/app/library-detail/library-detail.component.html @@ -1,6 +1,6 @@

Title (Manga/Recently Added)

-
- +
+
\ No newline at end of file diff --git a/src/app/library-detail/library-detail.component.ts b/src/app/library-detail/library-detail.component.ts index fd4625b9b..43c3d5460 100644 --- a/src/app/library-detail/library-detail.component.ts +++ b/src/app/library-detail/library-detail.component.ts @@ -31,4 +31,8 @@ export class LibraryDetailComponent implements OnInit { ngOnInit(): void { } + seriesClicked(series: Series) { + this.router.navigateByUrl('/series/' + series.id); + } + } diff --git a/src/app/library/library.component.html b/src/app/library/library.component.html index f618c2e64..666f33e34 100644 --- a/src/app/library/library.component.html +++ b/src/app/library/library.component.html @@ -2,6 +2,6 @@

Libraries

- +
\ No newline at end of file diff --git a/src/app/library/library.component.ts b/src/app/library/library.component.ts index 4b2a454ef..c4f027c73 100644 --- a/src/app/library/library.component.ts +++ b/src/app/library/library.component.ts @@ -1,6 +1,7 @@ import { Component, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import { take } from 'rxjs/operators'; +import { CardItemAction } from '../shared/card-item/card-item.component'; import { Library } from '../_models/library'; import { User } from '../_models/user'; import { AccountService } from '../_services/account.service'; @@ -16,13 +17,20 @@ export class LibraryComponent implements OnInit { user: User | undefined; libraries: Library[] = []; + actions: CardItemAction[] = []; constructor(public accountService: AccountService, private libraryService: LibraryService, private router: Router) { } ngOnInit(): void { this.accountService.currentUser$.pipe(take(1)).subscribe(user => { this.user = user; - console.log('user: ', this.user); + if (this.accountService.hasAdminRole(user)) { + this.actions = [ + {title: 'Scan Library', callback: (data: Library) => { + console.log('You tried to scan library: ' + data.name); + }} + ]; + } this.libraryService.getLibrariesForMember(this.user.username).subscribe(libraries => { this.libraries = libraries; console.log('Libraries: ', this.libraries); diff --git a/src/app/series-detail/series-detail.component.html b/src/app/series-detail/series-detail.component.html index 2416b8c0a..4ad9e0f2b 100644 --- a/src/app/series-detail/series-detail.component.html +++ b/src/app/series-detail/series-detail.component.html @@ -1,8 +1,8 @@ -

title

@@ -17,4 +17,4 @@ Volumes here - --> \ No newline at end of file + \ No newline at end of file diff --git a/src/app/series-detail/series-detail.component.ts b/src/app/series-detail/series-detail.component.ts index 54603840e..93df5173a 100644 --- a/src/app/series-detail/series-detail.component.ts +++ b/src/app/series-detail/series-detail.component.ts @@ -1,4 +1,5 @@ import { Component, OnInit } from '@angular/core'; +import { Series } from '../_models/series'; @Component({ selector: 'app-series-detail', @@ -7,6 +8,8 @@ import { Component, OnInit } from '@angular/core'; }) export class SeriesDetailComponent implements OnInit { + series: Series | undefined; + constructor() { } ngOnInit(): void { diff --git a/src/app/shared/card-item/card-item.component.html b/src/app/shared/card-item/card-item.component.html index bd8c0e237..41523ea92 100644 --- a/src/app/shared/card-item/card-item.component.html +++ b/src/app/shared/card-item/card-item.component.html @@ -1,7 +1,17 @@ -
- {{title}} +
+ {{title}}
-
{{title}}
+
{{title}}
+ +
+
+ +
+ +
+
+
+
\ No newline at end of file diff --git a/src/app/shared/card-item/card-item.component.scss b/src/app/shared/card-item/card-item.component.scss index a6b9c3588..9987ed9cc 100644 --- a/src/app/shared/card-item/card-item.component.scss +++ b/src/app/shared/card-item/card-item.component.scss @@ -6,4 +6,8 @@ .card-body { padding: 5px !important; +} + +.dropdown-toggle:after { + content: none !important; } \ No newline at end of file diff --git a/src/app/shared/card-item/card-item.component.ts b/src/app/shared/card-item/card-item.component.ts index 479765d76..ec10d3f28 100644 --- a/src/app/shared/card-item/card-item.component.ts +++ b/src/app/shared/card-item/card-item.component.ts @@ -1,8 +1,9 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -export interface CardItemTitle { + +export interface CardItemAction { title: string; - linkUrl: string; + callback: (data: any) => void; } @Component({ @@ -14,15 +15,15 @@ export class CardItemComponent implements OnInit { @Input() imageUrl = ''; @Input() title = ''; + @Input() actions: CardItemAction[] = []; + @Input() entity: any; // This is the entity we are representing. It will be returned if an action is executed. @Output() clicked = new EventEmitter(); - placeholderImage = 'assets/images/image-placeholder.jpg'; //../../.. + placeholderImage = 'assets/images/image-placeholder.jpg'; constructor() { } ngOnInit(): void { - console.log('card item'); - console.log('imageUrl: ', this.imageUrl); } handleClick() { @@ -33,4 +34,11 @@ export class CardItemComponent implements OnInit { return val === null || val === undefined || val === ''; } + performAction(event: any, action: CardItemAction) { + event.stopPropagation(); + if (typeof action.callback === 'function') { + action.callback(this.entity); + } + } + } diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index f8cc3fc44..01a2b702f 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -3,6 +3,7 @@ import { CommonModule } from '@angular/common'; import { RegisterMemberComponent } from './register-member/register-member.component'; import { ReactiveFormsModule } from '@angular/forms'; import { CardItemComponent } from './card-item/card-item.component'; +import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'; @@ -10,7 +11,8 @@ import { CardItemComponent } from './card-item/card-item.component'; declarations: [RegisterMemberComponent, CardItemComponent], imports: [ CommonModule, - ReactiveFormsModule + ReactiveFormsModule, + NgbDropdownModule ], exports: [ RegisterMemberComponent, From ca46d137c40b60f28ad27b3621cfd512c489f49a Mon Sep 17 00:00:00 2001 From: Joseph Milazzo Date: Fri, 1 Jan 2021 11:58:18 -0600 Subject: [PATCH 3/3] Finished the skeleton code for navigating Kavita. All UIs need to be reworked and major code cleanup. --- src/app/_models/series.ts | 1 + src/app/_models/volume.ts | 5 +-- src/app/_services/series.service.ts | 11 ++++++- .../library-detail.component.html | 14 ++++++--- .../library-detail.component.ts | 2 +- .../series-detail.component.html | 31 ++++++++++++------- .../series-detail/series-detail.component.ts | 24 +++++++++++++- .../shared/card-item/card-item.component.html | 2 +- .../shared/card-item/card-item.component.ts | 2 +- 9 files changed, 70 insertions(+), 22 deletions(-) diff --git a/src/app/_models/series.ts b/src/app/_models/series.ts index b54ebf135..78085a077 100644 --- a/src/app/_models/series.ts +++ b/src/app/_models/series.ts @@ -6,5 +6,6 @@ export interface Series { originalName: string; sortName: string; summary: string; + coverImage: string; volumes: Volume[]; } diff --git a/src/app/_models/volume.ts b/src/app/_models/volume.ts index 35f390d4d..77a7629fa 100644 --- a/src/app/_models/volume.ts +++ b/src/app/_models/volume.ts @@ -1,5 +1,6 @@ export interface Volume { id: number; number: string; - files: Array; // In future, we can refactor this to be a type with extra metadata around it -} \ No newline at end of file + files: Array; + coverImage: string; +} diff --git a/src/app/_services/series.service.ts b/src/app/_services/series.service.ts index 4ffd75f2a..c1fc3f188 100644 --- a/src/app/_services/series.service.ts +++ b/src/app/_services/series.service.ts @@ -2,6 +2,7 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { environment } from 'src/environments/environment'; import { Series } from '../_models/series'; +import { Volume } from '../_models/volume'; @Injectable({ providedIn: 'root' @@ -12,7 +13,15 @@ export class SeriesService { constructor(private httpClient: HttpClient) { } - getSeries(libraryId: number) { + getSeriesForLibrary(libraryId: number) { return this.httpClient.get(this.baseUrl + 'library/series?libraryId=' + libraryId); } + + getSeries(seriesId: number) { + return this.httpClient.get(this.baseUrl + 'series/' + seriesId); + } + + getVolumes(seriesId: number) { + return this.httpClient.get(this.baseUrl + 'series/volumes?seriesId=' + seriesId); + } } diff --git a/src/app/library-detail/library-detail.component.html b/src/app/library-detail/library-detail.component.html index 237312e99..15bd6e363 100644 --- a/src/app/library-detail/library-detail.component.html +++ b/src/app/library-detail/library-detail.component.html @@ -1,6 +1,12 @@ -

Title (Manga/Recently Added)

-
-
- +
+

Title (Manga/Recently Added)

+
+
+ +
+ + + Nothing here.... +
\ No newline at end of file diff --git a/src/app/library-detail/library-detail.component.ts b/src/app/library-detail/library-detail.component.ts index 43c3d5460..686726624 100644 --- a/src/app/library-detail/library-detail.component.ts +++ b/src/app/library-detail/library-detail.component.ts @@ -23,7 +23,7 @@ export class LibraryDetailComponent implements OnInit { return; } this.libraryId = parseInt(routeId, 10); - this.seriesService.getSeries(this.libraryId).subscribe(series => { + this.seriesService.getSeriesForLibrary(this.libraryId).subscribe(series => { this.series = series; }); } diff --git a/src/app/series-detail/series-detail.component.html b/src/app/series-detail/series-detail.component.html index 4ad9e0f2b..7154f9845 100644 --- a/src/app/series-detail/series-detail.component.html +++ b/src/app/series-detail/series-detail.component.html @@ -1,20 +1,29 @@ -
+
-
-
- -

title

-
+
+ +
-
-

Title

- - (metadata here) +
+

{{series.name | titlecase}}

+
+ +
+
+ +
+
+

{{series?.summary}}

+
+
+

Volumes

- Volumes here +
+ +
\ No newline at end of file diff --git a/src/app/series-detail/series-detail.component.ts b/src/app/series-detail/series-detail.component.ts index 93df5173a..f0e8a0455 100644 --- a/src/app/series-detail/series-detail.component.ts +++ b/src/app/series-detail/series-detail.component.ts @@ -1,5 +1,8 @@ import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; import { Series } from '../_models/series'; +import { Volume } from '../_models/volume'; +import { SeriesService } from '../_services/series.service'; @Component({ selector: 'app-series-detail', @@ -9,10 +12,29 @@ import { Series } from '../_models/series'; export class SeriesDetailComponent implements OnInit { series: Series | undefined; + volumes: Volume[] = []; - constructor() { } + constructor(private route: ActivatedRoute, private seriesService: SeriesService) { } ngOnInit(): void { + + const routeId = this.route.snapshot.paramMap.get('id'); + if (routeId === null) { + console.error('No library id was passed. Redirecting to home'); + //this.router.navigateByUrl('/home'); + return; + } + const seriesId = parseInt(routeId, 10); + this.seriesService.getSeries(seriesId).subscribe(series => { + this.series = series; + this.seriesService.getVolumes(this.series.id).subscribe(volumes => { + this.volumes = volumes; + }); + }); + } + + openVolume(volume: Volume) { + alert('TODO: Let user read Manga'); } } diff --git a/src/app/shared/card-item/card-item.component.html b/src/app/shared/card-item/card-item.component.html index 41523ea92..976a075f8 100644 --- a/src/app/shared/card-item/card-item.component.html +++ b/src/app/shared/card-item/card-item.component.html @@ -1,7 +1,7 @@
{{title}} -
+
{{title}}
diff --git a/src/app/shared/card-item/card-item.component.ts b/src/app/shared/card-item/card-item.component.ts index ec10d3f28..ff41443ee 100644 --- a/src/app/shared/card-item/card-item.component.ts +++ b/src/app/shared/card-item/card-item.component.ts @@ -15,7 +15,7 @@ export class CardItemComponent implements OnInit { @Input() imageUrl = ''; @Input() title = ''; - @Input() actions: CardItemAction[] = []; + @Input() actions: CardItemAction[] = []; // TODO: Create a factory that generates actions based on if admin, etc. for each card type. @Input() entity: any; // This is the entity we are representing. It will be returned if an action is executed. @Output() clicked = new EventEmitter();