Merge pull request #16 from Kareadita/feature/scan-library
Library, Series, Volumes, oh my!
This commit is contained in:
commit
7642ffa912
25 changed files with 293 additions and 19 deletions
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
export interface Library {
|
export interface Library {
|
||||||
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
coverImage: string;
|
coverImage: string;
|
||||||
type: any;
|
type: any;
|
||||||
|
|
|
||||||
11
src/app/_models/series.ts
Normal file
11
src/app/_models/series.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { Volume } from './volume';
|
||||||
|
|
||||||
|
export interface Series {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
originalName: string;
|
||||||
|
sortName: string;
|
||||||
|
summary: string;
|
||||||
|
coverImage: string;
|
||||||
|
volumes: Volume[];
|
||||||
|
}
|
||||||
6
src/app/_models/volume.ts
Normal file
6
src/app/_models/volume.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
export interface Volume {
|
||||||
|
id: number;
|
||||||
|
number: string;
|
||||||
|
files: Array<string>;
|
||||||
|
coverImage: string;
|
||||||
|
}
|
||||||
|
|
@ -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) => {
|
||||||
|
|
|
||||||
|
|
@ -26,12 +26,13 @@ export class LibraryService {
|
||||||
}
|
}
|
||||||
|
|
||||||
getLibrariesForMember(username: string) {
|
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[]) {
|
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});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ export class MemberService {
|
||||||
adminExists() {
|
adminExists() {
|
||||||
return this.httpClient.get<boolean>(this.baseUrl + 'admin/exists');
|
return this.httpClient.get<boolean>(this.baseUrl + 'admin/exists');
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteMember(username: string) {
|
deleteMember(username: string) {
|
||||||
return this.httpClient.delete(this.baseUrl + 'users/delete-user?username=' + username);
|
return this.httpClient.delete(this.baseUrl + 'users/delete-user?username=' + username);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
27
src/app/_services/series.service.ts
Normal file
27
src/app/_services/series.service.ts
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
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'
|
||||||
|
})
|
||||||
|
export class SeriesService {
|
||||||
|
|
||||||
|
baseUrl = environment.apiUrl;
|
||||||
|
|
||||||
|
constructor(private httpClient: HttpClient) { }
|
||||||
|
|
||||||
|
getSeriesForLibrary(libraryId: number) {
|
||||||
|
return this.httpClient.get<Series[]>(this.baseUrl + 'library/series?libraryId=' + libraryId);
|
||||||
|
}
|
||||||
|
|
||||||
|
getSeries(seriesId: number) {
|
||||||
|
return this.httpClient.get<Series>(this.baseUrl + 'series/' + seriesId);
|
||||||
|
}
|
||||||
|
|
||||||
|
getVolumes(seriesId: number) {
|
||||||
|
return this.httpClient.get<Volume[]>(this.baseUrl + 'series/volumes?seriesId=' + seriesId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -13,7 +13,6 @@
|
||||||
{{member.username | titlecase}} <span *ngIf="member.isAdmin" class="badge badge-pill badge-secondary">Admin</span>
|
{{member.username | titlecase}} <span *ngIf="member.isAdmin" class="badge badge-pill badge-secondary">Admin</span>
|
||||||
<div class="pull-right" *ngIf="canEditMember(member)">
|
<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-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>
|
<button class="btn btn-primary" (click)="openEditLibraryAccess(member)"><i class="fa fa-pencil" title="Edit {{member.username | titlecase}}"></i></button>
|
||||||
</div>
|
</div>
|
||||||
</h4>
|
</h4>
|
||||||
|
|
|
||||||
|
|
@ -61,11 +61,8 @@ export class ManageUsersComponent implements OnInit {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
openChangeRole(member: Member) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteUser(member: Member) {
|
deleteUser(member: Member) {
|
||||||
|
// TODO: Use a modal for this confirm
|
||||||
if (confirm('Are you sure you want to delete this user?')) {
|
if (confirm('Are you sure you want to delete this user?')) {
|
||||||
this.memberService.deleteMember(member.username).subscribe(() => {
|
this.memberService.deleteMember(member.username).subscribe(() => {
|
||||||
this.loadMembers();
|
this.loadMembers();
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { Routes, RouterModule } from '@angular/router';
|
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 { LibraryComponent } from './library/library.component';
|
import { LibraryComponent } from './library/library.component';
|
||||||
import { AdminGuard } from './_guards/admin.guard';
|
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: 'home', component: HomeComponent},
|
{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'}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@ import { ToastrModule } from 'ngx-toastr';
|
||||||
import { ErrorInterceptor } from './_interceptors/error.interceptor';
|
import { ErrorInterceptor } from './_interceptors/error.interceptor';
|
||||||
import { LibraryComponent } from './library/library.component';
|
import { LibraryComponent } from './library/library.component';
|
||||||
import { SharedModule } from './shared/shared.module';
|
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,
|
NavHeaderComponent,
|
||||||
UserLoginComponent,
|
UserLoginComponent,
|
||||||
LibraryComponent,
|
LibraryComponent,
|
||||||
|
LibraryDetailComponent,
|
||||||
|
SeriesDetailComponent,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
HttpClientModule,
|
HttpClientModule,
|
||||||
|
|
|
||||||
12
src/app/library-detail/library-detail.component.html
Normal file
12
src/app/library-detail/library-detail.component.html
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
<div class="container">
|
||||||
|
<h2>Title (Manga/Recently Added)</h2>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-2" *ngFor="let manga of series">
|
||||||
|
<app-card-item [title]="manga.name" (clicked)="seriesClicked(manga)"></app-card-item>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ng-container *ngIf="series.length === 0">
|
||||||
|
<!-- Put a cricket here -->
|
||||||
|
Nothing here....
|
||||||
|
</ng-container>
|
||||||
|
</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;
|
||||||
|
}
|
||||||
38
src/app/library-detail/library-detail.component.ts
Normal file
38
src/app/library-detail/library-detail.component.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
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.getSeriesForLibrary(this.libraryId).subscribe(series => {
|
||||||
|
this.series = series;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
seriesClicked(series: Series) {
|
||||||
|
this.router.navigateByUrl('/series/' + series.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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)" [actions]="actions" [entity]="library"></app-card-item>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
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';
|
||||||
|
|
@ -15,16 +17,29 @@ export class LibraryComponent implements OnInit {
|
||||||
|
|
||||||
user: User | undefined;
|
user: User | undefined;
|
||||||
libraries: Library[] = [];
|
libraries: Library[] = [];
|
||||||
|
actions: CardItemAction[] = [];
|
||||||
|
|
||||||
constructor(public accountService: AccountService, private memberService: MemberService, private libraryService: LibraryService) { }
|
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;
|
||||||
// this.libraryService.getLibrariesForUser(this.user.username).subscribe(libraries => {
|
if (this.accountService.hasAdminRole(user)) {
|
||||||
// this.libraries = libraries;
|
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);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleNavigation(event: any, library: Library) {
|
||||||
|
this.router.navigateByUrl('/library/' + library.id);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
29
src/app/series-detail/series-detail.component.html
Normal file
29
src/app/series-detail/series-detail.component.html
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
<div class="container" *ngIf="series !== undefined">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-2">
|
||||||
|
<app-card-item [imageUrl]="series.coverImage === null ? 'assets/images/image-placeholder.jpg' : series.coverImage"></app-card-item>
|
||||||
|
<button class="btn btn-primary">Read</button>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-10">
|
||||||
|
<h2>{{series.name | titlecase}}</h2>
|
||||||
|
<div class="row">
|
||||||
|
<ngb-rating></ngb-rating>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<p>{{series?.summary}}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h4 class="mt-3">Volumes</h4>
|
||||||
|
<div class="row mt-3">
|
||||||
|
<div class="col-md-2" *ngFor="let volume of volumes">
|
||||||
|
<app-card-item [entity]="volume" [title]="'Volume ' + volume.number" (click)="openVolume(volume)"></app-card-item>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
0
src/app/series-detail/series-detail.component.scss
Normal file
0
src/app/series-detail/series-detail.component.scss
Normal file
40
src/app/series-detail/series-detail.component.ts
Normal file
40
src/app/series-detail/series-detail.component.ts
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
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',
|
||||||
|
templateUrl: './series-detail.component.html',
|
||||||
|
styleUrls: ['./series-detail.component.scss']
|
||||||
|
})
|
||||||
|
export class SeriesDetailComponent implements OnInit {
|
||||||
|
|
||||||
|
series: Series | undefined;
|
||||||
|
volumes: Volume[] = [];
|
||||||
|
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
17
src/app/shared/card-item/card-item.component.html
Normal file
17
src/app/shared/card-item/card-item.component.html
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
<div class="card" style="width: 18rem;">
|
||||||
|
<img (click)="handleClick()" class="card-img-top" src="{{isNullOrEmpty(imageUrl) ? placeholderImage : imageUrl}}" alt="{{title}}">
|
||||||
|
|
||||||
|
<div class="card-body text-center" *ngIf="title.length > 0 || actions.length > 0">
|
||||||
|
<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>
|
||||||
13
src/app/shared/card-item/card-item.component.scss
Normal file
13
src/app/shared/card-item/card-item.component.scss
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
.card {
|
||||||
|
margin: 5px;
|
||||||
|
max-width: 130px;
|
||||||
|
max-height: 195px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-body {
|
||||||
|
padding: 5px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-toggle:after {
|
||||||
|
content: none !important;
|
||||||
|
}
|
||||||
44
src/app/shared/card-item/card-item.component.ts
Normal file
44
src/app/shared/card-item/card-item.component.ts
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||||
|
|
||||||
|
|
||||||
|
export interface CardItemAction {
|
||||||
|
title: string;
|
||||||
|
callback: (data: any) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-card-item',
|
||||||
|
templateUrl: './card-item.component.html',
|
||||||
|
styleUrls: ['./card-item.component.scss']
|
||||||
|
})
|
||||||
|
export class CardItemComponent implements OnInit {
|
||||||
|
|
||||||
|
@Input() imageUrl = '';
|
||||||
|
@Input() title = '';
|
||||||
|
@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<string>();
|
||||||
|
|
||||||
|
placeholderImage = 'assets/images/image-placeholder.jpg';
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
handleClick() {
|
||||||
|
this.clicked.emit(this.title);
|
||||||
|
}
|
||||||
|
|
||||||
|
isNullOrEmpty(val: string) {
|
||||||
|
return val === null || val === undefined || val === '';
|
||||||
|
}
|
||||||
|
|
||||||
|
performAction(event: any, action: CardItemAction) {
|
||||||
|
event.stopPropagation();
|
||||||
|
if (typeof action.callback === 'function') {
|
||||||
|
action.callback(this.entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -2,17 +2,21 @@ import { NgModule } from '@angular/core';
|
||||||
import { CommonModule } from '@angular/common';
|
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 { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [RegisterMemberComponent],
|
declarations: [RegisterMemberComponent, CardItemComponent],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
ReactiveFormsModule
|
ReactiveFormsModule,
|
||||||
|
NgbDropdownModule
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
RegisterMemberComponent
|
RegisterMemberComponent,
|
||||||
|
CardItemComponent
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class SharedModule { }
|
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