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:
Joseph Milazzo 2020-12-31 13:19:18 -06:00
parent fdc0c5753d
commit efdbe5eb35
22 changed files with 197 additions and 15 deletions

View file

@ -1,4 +1,5 @@
export interface Library {
id: number;
name: string;
coverImage: string;
type: any;

10
src/app/_models/series.ts Normal file
View file

@ -0,0 +1,10 @@
import { Volume } from './volume';
export interface Series {
id: number;
name: string;
originalName: string;
sortName: string;
summary: string;
volumes: Volume[];
}

View 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
}

View file

@ -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});
}
}

View 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);
}
}

View file

@ -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>

View file

@ -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();

View file

@ -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'}
];

View file

@ -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,

View 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>

View file

@ -0,0 +1,4 @@
.card {
height: 400;
width: 200;
}

View 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 {
}
}

View file

@ -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>

View file

@ -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);
}
}

View 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> -->

View 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 {
}
}

View 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>

View file

@ -0,0 +1,9 @@
.card {
margin: 5px;
max-width: 130px;
max-height: 195px;
}
.card-body {
padding: 5px !important;
}

View 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 === '';
}
}

View file

@ -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 { }

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB