Added ability to have actions on cards.

This commit is contained in:
Joseph Milazzo 2020-12-31 15:14:32 -06:00
parent efdbe5eb35
commit 8865c121d2
14 changed files with 64 additions and 19 deletions

View file

@ -16,7 +16,7 @@ export class AdminGuard implements CanActivate {
// this automaticallys subs due to being router guard // this automaticallys subs due to being router guard
return this.accountService.currentUser$.pipe( return this.accountService.currentUser$.pipe(
map((user: User) => { map((user: User) => {
if (user && user.roles.includes('Admin')) { if (this.accountService.hasAdminRole(user)) {
return true; return true;
} }

View file

@ -21,6 +21,10 @@ export class AccountService {
constructor(private httpClient: HttpClient) { constructor(private httpClient: HttpClient) {
} }
hasAdminRole(user: User) {
return user && user.roles.includes('Admin');
}
login(model: any): Observable<any> { login(model: any): Observable<any> {
return this.httpClient.post<User>(this.baseUrl + 'account/login', model).pipe( return this.httpClient.post<User>(this.baseUrl + 'account/login', model).pipe(
map((response: User) => { map((response: User) => {

View file

@ -21,7 +21,7 @@ export class MemberService {
} }
updatePassword(newPassword: string) { updatePassword(newPassword: string) {
// TODO: Implement update password (use JWT to assume role) // TODO: Implement update password
} }
deleteMember(username: string) { deleteMember(username: string) {

View file

@ -3,6 +3,7 @@ import { Routes, RouterModule } from '@angular/router';
import { HomeComponent } from './home/home.component'; import { HomeComponent } from './home/home.component';
import { LibraryDetailComponent } from './library-detail/library-detail.component'; import { LibraryDetailComponent } from './library-detail/library-detail.component';
import { LibraryComponent } from './library/library.component'; import { LibraryComponent } from './library/library.component';
import { SeriesDetailComponent } from './series-detail/series-detail.component';
const routes: Routes = [ const routes: Routes = [
{path: '', component: HomeComponent}, {path: '', component: HomeComponent},
@ -11,7 +12,8 @@ const routes: Routes = [
loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule) loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule)
}, },
{path: 'library', component: LibraryComponent}, {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'} {path: '**', component: HomeComponent, pathMatch: 'full'}
]; ];

View file

@ -1,6 +1,6 @@
<h2>Title (Manga/Recently Added)</h2> <h2>Title (Manga/Recently Added)</h2>
<div class="row"> <div class="row">
<div class="col-md-2"> <div class="col-md-2" *ngFor="let series of series">
<app-card-item title="Series 1"></app-card-item> <app-card-item [title]="series.name" (clicked)="seriesClicked(series)"></app-card-item>
</div> </div>
</div> </div>

View file

@ -31,4 +31,8 @@ export class LibraryDetailComponent implements OnInit {
ngOnInit(): void { ngOnInit(): void {
} }
seriesClicked(series: Series) {
this.router.navigateByUrl('/series/' + series.id);
}
} }

View file

@ -2,6 +2,6 @@
<h2>Libraries</h2> <h2>Libraries</h2>
<div class="row"> <div class="row">
<div class="col-sm-6 col-md-4 col-lg-2" *ngFor="let library of libraries"> <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> <app-card-item [imageUrl]="library.coverImage" [title]="library.name" (clicked)="handleNavigation($event, library)" [actions]="actions" [entity]="library"></app-card-item>
</div> </div>
</div> </div>

View file

@ -1,6 +1,7 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { take } from 'rxjs/operators'; import { take } from 'rxjs/operators';
import { CardItemAction } from '../shared/card-item/card-item.component';
import { Library } from '../_models/library'; import { Library } from '../_models/library';
import { User } from '../_models/user'; import { User } from '../_models/user';
import { AccountService } from '../_services/account.service'; import { AccountService } from '../_services/account.service';
@ -16,13 +17,20 @@ export class LibraryComponent implements OnInit {
user: User | undefined; user: User | undefined;
libraries: Library[] = []; libraries: Library[] = [];
actions: CardItemAction[] = [];
constructor(public accountService: AccountService, private libraryService: LibraryService, private router: Router) { } constructor(public accountService: AccountService, private libraryService: LibraryService, private router: Router) { }
ngOnInit(): void { ngOnInit(): void {
this.accountService.currentUser$.pipe(take(1)).subscribe(user => { this.accountService.currentUser$.pipe(take(1)).subscribe(user => {
this.user = 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.libraryService.getLibrariesForMember(this.user.username).subscribe(libraries => {
this.libraries = libraries; this.libraries = libraries;
console.log('Libraries: ', this.libraries); console.log('Libraries: ', this.libraries);

View file

@ -1,8 +1,8 @@
<!-- <div class="container"> <div class="container">
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<div class="card"> <div class="card">
<img src="{{manga.coverImage === null ? 'image-placeholder.jpg' : manga.coverImage}}"> <!-- <img src="{{series.coverImage === null ? 'image-placeholder.jpg' : series.coverImage}}"> -->
<p>title</p> <p>title</p>
</div> </div>
</div> </div>
@ -17,4 +17,4 @@
Volumes here Volumes here
</div> </div>
</div> --> </div>

View file

@ -1,4 +1,5 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { Series } from '../_models/series';
@Component({ @Component({
selector: 'app-series-detail', selector: 'app-series-detail',
@ -7,6 +8,8 @@ import { Component, OnInit } from '@angular/core';
}) })
export class SeriesDetailComponent implements OnInit { export class SeriesDetailComponent implements OnInit {
series: Series | undefined;
constructor() { } constructor() { }
ngOnInit(): void { ngOnInit(): void {

View file

@ -1,7 +1,17 @@
<div class="card" style="width: 18rem;" (click)="handleClick()"> <div class="card" style="width: 18rem;">
<img class="card-img-top" src="{{isNullOrEmpty(imageUrl) ? placeholderImage : imageUrl}}" alt="{{title}}"> <img (click)="handleClick()" class="card-img-top" src="{{isNullOrEmpty(imageUrl) ? placeholderImage : imageUrl}}" alt="{{title}}">
<div class="card-body text-center"> <div class="card-body text-center">
<h5 class="card-title">{{title}}</h5> <h5 class="card-title" (click)="handleClick()">{{title}}</h5>
<ng-container *ngIf="actions.length > 0">
<div class="col">
<div ngbDropdown class="d-inline-block">
<button class="btn" id="actions-{{title}}" ngbDropdownToggle><i class="fa fa-ellipsis-v" aria-hidden="true"></i></button>
<div ngbDropdownMenu attr.aria-labelledby="actions-{{title}}">
<button ngbDropdownItem *ngFor="let action of actions" (click)="performAction($event, action)">{{action.title}}</button>
</div>
</div>
</div>
</ng-container>
</div> </div>
</div> </div>

View file

@ -6,4 +6,8 @@
.card-body { .card-body {
padding: 5px !important; padding: 5px !important;
}
.dropdown-toggle:after {
content: none !important;
} }

View file

@ -1,8 +1,9 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
export interface CardItemTitle {
export interface CardItemAction {
title: string; title: string;
linkUrl: string; callback: (data: any) => void;
} }
@Component({ @Component({
@ -14,15 +15,15 @@ export class CardItemComponent implements OnInit {
@Input() imageUrl = ''; @Input() imageUrl = '';
@Input() title = ''; @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<string>(); @Output() clicked = new EventEmitter<string>();
placeholderImage = 'assets/images/image-placeholder.jpg'; //../../.. placeholderImage = 'assets/images/image-placeholder.jpg';
constructor() { } constructor() { }
ngOnInit(): void { ngOnInit(): void {
console.log('card item');
console.log('imageUrl: ', this.imageUrl);
} }
handleClick() { handleClick() {
@ -33,4 +34,11 @@ export class CardItemComponent implements OnInit {
return val === null || val === undefined || val === ''; return val === null || val === undefined || val === '';
} }
performAction(event: any, action: CardItemAction) {
event.stopPropagation();
if (typeof action.callback === 'function') {
action.callback(this.entity);
}
}
} }

View file

@ -3,6 +3,7 @@ import { CommonModule } from '@angular/common';
import { RegisterMemberComponent } from './register-member/register-member.component'; import { RegisterMemberComponent } from './register-member/register-member.component';
import { ReactiveFormsModule } from '@angular/forms'; import { ReactiveFormsModule } from '@angular/forms';
import { CardItemComponent } from './card-item/card-item.component'; 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], declarations: [RegisterMemberComponent, CardItemComponent],
imports: [ imports: [
CommonModule, CommonModule,
ReactiveFormsModule ReactiveFormsModule,
NgbDropdownModule
], ],
exports: [ exports: [
RegisterMemberComponent, RegisterMemberComponent,