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.
This commit is contained in:
parent
fdc0c5753d
commit
efdbe5eb35
22 changed files with 197 additions and 15 deletions
|
|
@ -1,4 +1,5 @@
|
|||
export interface Library {
|
||||
id: number;
|
||||
name: string;
|
||||
coverImage: string;
|
||||
type: any;
|
||||
|
|
|
|||
10
src/app/_models/series.ts
Normal file
10
src/app/_models/series.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import { Volume } from './volume';
|
||||
|
||||
export interface Series {
|
||||
id: number;
|
||||
name: string;
|
||||
originalName: string;
|
||||
sortName: string;
|
||||
summary: string;
|
||||
volumes: Volume[];
|
||||
}
|
||||
5
src/app/_models/volume.ts
Normal file
5
src/app/_models/volume.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
export interface Volume {
|
||||
id: number;
|
||||
number: string;
|
||||
files: Array<string>; // In future, we can refactor this to be a type with extra metadata around it
|
||||
}
|
||||
|
|
@ -26,12 +26,13 @@ export class LibraryService {
|
|||
}
|
||||
|
||||
getLibrariesForMember(username: string) {
|
||||
return this.httpClient.get<Library[]>(this.baseUrl + 'library/' + username);
|
||||
return this.httpClient.get<Library[]>(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});
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
18
src/app/_services/series.service.ts
Normal file
18
src/app/_services/series.service.ts
Normal file
|
|
@ -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<Series[]>(this.baseUrl + 'library/series?libraryId=' + libraryId);
|
||||
}
|
||||
}
|
||||
|
|
@ -13,7 +13,6 @@
|
|||
{{member.username | titlecase}} <span *ngIf="member.isAdmin" class="badge badge-pill badge-secondary">Admin</span>
|
||||
<div class="pull-right" *ngIf="canEditMember(member)">
|
||||
<button class="btn btn-danger mr-2" (click)="deleteUser(member)"><i class="fa fa-trash" title="Delete {{member.username | titlecase}}"></i></button>
|
||||
<!-- <button class="btn btn-primary" (click)="openChangeRole(member)"><i class="fa fa-pencil"></i>{{member.roles.includes('Admin') ? 'Demote' : 'Promote'}}</button> -->
|
||||
<button class="btn btn-primary" (click)="openEditLibraryAccess(member)"><i class="fa fa-pencil" title="Edit {{member.username | titlecase}}"></i></button>
|
||||
</div>
|
||||
</h4>
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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'}
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
6
src/app/library-detail/library-detail.component.html
Normal file
6
src/app/library-detail/library-detail.component.html
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<h2>Title (Manga/Recently Added)</h2>
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<app-card-item title="Series 1"></app-card-item>
|
||||
</div>
|
||||
</div>
|
||||
4
src/app/library-detail/library-detail.component.scss
Normal file
4
src/app/library-detail/library-detail.component.scss
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
.card {
|
||||
height: 400;
|
||||
width: 200;
|
||||
}
|
||||
34
src/app/library-detail/library-detail.component.ts
Normal file
34
src/app/library-detail/library-detail.component.ts
Normal file
|
|
@ -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 {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1 +1,7 @@
|
|||
<p>library works!</p>
|
||||
|
||||
<h2>Libraries</h2>
|
||||
<div class="row">
|
||||
<div class="col-sm-6 col-md-4 col-lg-2" *ngFor="let library of libraries">
|
||||
<app-card-item [imageUrl]="library.coverImage" [title]="library.name" (clicked)="handleNavigation($event, library)"></app-card-item>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
20
src/app/series-detail/series-detail.component.html
Normal file
20
src/app/series-detail/series-detail.component.html
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<!-- <div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<img src="{{manga.coverImage === null ? 'image-placeholder.jpg' : manga.coverImage}}">
|
||||
<p>title</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h2>Title</h2>
|
||||
<button class="btn btn-primary">Read/Continue Reading</button>
|
||||
(metadata here)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-3">
|
||||
Volumes here
|
||||
</div>
|
||||
|
||||
</div> -->
|
||||
0
src/app/series-detail/series-detail.component.scss
Normal file
0
src/app/series-detail/series-detail.component.scss
Normal file
15
src/app/series-detail/series-detail.component.ts
Normal file
15
src/app/series-detail/series-detail.component.ts
Normal file
|
|
@ -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 {
|
||||
}
|
||||
|
||||
}
|
||||
7
src/app/shared/card-item/card-item.component.html
Normal file
7
src/app/shared/card-item/card-item.component.html
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<div class="card" style="width: 18rem;" (click)="handleClick()">
|
||||
<img class="card-img-top" src="{{isNullOrEmpty(imageUrl) ? placeholderImage : imageUrl}}" alt="{{title}}">
|
||||
|
||||
<div class="card-body text-center">
|
||||
<h5 class="card-title">{{title}}</h5>
|
||||
</div>
|
||||
</div>
|
||||
9
src/app/shared/card-item/card-item.component.scss
Normal file
9
src/app/shared/card-item/card-item.component.scss
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
.card {
|
||||
margin: 5px;
|
||||
max-width: 130px;
|
||||
max-height: 195px;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: 5px !important;
|
||||
}
|
||||
36
src/app/shared/card-item/card-item.component.ts
Normal file
36
src/app/shared/card-item/card-item.component.ts
Normal file
|
|
@ -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<string>();
|
||||
|
||||
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 === '';
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 { }
|
||||
|
|
|
|||
BIN
src/assets/images/image-placeholder.jpg
Normal file
BIN
src/assets/images/image-placeholder.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.1 KiB |
Loading…
Add table
Add a link
Reference in a new issue