Merge branch 'feature/34-refactor-kontor-angular' into 'develop/0.2.0'
Resolve "refactor kontor-angular" Closes #34 See merge request tpeetz/kontor!36
This commit was merged in pull request #80.
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 544 B |
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 634 B |
@@ -1,5 +1,6 @@
|
|||||||
<div class="app">
|
<div class="app">
|
||||||
<kontor-header />
|
<kontor-header />
|
||||||
|
<app-navigation/>
|
||||||
<main>
|
<main>
|
||||||
<router-outlet />
|
<router-outlet />
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -2,10 +2,11 @@ import { Component } from '@angular/core';
|
|||||||
import { RouterOutlet } from '@angular/router';
|
import { RouterOutlet } from '@angular/router';
|
||||||
import { KontorHeaderComponent } from "./kontor/header/header.component";
|
import { KontorHeaderComponent } from "./kontor/header/header.component";
|
||||||
import { KontorFooterComponent } from './kontor/footer/footer.component';
|
import { KontorFooterComponent } from './kontor/footer/footer.component';
|
||||||
|
import { NavigationComponent } from "./kontor/navigation/navigation.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
imports: [RouterOutlet, KontorHeaderComponent, KontorFooterComponent],
|
imports: [RouterOutlet, KontorHeaderComponent, KontorFooterComponent, NavigationComponent],
|
||||||
templateUrl: './app.component.html',
|
templateUrl: './app.component.html',
|
||||||
styleUrl: './app.component.css'
|
styleUrl: './app.component.css'
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,52 +1,26 @@
|
|||||||
import { Routes } from '@angular/router';
|
import { Routes } from '@angular/router';
|
||||||
import { KontorComponent } from './kontor/kontor.component';
|
import { KontorComponent } from './kontor/kontor.component';
|
||||||
import { Login } from './common/login/login';
|
import { Login } from './common/login/login';
|
||||||
import { ComicOverviewComponent } from './comic/comic-overview/comic-overview.component';
|
import { ComicSectionComponent } from './kontor/comic/comic-section/comic-section.component';
|
||||||
import { MediaOverviewComponent } from './media/media-overview/media-overview.component';
|
import { comicRoutes } from './kontor/comic/comic-section/comic-section.routes';
|
||||||
import { ArtistListComponent } from './comic/artist/artist-list/artist-list.component';
|
import { TyscSectionComponent } from './kontor/tysc/tysc-section/tysc-section.component';
|
||||||
import { ComicListComponent } from './comic/comic/comic-list/comic-list.component';
|
import { tyscRoutes } from './kontor/tysc/tysc-section/tysc-section.routes';
|
||||||
import { MediaFileListComponent } from './media/media-file/media-file-list/media-file-list.component';
|
import { MediaSectionComponent } from './kontor/media/media-section/media-section.component';
|
||||||
import { MediaActorListComponent } from './media/media-actor/media-actor-list/media-actor-list.component';
|
import { mediaRoutes } from './kontor/media/media-section/media-section.routes';
|
||||||
import { MediaVideoListComponent } from './media/media-video/media-video-list/media-video-list.component';
|
|
||||||
import { PublisherListComponent } from './comic/publisher/publisher-list/publisher-list.component';
|
|
||||||
import { TyscMenuComponent } from './tysc/tysc-menu/tysc-menu.component';
|
|
||||||
import { TeamListComponent } from './tysc/team/team-list/team-list.component';
|
|
||||||
import { VendorListComponent } from './tysc/vendor/vendor-list/vendor-list.component';
|
|
||||||
import { CardsetListComponent } from './tysc/cardset/cardset-list/cardset-list.component';
|
|
||||||
import { PositionListComponent } from './tysc/position/position-list/position-list.component';
|
|
||||||
import { PlayerListComponent } from './tysc/player/player-list/player-list.component';
|
|
||||||
import { SportListComponent } from './tysc/sport/sport-list/sport-list.component';
|
|
||||||
|
|
||||||
export const routes: Routes = [
|
export const routes: Routes = [
|
||||||
{ path: '', component: KontorComponent, },
|
{ path: '', component: KontorComponent, },
|
||||||
{ path: 'login', component: Login, },
|
{ path: 'login', component: Login, },
|
||||||
{
|
{
|
||||||
path: 'comic', component: ComicOverviewComponent,
|
path: 'comic', component: ComicSectionComponent,
|
||||||
children: [
|
children: comicRoutes,
|
||||||
{ path: '', component: ComicListComponent},
|
|
||||||
{ path: 'comics', component: ComicListComponent},
|
|
||||||
{ path: 'publisher', component: PublisherListComponent},
|
|
||||||
{ path: 'artist', component: ArtistListComponent},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'tysc', component: TyscMenuComponent,
|
path: 'tysc', component: TyscSectionComponent,
|
||||||
children: [
|
children: tyscRoutes,
|
||||||
{ path: '', component: SportListComponent},
|
|
||||||
{ path: 'team', component: TeamListComponent},
|
|
||||||
{ path: 'player', component: PlayerListComponent},
|
|
||||||
{ path: 'position', component: PositionListComponent},
|
|
||||||
{ path: 'cardset', component: CardsetListComponent},
|
|
||||||
{ path: 'vendor', component: VendorListComponent},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'media', component: MediaOverviewComponent,
|
path: 'media', component: MediaSectionComponent,
|
||||||
children: [
|
children: mediaRoutes,
|
||||||
{ path: '', component: MediaFileListComponent},
|
|
||||||
{ path: 'mediafiles', component: MediaFileListComponent},
|
|
||||||
{ path: 'mediaactors', component: MediaActorListComponent},
|
|
||||||
{ path: 'mediavideos', component: MediaVideoListComponent},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
<div>
|
|
||||||
<a [routerLink]="[artist().id]" routerLinkActive="active">
|
|
||||||
<span>{{ artist().name }}</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import { Component, input, signal } from '@angular/core';
|
|
||||||
import { Artist } from '../artist.model';
|
|
||||||
import { RouterLink, RouterLinkActive } from '@angular/router';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-artist-detail',
|
|
||||||
imports: [RouterLink, RouterLinkActive],
|
|
||||||
templateUrl: './artist-detail.component.html',
|
|
||||||
styleUrl: './artist-detail.component.css'
|
|
||||||
})
|
|
||||||
export class ArtistDetailComponent {
|
|
||||||
artist = input.required<Artist>();
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
export interface Artist {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
<!-- <p>comic-overview works!</p>
|
|
||||||
<div class="subnav">
|
|
||||||
<ul>
|
|
||||||
<li><a routerLink="/comic/comics" routerLinkActive>Comics</a></li>
|
|
||||||
<li><a routerLink="/comic/publisher">Publisher</a></li>
|
|
||||||
<li><a routerLink="/comic/artist">Artists</a></li>
|
|
||||||
</ul>
|
|
||||||
<h4>Child Routes Result</h4>
|
|
||||||
<router-outlet></router-outlet>
|
|
||||||
</div> -->
|
|
||||||
<div class="subnav">
|
|
||||||
<a routerLink="/comic/comics" routerLinkActive="active">Comics</a>
|
|
||||||
<a routerLink="/comic/publisher" routerLinkActive="active">Publisher</a>
|
|
||||||
<a routerLink="/comic/artist" routerLinkActive="active">Artists</a>
|
|
||||||
</div>
|
|
||||||
<router-outlet></router-outlet>
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
import { Component } from '@angular/core';
|
|
||||||
import { RouterLink, RouterOutlet, RouterLinkActive } from '@angular/router';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-comic-overview',
|
|
||||||
imports: [RouterLink, RouterOutlet, RouterLinkActive],
|
|
||||||
templateUrl: './comic-overview.component.html',
|
|
||||||
styleUrl: './comic-overview.component.css'
|
|
||||||
})
|
|
||||||
export class ComicOverviewComponent {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
<p>comic-list works!</p>
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import { Component } from '@angular/core';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-comic-list',
|
|
||||||
imports: [],
|
|
||||||
templateUrl: './comic-list.component.html',
|
|
||||||
styleUrl: './comic-list.component.css'
|
|
||||||
})
|
|
||||||
export class ComicListComponent {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
<p>publisher-list works!</p>
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import { Component } from '@angular/core';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-publisher-list',
|
|
||||||
imports: [],
|
|
||||||
templateUrl: './publisher-list.component.html',
|
|
||||||
styleUrl: './publisher-list.component.css'
|
|
||||||
})
|
|
||||||
export class PublisherListComponent {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<div>
|
||||||
|
<a [routerLink]="['/', 'comic', 'artist', artist().id]" routerLinkActive="active">
|
||||||
|
<span>{{ artist().name }}</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
+6
-6
@@ -1,18 +1,18 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { CardsetListComponent } from './cardset-list.component';
|
import { ComicArtistComponent } from './comic-artist.component';
|
||||||
|
|
||||||
describe('CardsetListComponent', () => {
|
describe('ComicArtistComponent', () => {
|
||||||
let component: CardsetListComponent;
|
let component: ComicArtistComponent;
|
||||||
let fixture: ComponentFixture<CardsetListComponent>;
|
let fixture: ComponentFixture<ComicArtistComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [CardsetListComponent]
|
imports: [ComicArtistComponent]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
|
|
||||||
fixture = TestBed.createComponent(CardsetListComponent);
|
fixture = TestBed.createComponent(ComicArtistComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { Component, input } from '@angular/core';
|
||||||
|
import { Artist } from '../comic.model';
|
||||||
|
import { RouterLink, RouterLinkActive } from '@angular/router';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-comic-artist',
|
||||||
|
imports: [RouterLink, RouterLinkActive],
|
||||||
|
templateUrl: './comic-artist.component.html',
|
||||||
|
styleUrl: './comic-artist.component.css'
|
||||||
|
})
|
||||||
|
export class ComicArtistComponent {
|
||||||
|
artist = input.required<Artist>();
|
||||||
|
}
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
<ul>
|
<ul>
|
||||||
@for (artist of artists(); track artist.id) {
|
@for (artist of artists(); track artist.id) {
|
||||||
<li>
|
<li>
|
||||||
<app-artist-detail [artist]="artist"/>
|
<app-comic-artist [artist]="artist"/>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ComicArtistsListComponent } from './comic-artists-list.component';
|
||||||
|
|
||||||
|
describe('ComicArtistsListComponent', () => {
|
||||||
|
let component: ComicArtistsListComponent;
|
||||||
|
let fixture: ComponentFixture<ComicArtistsListComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [ComicArtistsListComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ComicArtistsListComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
+10
-10
@@ -1,16 +1,17 @@
|
|||||||
import { Component, DestroyRef, inject, OnInit, signal } from '@angular/core';
|
import { Component, DestroyRef, inject, OnInit, signal } from '@angular/core';
|
||||||
import { Artist } from '../artist.model';
|
import { Artist } from '../comic.model';
|
||||||
import { ArtistService } from '../artist.service';
|
import { ComicArtistComponent } from '../comic-artist/comic-artist.component';
|
||||||
import { ArtistDetailComponent } from "../artist-detail/artist-detail.component";
|
import { ArtistService } from '../comic-artists/artist.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-artist-list',
|
selector: 'app-comic-artists-list',
|
||||||
imports: [ArtistDetailComponent],
|
imports: [ComicArtistComponent],
|
||||||
templateUrl: './artist-list.component.html',
|
templateUrl: './comic-artists-list.component.html',
|
||||||
styleUrl: './artist-list.component.css'
|
styleUrl: './comic-artists-list.component.css'
|
||||||
})
|
})
|
||||||
export class ArtistListComponent implements OnInit {
|
export class ComicArtistsListComponent implements OnInit {
|
||||||
artists = signal<Artist[] | undefined>(undefined);
|
|
||||||
|
artists = signal<Artist[]>([]);
|
||||||
isFetching = signal(false);
|
isFetching = signal(false);
|
||||||
error = signal('');
|
error = signal('');
|
||||||
private artistService = inject(ArtistService);
|
private artistService = inject(ArtistService);
|
||||||
@@ -34,5 +35,4 @@ export class ArtistListComponent implements OnInit {
|
|||||||
subscription.unsubscribe();
|
subscription.unsubscribe();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+19
-2
@@ -1,8 +1,8 @@
|
|||||||
import { inject, Injectable, signal } from "@angular/core";
|
import { inject, Injectable, signal } from "@angular/core";
|
||||||
import { ErrorService } from "../../shared/error.service";
|
|
||||||
import { HttpClient } from "@angular/common/http";
|
import { HttpClient } from "@angular/common/http";
|
||||||
import { Artist } from "./artist.model";
|
|
||||||
import { catchError, map, throwError } from "rxjs";
|
import { catchError, map, throwError } from "rxjs";
|
||||||
|
import { ErrorService } from "../../../shared/error.service";
|
||||||
|
import { Artist, ArtistDetails } from "../comic.model";
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
@@ -18,6 +18,10 @@ export class ArtistService {
|
|||||||
return this.fetchArtists('http://127.0.0.1:8800/api/comics/artists', 'Someting went wrong fetching artists. Please try again later-');
|
return this.fetchArtists('http://127.0.0.1:8800/api/comics/artists', 'Someting went wrong fetching artists. Please try again later-');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loadArtistDetails(artistId: string | null) {
|
||||||
|
return this.fetchArtistDetails('http://127.0.0.1:8800/api/comics/artists/' + artistId, 'Someting went wrong fetching comic artists. Please try again later.');
|
||||||
|
}
|
||||||
|
|
||||||
private fetchArtists(url: string, errorMessage: string) {
|
private fetchArtists(url: string, errorMessage: string) {
|
||||||
return this.httpClient.get<Artist[]>(url).pipe(
|
return this.httpClient.get<Artist[]>(url).pipe(
|
||||||
map((resData) => resData),
|
map((resData) => resData),
|
||||||
@@ -27,4 +31,17 @@ export class ArtistService {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fetchArtistDetails(url: string, errorMessage: string) {
|
||||||
|
return this.httpClient.get<ArtistDetails>(url).pipe(
|
||||||
|
map((resData) => {
|
||||||
|
console.log(resData);
|
||||||
|
return resData;
|
||||||
|
}),
|
||||||
|
catchError((error) => {
|
||||||
|
console.log(error);
|
||||||
|
return throwError(() => new Error(errorMessage));
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
.grid-container {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
grid-gap: 20px;
|
||||||
|
}
|
||||||
|
section {
|
||||||
|
margin-left: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
background-color: darkgrey;
|
||||||
|
border-radius: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
article {
|
||||||
|
margin-left: 10px;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
<div class="grid-container">
|
||||||
|
<div>
|
||||||
|
<app-comic-artists-list />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
@if (artist()) {
|
||||||
|
<section>
|
||||||
|
<h2>{{ artist().name }}</h2>
|
||||||
|
<a href="{{ artist().weblink }}" style="background-color: green;">{{ artist().name }}</a>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
@for (work of artist().comic_works; track work.worktype.id) {
|
||||||
|
<app-comic-worktype [worktype]="work.worktype"/>
|
||||||
|
@for (comic of work.comics; track comic.id) {
|
||||||
|
<article>
|
||||||
|
<app-comic-comic [comic]="comic"/>
|
||||||
|
</article>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
@for (work of artist().issue_works; track work.worktype.id) {
|
||||||
|
<app-comic-worktype [worktype]="work.worktype"/>
|
||||||
|
@for (issue of work.issues; track issue.id) {
|
||||||
|
<article>
|
||||||
|
<app-comic-comic [comic]="issue.comic"/>
|
||||||
|
</article>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</section>
|
||||||
|
} @else {
|
||||||
|
<h2>Artist Details</h2>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
+6
-6
@@ -1,18 +1,18 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { PositionListComponent } from './position-list.component';
|
import { ComicArtistsComponent } from './comic-artists.component';
|
||||||
|
|
||||||
describe('PositionListComponent', () => {
|
describe('ComicArtistsComponent', () => {
|
||||||
let component: PositionListComponent;
|
let component: ComicArtistsComponent;
|
||||||
let fixture: ComponentFixture<PositionListComponent>;
|
let fixture: ComponentFixture<ComicArtistsComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [PositionListComponent]
|
imports: [ComicArtistsComponent]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
|
|
||||||
fixture = TestBed.createComponent(PositionListComponent);
|
fixture = TestBed.createComponent(ComicArtistsComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { Component, inject, input } from '@angular/core';
|
||||||
|
import { ComicArtistsListComponent } from '../comic-artists-list/comic-artists-list.component';
|
||||||
|
import { ActivatedRouteSnapshot, ResolveFn, RouterStateSnapshot } from '@angular/router';
|
||||||
|
import { ArtistService } from './artist.service';
|
||||||
|
import { ComicComicComponent } from "../comic-comic/comic-comic.component";
|
||||||
|
import { ComicWorktypeComponent } from "../comic-worktype/comic-worktype.component";
|
||||||
|
import { ArtistDetails } from '../comic.model';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-comic-artists',
|
||||||
|
imports: [ComicArtistsListComponent, ComicComicComponent, ComicWorktypeComponent],
|
||||||
|
templateUrl: './comic-artists.component.html',
|
||||||
|
styleUrl: './comic-artists.component.css'
|
||||||
|
})
|
||||||
|
export class ComicArtistsComponent {
|
||||||
|
artist = input.required<ArtistDetails>();
|
||||||
|
}
|
||||||
|
|
||||||
|
export const artistResolver: ResolveFn<ArtistDetails> = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => {
|
||||||
|
const artistService = inject(ArtistService);
|
||||||
|
const artistId = route.paramMap.get('artistId');
|
||||||
|
const artistDetails = artistService.loadArtistDetails(artistId);
|
||||||
|
console.log(artistDetails);
|
||||||
|
return artistDetails;
|
||||||
|
};
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<div>
|
||||||
|
<a [routerLink]="['/', 'comic', 'comics', comic().id]" routerLinkActive="active">
|
||||||
|
<span>{{ comic().title }}</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
+6
-6
@@ -1,18 +1,18 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { ArtistListComponent } from './artist-list.component';
|
import { ComicComicComponent } from './comic-comic.component';
|
||||||
|
|
||||||
describe('ArtistListComponent', () => {
|
describe('ComicComicComponent', () => {
|
||||||
let component: ArtistListComponent;
|
let component: ComicComicComponent;
|
||||||
let fixture: ComponentFixture<ArtistListComponent>;
|
let fixture: ComponentFixture<ComicComicComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [ArtistListComponent]
|
imports: [ComicComicComponent]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
|
|
||||||
fixture = TestBed.createComponent(ArtistListComponent);
|
fixture = TestBed.createComponent(ComicComicComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { Component, input } from '@angular/core';
|
||||||
|
import { Comic } from '../comic.model';
|
||||||
|
import { RouterLink, RouterLinkActive } from '@angular/router';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-comic-comic',
|
||||||
|
imports: [RouterLink, RouterLinkActive],
|
||||||
|
templateUrl: './comic-comic.component.html',
|
||||||
|
styleUrl: './comic-comic.component.css'
|
||||||
|
})
|
||||||
|
export class ComicComicComponent {
|
||||||
|
comic = input.required<Comic>();
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
ul {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<ul>
|
||||||
|
@for (comic of comics(); track comic.id) {
|
||||||
|
<li>
|
||||||
|
<app-comic-comic [comic]="comic"/>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ComicComicsListComponent } from './comic-comics-list.component';
|
||||||
|
|
||||||
|
describe('ComicComicsListComponent', () => {
|
||||||
|
let component: ComicComicsListComponent;
|
||||||
|
let fixture: ComponentFixture<ComicComicsListComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [ComicComicsListComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ComicComicsListComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { Component, DestroyRef, inject, OnInit, signal } from '@angular/core';
|
||||||
|
import { Comic } from '../comic.model';
|
||||||
|
import { ComicService } from '../comic-comics/comic.service';
|
||||||
|
import { ComicComicComponent } from "../comic-comic/comic-comic.component";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-comic-comics-list',
|
||||||
|
imports: [ComicComicComponent],
|
||||||
|
templateUrl: './comic-comics-list.component.html',
|
||||||
|
styleUrl: './comic-comics-list.component.css'
|
||||||
|
})
|
||||||
|
export class ComicComicsListComponent implements OnInit {
|
||||||
|
comics = signal<Comic[] | undefined>(undefined);
|
||||||
|
isFetching = signal(false);
|
||||||
|
error = signal('');
|
||||||
|
private comicsService = inject(ComicService);
|
||||||
|
private destroyRef = inject(DestroyRef);
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.isFetching.set(true);
|
||||||
|
const subscription = this.comicsService.loadComics().subscribe({
|
||||||
|
next: (comics) => {
|
||||||
|
this.comics.set(comics);
|
||||||
|
},
|
||||||
|
error: (error: Error) => {
|
||||||
|
this.error.set(error.message);
|
||||||
|
},
|
||||||
|
complete: () => {
|
||||||
|
this.isFetching.set(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.destroyRef.onDestroy(() => {
|
||||||
|
subscription.unsubscribe();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
.float-parent-element {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
.float-child-element {
|
||||||
|
float: left;
|
||||||
|
width: 50%;
|
||||||
|
border-width: 10px;
|
||||||
|
border-color: black;
|
||||||
|
}
|
||||||
|
.float-details-element {
|
||||||
|
border: 1px solid darkgreen;
|
||||||
|
background-color: lightgreen;
|
||||||
|
margin: 5px;
|
||||||
|
padding: 1rem;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.parent {
|
||||||
|
border: 1px solid black;
|
||||||
|
margin: 1rem;
|
||||||
|
padding: 2rem 2rem;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.child {
|
||||||
|
display: inline-block;
|
||||||
|
border: 1px solid red;
|
||||||
|
padding: 1rem 1rem;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<div class="grid-container">
|
||||||
|
<div>
|
||||||
|
<app-comic-comics-list/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
@if (comic()) {
|
||||||
|
<section>
|
||||||
|
<h2>{{ comic().title }}</h2>
|
||||||
|
<a href="{{ comic().weblink }}" style="background-color: green;">{{ comic().title }}</a>
|
||||||
|
<app-comic-publisher [publisher]="comic().publisher" />
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
@for (issue of comic().issues; track issue.id) {
|
||||||
|
<app-comic-issue [issue]="issue"/>
|
||||||
|
}
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
@for (work of comic().works; track work.worktype.id) {
|
||||||
|
<app-comic-worktype [worktype]="work.worktype"/>
|
||||||
|
@for (artist of work.artists; track artist.id) {
|
||||||
|
<article>
|
||||||
|
<app-comic-artist [artist]="artist"/>
|
||||||
|
</article>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</section>
|
||||||
|
} @else {
|
||||||
|
<h2>Comic Details</h2>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ComicComicsComponent } from './comic-comics.component';
|
||||||
|
|
||||||
|
describe('ComicComicsComponent', () => {
|
||||||
|
let component: ComicComicsComponent;
|
||||||
|
let fixture: ComponentFixture<ComicComicsComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [ComicComicsComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ComicComicsComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { Component, inject, input } from '@angular/core';
|
||||||
|
import { ComicComicsListComponent } from "../comic-comics-list/comic-comics-list.component";
|
||||||
|
import { ComicDetails } from '../comic.model';
|
||||||
|
import { ActivatedRouteSnapshot, ResolveFn, RouterStateSnapshot } from '@angular/router';
|
||||||
|
import { ComicService } from './comic.service';
|
||||||
|
import { ComicWorktypeComponent } from '../comic-worktype/comic-worktype.component';
|
||||||
|
import { ComicArtistComponent } from '../comic-artist/comic-artist.component';
|
||||||
|
import { ComicIssueComponent } from '../comic-issue/comic-issue.component';
|
||||||
|
import { ComicPublisherComponent } from "../comic-publisher/comic-publisher.component";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-comic-comics',
|
||||||
|
imports: [ComicComicsListComponent, ComicWorktypeComponent, ComicArtistComponent, ComicIssueComponent, ComicPublisherComponent],
|
||||||
|
templateUrl: './comic-comics.component.html',
|
||||||
|
styleUrl: './comic-comics.component.css'
|
||||||
|
})
|
||||||
|
export class ComicComicsComponent {
|
||||||
|
comic = input.required<ComicDetails>();
|
||||||
|
}
|
||||||
|
|
||||||
|
export const comicResolver: ResolveFn<ComicDetails> = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => {
|
||||||
|
const comicService = inject(ComicService);
|
||||||
|
const comicId = route.paramMap.get('comicId');
|
||||||
|
const comicDetails = comicService.loadComicDetails(comicId);
|
||||||
|
console.log(comicDetails);
|
||||||
|
return comicDetails;
|
||||||
|
};
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import { HttpClient } from "@angular/common/http";
|
||||||
|
import { inject, Injectable, signal } from "@angular/core";
|
||||||
|
import { Comic, ComicDetails } from "../comic.model";
|
||||||
|
import { catchError, map, throwError } from "rxjs";
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class ComicService {
|
||||||
|
private httpClient = inject(HttpClient);
|
||||||
|
private comics = signal<Comic[]>([]);
|
||||||
|
|
||||||
|
loadedComics = this.comics.asReadonly();
|
||||||
|
|
||||||
|
loadComics() {
|
||||||
|
return this.fetchComics('http://127.0.0.1:8800/api/comics/comics', 'Someting went wrong fetching comics. Please try again later.');
|
||||||
|
}
|
||||||
|
|
||||||
|
loadComicDetails(comicId: string | null) {
|
||||||
|
return this.fetchComicDetails('http://127.0.0.1:8800/api/comics/comics/' + comicId, 'Someting went wrong fetching comics. Please try again later.');
|
||||||
|
}
|
||||||
|
|
||||||
|
private fetchComicDetails(url: string, errorMessage: string) {
|
||||||
|
return this.httpClient.get<ComicDetails>(url).pipe(
|
||||||
|
map((resData) => {
|
||||||
|
console.log(resData);
|
||||||
|
return resData;
|
||||||
|
}),
|
||||||
|
catchError((error) => {
|
||||||
|
console.log(error);
|
||||||
|
return throwError(() => new Error(errorMessage));
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private fetchComics(url: string, errorMessage: string) {
|
||||||
|
return this.httpClient.get<Comic[]>(url).pipe(
|
||||||
|
map((resData) => resData),
|
||||||
|
catchError((error) => {
|
||||||
|
console.log(error);
|
||||||
|
return throwError(() => new Error(errorMessage));
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<div>
|
||||||
|
<a [routerLink]="['/', 'comic', 'issue', issue().id]" routerLinkActive="active">
|
||||||
|
<span>{{ issue().issue_number }}</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
+6
-6
@@ -1,18 +1,18 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { PlayerListComponent } from './player-list.component';
|
import { ComicIssueComponent } from './comic-issue.component';
|
||||||
|
|
||||||
describe('PlayerListComponent', () => {
|
describe('ComicIssueComponent', () => {
|
||||||
let component: PlayerListComponent;
|
let component: ComicIssueComponent;
|
||||||
let fixture: ComponentFixture<PlayerListComponent>;
|
let fixture: ComponentFixture<ComicIssueComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [PlayerListComponent]
|
imports: [ComicIssueComponent]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
|
|
||||||
fixture = TestBed.createComponent(PlayerListComponent);
|
fixture = TestBed.createComponent(ComicIssueComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { Component, input } from '@angular/core';
|
||||||
|
import { Issue } from '../comic.model';
|
||||||
|
import { RouterLink, RouterLinkActive } from '@angular/router';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-comic-issue',
|
||||||
|
imports: [RouterLink, RouterLinkActive],
|
||||||
|
templateUrl: './comic-issue.component.html',
|
||||||
|
styleUrl: './comic-issue.component.css'
|
||||||
|
})
|
||||||
|
export class ComicIssueComponent {
|
||||||
|
issue = input.required<Issue>();
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<div class="subnav">
|
||||||
|
<a routerLink="/comic/comics" routerLinkActive="active">Comics</a>
|
||||||
|
<a routerLink="/comic/publisher" routerLinkActive="active">Publisher</a>
|
||||||
|
<a routerLink="/comic/artist" routerLinkActive="active">Artists</a>
|
||||||
|
</div>
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ComicNavigationComponent } from './comic-navigation.component';
|
||||||
|
|
||||||
|
describe('ComicNavigationComponent', () => {
|
||||||
|
let component: ComicNavigationComponent;
|
||||||
|
let fixture: ComponentFixture<ComicNavigationComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [ComicNavigationComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ComicNavigationComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { RouterLink, RouterLinkActive } from '@angular/router';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-comic-navigation',
|
||||||
|
imports: [RouterLink, RouterLinkActive],
|
||||||
|
templateUrl: './comic-navigation.component.html',
|
||||||
|
styleUrl: './comic-navigation.component.css'
|
||||||
|
})
|
||||||
|
export class ComicNavigationComponent {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<div>
|
||||||
|
<a [routerLink]="['/', 'comic', 'publisher', publisher().id]" routerLinkActive="active">
|
||||||
|
<span>{{ publisher().name }}</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ComicPublisherComponent } from './comic-publisher.component';
|
||||||
|
|
||||||
|
describe('ComicPublisherComponent', () => {
|
||||||
|
let component: ComicPublisherComponent;
|
||||||
|
let fixture: ComponentFixture<ComicPublisherComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [ComicPublisherComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ComicPublisherComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { Component, input } from '@angular/core';
|
||||||
|
import { Publisher } from '../comic.model';
|
||||||
|
import { RouterLink, RouterLinkActive } from '@angular/router';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-comic-publisher',
|
||||||
|
imports: [RouterLink, RouterLinkActive],
|
||||||
|
templateUrl: './comic-publisher.component.html',
|
||||||
|
styleUrl: './comic-publisher.component.css'
|
||||||
|
})
|
||||||
|
export class ComicPublisherComponent {
|
||||||
|
publisher = input.required<Publisher>();
|
||||||
|
}
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
ul {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
<ul>
|
||||||
|
@for (publisher of publishers(); track publisher.id) {
|
||||||
|
<li>
|
||||||
|
<app-comic-publisher [publisher]="publisher" />
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ComicPublishersListComponent } from './comic-publishers-list.component';
|
||||||
|
|
||||||
|
describe('ComicPublishersListComponent', () => {
|
||||||
|
let component: ComicPublishersListComponent;
|
||||||
|
let fixture: ComponentFixture<ComicPublishersListComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [ComicPublishersListComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ComicPublishersListComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
import { Component, DestroyRef, inject, OnInit, signal } from '@angular/core';
|
||||||
|
import { Publisher } from '../comic.model';
|
||||||
|
import { PublisherService } from '../comic-publishers/publisher.service';
|
||||||
|
import { ComicPublisherComponent } from "../comic-publisher/comic-publisher.component";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-comic-publishers-list',
|
||||||
|
imports: [ComicPublisherComponent],
|
||||||
|
templateUrl: './comic-publishers-list.component.html',
|
||||||
|
styleUrl: './comic-publishers-list.component.css'
|
||||||
|
})
|
||||||
|
export class ComicPublishersListComponent implements OnInit {
|
||||||
|
publishers = signal<Publisher[]>([]);
|
||||||
|
isFetching = signal(false);
|
||||||
|
error = signal('');
|
||||||
|
private publisherService = inject(PublisherService);
|
||||||
|
private destroyRef = inject(DestroyRef);
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.isFetching.set(true);
|
||||||
|
const subscription = this.publisherService.loadPublishers().subscribe({
|
||||||
|
next: (publishers) => {
|
||||||
|
this.publishers.set(publishers);
|
||||||
|
},
|
||||||
|
error: (error: Error) => {
|
||||||
|
this.error.set(error.message);
|
||||||
|
},
|
||||||
|
complete: () => {
|
||||||
|
this.isFetching.set(false);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
this.destroyRef.onDestroy(() => {
|
||||||
|
subscription.unsubscribe();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<div class="grid-container">
|
||||||
|
<div>
|
||||||
|
<app-comic-publishers-list />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
@if (publisher()) {
|
||||||
|
<section>
|
||||||
|
<h2>{{ publisher().name }}</h2>
|
||||||
|
</section>
|
||||||
|
} @else {
|
||||||
|
<h2>Publisher Details</h2>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ComicPublishersComponent } from './comic-publishers.component';
|
||||||
|
|
||||||
|
describe('ComicPublishersComponent', () => {
|
||||||
|
let component: ComicPublishersComponent;
|
||||||
|
let fixture: ComponentFixture<ComicPublishersComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [ComicPublishersComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ComicPublishersComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { Component, inject, input } from '@angular/core';
|
||||||
|
import { Publisher, PublisherDetails } from '../comic.model';
|
||||||
|
import { ComicPublishersListComponent } from '../comic-publishers-list/comic-publishers-list.component';
|
||||||
|
import { ActivatedRouteSnapshot, ResolveFn, RouterStateSnapshot } from '@angular/router';
|
||||||
|
import { PublisherService } from './publisher.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-comic-publishers',
|
||||||
|
imports: [ComicPublishersListComponent],
|
||||||
|
templateUrl: './comic-publishers.component.html',
|
||||||
|
styleUrl: './comic-publishers.component.css'
|
||||||
|
})
|
||||||
|
export class ComicPublishersComponent {
|
||||||
|
publisher = input.required<Publisher>();
|
||||||
|
}
|
||||||
|
|
||||||
|
export const publisherResolver: ResolveFn<PublisherDetails> = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => {
|
||||||
|
const publisherService = inject(PublisherService);
|
||||||
|
const publisherId = route.paramMap.get('publisherId');
|
||||||
|
const publisherDetails = publisherService.loadPublisherDetails(publisherId);
|
||||||
|
console.log(publisherDetails);
|
||||||
|
return publisherDetails;
|
||||||
|
};
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import { inject, Injectable, signal } from "@angular/core";
|
||||||
|
import { HttpClient } from "@angular/common/http";
|
||||||
|
import { catchError, map, throwError } from "rxjs";
|
||||||
|
import { ErrorService } from "../../../shared/error.service";
|
||||||
|
import { Publisher, PublisherDetails } from "../comic.model";
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class PublisherService {
|
||||||
|
private errorService = inject(ErrorService);
|
||||||
|
private httpClient = inject(HttpClient);
|
||||||
|
private publishers = signal<Publisher[]>([]);
|
||||||
|
|
||||||
|
loadedPublishers = this.publishers.asReadonly();
|
||||||
|
|
||||||
|
loadPublishers() {
|
||||||
|
return this.fetchPublishers('http://127.0.0.1:8800/api/comics/publishers', 'Someting went wrong fetching artists. Please try again later-');
|
||||||
|
}
|
||||||
|
|
||||||
|
loadPublisherDetails(artistId: string | null) {
|
||||||
|
return this.fetchPublisherDetails('http://127.0.0.1:8800/api/comics/publishers/' + artistId, 'Someting went wrong fetching comic artists. Please try again later.');
|
||||||
|
}
|
||||||
|
|
||||||
|
private fetchPublishers(url: string, errorMessage: string) {
|
||||||
|
return this.httpClient.get<Publisher[]>(url).pipe(
|
||||||
|
map((resData) => resData),
|
||||||
|
catchError((error) => {
|
||||||
|
console.log(error);
|
||||||
|
return throwError(() => new Error(errorMessage));
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private fetchPublisherDetails(url: string, errorMessage: string) {
|
||||||
|
return this.httpClient.get<PublisherDetails>(url).pipe(
|
||||||
|
map((resData) => {
|
||||||
|
console.log(resData);
|
||||||
|
return resData;
|
||||||
|
}),
|
||||||
|
catchError((error) => {
|
||||||
|
console.log(error);
|
||||||
|
return throwError(() => new Error(errorMessage));
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
<app-comic-navigation />
|
||||||
|
<router-outlet></router-outlet>
|
||||||
+6
-6
@@ -1,18 +1,18 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { ArtistDetailComponent } from './artist-detail.component';
|
import { ComicSectionComponent } from './comic-section.component';
|
||||||
|
|
||||||
describe('ArtistDetailComponent', () => {
|
describe('ComicSectionComponent', () => {
|
||||||
let component: ArtistDetailComponent;
|
let component: ComicSectionComponent;
|
||||||
let fixture: ComponentFixture<ArtistDetailComponent>;
|
let fixture: ComponentFixture<ComicSectionComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [ArtistDetailComponent]
|
imports: [ComicSectionComponent]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
|
|
||||||
fixture = TestBed.createComponent(ArtistDetailComponent);
|
fixture = TestBed.createComponent(ComicSectionComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { ComicNavigationComponent } from "../comic-navigation/comic-navigation.component";
|
||||||
|
import { RouterOutlet } from '@angular/router';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-comic-section',
|
||||||
|
imports: [ComicNavigationComponent, RouterOutlet],
|
||||||
|
templateUrl: './comic-section.component.html',
|
||||||
|
styleUrl: './comic-section.component.css'
|
||||||
|
})
|
||||||
|
export class ComicSectionComponent {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import { Routes } from "@angular/router";
|
||||||
|
import { artistResolver, ComicArtistsComponent } from "../comic-artists/comic-artists.component";
|
||||||
|
import { ComicPublishersComponent, publisherResolver } from './../comic-publishers/comic-publishers.component';
|
||||||
|
import { ComicComicsComponent, comicResolver } from "../comic-comics/comic-comics.component";
|
||||||
|
|
||||||
|
export const comicRoutes: Routes = [
|
||||||
|
{
|
||||||
|
path: 'comics',
|
||||||
|
component: ComicComicsComponent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'comics/:comicId',
|
||||||
|
component: ComicComicsComponent,
|
||||||
|
resolve: {
|
||||||
|
comic: comicResolver
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'publisher',
|
||||||
|
component: ComicPublishersComponent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'publisher/:publisherId',
|
||||||
|
component: ComicPublishersComponent,
|
||||||
|
resolve: {
|
||||||
|
publisher: publisherResolver
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'artist',
|
||||||
|
component: ComicArtistsComponent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'artist/:artistId',
|
||||||
|
component: ComicArtistsComponent,
|
||||||
|
resolve: {
|
||||||
|
artist: artistResolver
|
||||||
|
}
|
||||||
|
},
|
||||||
|
];
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<div>
|
||||||
|
<a [routerLink]="['/', 'comic', 'worktype', worktype().id]" routerLinkActive="active">
|
||||||
|
<span>{{ worktype().name }}</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
+6
-6
@@ -1,18 +1,18 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { PublisherListComponent } from './publisher-list.component';
|
import { ComicWorktypeComponent } from './comic-worktype.component';
|
||||||
|
|
||||||
describe('PublisherListComponent', () => {
|
describe('ComicWorktypeComponent', () => {
|
||||||
let component: PublisherListComponent;
|
let component: ComicWorktypeComponent;
|
||||||
let fixture: ComponentFixture<PublisherListComponent>;
|
let fixture: ComponentFixture<ComicWorktypeComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [PublisherListComponent]
|
imports: [ComicWorktypeComponent]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
|
|
||||||
fixture = TestBed.createComponent(PublisherListComponent);
|
fixture = TestBed.createComponent(ComicWorktypeComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { Component, input } from '@angular/core';
|
||||||
|
import { RouterLink, RouterLinkActive } from '@angular/router';
|
||||||
|
import { Worktype } from '../comic.model';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-comic-worktype',
|
||||||
|
imports: [RouterLink, RouterLinkActive],
|
||||||
|
templateUrl: './comic-worktype.component.html',
|
||||||
|
styleUrl: './comic-worktype.component.css'
|
||||||
|
})
|
||||||
|
export class ComicWorktypeComponent {
|
||||||
|
worktype = input.required<Worktype>();
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
export interface Artist {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Worktype {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Publisher {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PublisherDetails {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
}
|
||||||
|
export interface Comic {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
completed: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Volume {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Issue {
|
||||||
|
id: string;
|
||||||
|
issue_number: string;
|
||||||
|
in_stock: boolean;
|
||||||
|
is_read: boolean;
|
||||||
|
comic: Comic;
|
||||||
|
volume: Volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ComicWork {
|
||||||
|
worktype: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ComicWorktypeArtists {
|
||||||
|
worktype: Worktype;
|
||||||
|
artists: Artist[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ComicDetails {
|
||||||
|
id: string;
|
||||||
|
created: string;
|
||||||
|
title: string;
|
||||||
|
completed: boolean;
|
||||||
|
current_order: boolean;
|
||||||
|
weblink: string;
|
||||||
|
publisher: Publisher;
|
||||||
|
issues: Issue[];
|
||||||
|
volumes: Volume[];
|
||||||
|
works: ComicWorktypeArtists[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ArtistWorktypeComics {
|
||||||
|
worktype: Worktype;
|
||||||
|
comics: Comic[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ArtistWorktypeIssues {
|
||||||
|
worktype: Worktype;
|
||||||
|
issues: Issue[];
|
||||||
|
}
|
||||||
|
export interface ArtistDetails {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
weblink: string;
|
||||||
|
comic_works: ArtistWorktypeComics[];
|
||||||
|
issue_works: ArtistWorktypeIssues[];
|
||||||
|
}
|
||||||
@@ -1,7 +1,12 @@
|
|||||||
/* Footer */
|
/* Footer */
|
||||||
a {
|
.footer {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background: #ddd;
|
background-color: lightblue;
|
||||||
margin-top: 20px;
|
position: sticky;
|
||||||
|
bottom: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer a {
|
||||||
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
<div>
|
<!-- <div> -->
|
||||||
|
<footer class="footer">
|
||||||
<a href="{{ footerUrl }}">{{ footerLink }}</a>
|
<a href="{{ footerUrl }}">{{ footerLink }}</a>
|
||||||
</div>
|
</footer>
|
||||||
|
<!-- </div> -->
|
||||||
|
|||||||
@@ -9,5 +9,4 @@ import { Component } from '@angular/core';
|
|||||||
export class KontorFooterComponent {
|
export class KontorFooterComponent {
|
||||||
footerUrl = "https://kontor.thpeetz.de";
|
footerUrl = "https://kontor.thpeetz.de";
|
||||||
footerLink = "kontor.thpeetz.de";
|
footerLink = "kontor.thpeetz.de";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,18 +2,4 @@
|
|||||||
<section>
|
<section>
|
||||||
<h1>{{ title() }}</h1>
|
<h1>{{ title() }}</h1>
|
||||||
</section>
|
</section>
|
||||||
<!-- <nav>
|
|
||||||
<ul>
|
|
||||||
<li><a routerLink="/">Kontor</a></li>
|
|
||||||
<li><a routerLink="/comic">Comics</a></li>
|
|
||||||
<li><a routerLink="/tysc">TradeYourSportsCards</a></li>
|
|
||||||
<li><a routerLink="/media">Media</a></li>
|
|
||||||
</ul>
|
|
||||||
</nav> -->
|
|
||||||
</header>
|
</header>
|
||||||
<div class="topnav">
|
|
||||||
<a routerLink="/" routerLinkActive="active">Kontor</a>
|
|
||||||
<a routerLink="/comic" routerLinkActive="active">Comics</a>
|
|
||||||
<a routerLink="/tysc" routerLinkActive="active">TradeYourSportsCards</a>
|
|
||||||
<a routerLink="/media" routerLinkActive="active">Media</a>
|
|
||||||
</div>
|
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import { Component, signal } from '@angular/core';
|
import { Component, signal } from '@angular/core';
|
||||||
import { RouterLink, RouterLinkActive } from '@angular/router';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'kontor-header',
|
selector: 'kontor-header',
|
||||||
imports: [RouterLink, RouterLinkActive],
|
imports: [],
|
||||||
templateUrl: './header.component.html',
|
templateUrl: './header.component.html',
|
||||||
styleUrl: './header.component.css'
|
styleUrl: './header.component.css'
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
<p>media-actors works!</p>
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { MediaActorsComponent } from './media-actors.component';
|
||||||
|
|
||||||
|
describe('MediaActorsComponent', () => {
|
||||||
|
let component: MediaActorsComponent;
|
||||||
|
let fixture: ComponentFixture<MediaActorsComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [MediaActorsComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(MediaActorsComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-media-actors',
|
||||||
|
imports: [],
|
||||||
|
templateUrl: './media-actors.component.html',
|
||||||
|
styleUrl: './media-actors.component.css'
|
||||||
|
})
|
||||||
|
export class MediaActorsComponent {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<div>
|
||||||
|
<article>
|
||||||
|
<a [routerLink]="['/', 'media', 'file', mediafile().id]" routerLinkActive="active">
|
||||||
|
<span>{{ mediafile().title }}</span>
|
||||||
|
</a>
|
||||||
|
<a href="{{ mediafile().url }}"> >></a>
|
||||||
|
<p>
|
||||||
|
<span>
|
||||||
|
@if (mediafile().review) {
|
||||||
|
Review<img class="images" src="tick.png" />
|
||||||
|
} @else {
|
||||||
|
Review<img class="images" src="cross.png" />
|
||||||
|
}
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
+6
-6
@@ -1,18 +1,18 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { SportListComponent } from './sport-list.component';
|
import { MediaFileComponent } from './media-file.component';
|
||||||
|
|
||||||
describe('SportListComponent', () => {
|
describe('MediaFileComponent', () => {
|
||||||
let component: SportListComponent;
|
let component: MediaFileComponent;
|
||||||
let fixture: ComponentFixture<SportListComponent>;
|
let fixture: ComponentFixture<MediaFileComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [SportListComponent]
|
imports: [MediaFileComponent]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
|
|
||||||
fixture = TestBed.createComponent(SportListComponent);
|
fixture = TestBed.createComponent(MediaFileComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { Component, input } from '@angular/core';
|
||||||
|
import { MediaFile } from '../media-files/media-file.model';
|
||||||
|
import { RouterLink, RouterLinkActive } from '@angular/router';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-media-file',
|
||||||
|
imports: [RouterLink, RouterLinkActive],
|
||||||
|
templateUrl: './media-file.component.html',
|
||||||
|
styleUrl: './media-file.component.css'
|
||||||
|
})
|
||||||
|
export class MediaFileComponent {
|
||||||
|
mediafile = input.required<MediaFile>();
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
ul {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<ul>
|
||||||
|
@for (mediafile of files(); track mediafile.id) {
|
||||||
|
<li>
|
||||||
|
<app-media-file [mediafile]="mediafile" />
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
+6
-6
@@ -1,18 +1,18 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { MediaActorListComponent } from './media-actor-list.component';
|
import { MediaFilesListComponent } from './media-files-list.component';
|
||||||
|
|
||||||
describe('MediaActorListComponent', () => {
|
describe('MediaFilesListComponent', () => {
|
||||||
let component: MediaActorListComponent;
|
let component: MediaFilesListComponent;
|
||||||
let fixture: ComponentFixture<MediaActorListComponent>;
|
let fixture: ComponentFixture<MediaFilesListComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [MediaActorListComponent]
|
imports: [MediaFilesListComponent]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
|
|
||||||
fixture = TestBed.createComponent(MediaActorListComponent);
|
fixture = TestBed.createComponent(MediaFilesListComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { Component, DestroyRef, inject, OnInit, signal } from '@angular/core';
|
||||||
|
import { MediaFileComponent } from '../media-file/media-file.component';
|
||||||
|
import { MediaFileService } from '../media-files/media-file.service';
|
||||||
|
import { MediaFile } from '../media-files/media-file.model';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-media-files-list',
|
||||||
|
imports: [MediaFileComponent],
|
||||||
|
templateUrl: './media-files-list.component.html',
|
||||||
|
styleUrl: './media-files-list.component.css'
|
||||||
|
})
|
||||||
|
export class MediaFilesListComponent implements OnInit {
|
||||||
|
files = signal<MediaFile[]>([]);
|
||||||
|
isFetching = signal(false);
|
||||||
|
error = signal('');
|
||||||
|
private mediaFileService = inject(MediaFileService);
|
||||||
|
private destroyRef = inject(DestroyRef);
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.isFetching.set(true);
|
||||||
|
const subscription = this.mediaFileService.loadFiles().subscribe({
|
||||||
|
next: (files) => {
|
||||||
|
this.files.set(files);
|
||||||
|
},
|
||||||
|
error: (error: Error) => {
|
||||||
|
this.error.set(error.message);
|
||||||
|
},
|
||||||
|
complete: () => {
|
||||||
|
this.isFetching.set(false);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
this.destroyRef.onDestroy(() => {
|
||||||
|
subscription.unsubscribe();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { StreamingResourceOptions } from "@angular/core";
|
||||||
|
|
||||||
|
export interface MediaFile {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
file_name: string;
|
||||||
|
cloud_link: string;
|
||||||
|
url: string;
|
||||||
|
review: boolean;
|
||||||
|
should_download: boolean;
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { inject, Injectable, signal } from "@angular/core";
|
||||||
|
import { ErrorService } from "../../../shared/error.service";
|
||||||
|
import { HttpClient } from "@angular/common/http";
|
||||||
|
import { MediaFile } from "./media-file.model";
|
||||||
|
import { catchError, map, throwError } from "rxjs";
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class MediaFileService {
|
||||||
|
private errorService = inject(ErrorService);
|
||||||
|
private httpClient = inject(HttpClient);
|
||||||
|
private files = signal<MediaFile[]>([]);
|
||||||
|
|
||||||
|
loadedFiles = this.files.asReadonly();
|
||||||
|
|
||||||
|
loadFiles() {
|
||||||
|
return this.fetchMediaFiles('http://127.0.0.1:8800/api/media/files', 'Someting went wrong fetching artists. Please try again later-');
|
||||||
|
}
|
||||||
|
|
||||||
|
private fetchMediaFiles(url: string, errorMessage: string) {
|
||||||
|
return this.httpClient.get<MediaFile[]>(url).pipe(
|
||||||
|
map((resData) => resData),
|
||||||
|
catchError((error) => {
|
||||||
|
console.log(error);
|
||||||
|
return throwError(() => new Error(errorMessage));
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
.grid-container {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
grid-gap: 20px;
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<div class="grid-container">
|
||||||
|
<div>
|
||||||
|
<app-media-files-list />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
+6
-6
@@ -1,18 +1,18 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { ComicListComponent } from './comic-list.component';
|
import { MediaFilesComponent } from './media-files.component';
|
||||||
|
|
||||||
describe('ComicListComponent', () => {
|
describe('MediaFilesComponent', () => {
|
||||||
let component: ComicListComponent;
|
let component: MediaFilesComponent;
|
||||||
let fixture: ComponentFixture<ComicListComponent>;
|
let fixture: ComponentFixture<MediaFilesComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [ComicListComponent]
|
imports: [MediaFilesComponent]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
|
|
||||||
fixture = TestBed.createComponent(ComicListComponent);
|
fixture = TestBed.createComponent(MediaFilesComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { MediaFilesListComponent } from '../media-files-list/media-files-list.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-media-files',
|
||||||
|
imports: [MediaFilesListComponent],
|
||||||
|
templateUrl: './media-files.component.html',
|
||||||
|
styleUrl: './media-files.component.css'
|
||||||
|
})
|
||||||
|
export class MediaFilesComponent {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<div class="subnav">
|
||||||
|
<a routerLink="/media/file" routerLinkActive="active">MediaFiles</a>
|
||||||
|
<a routerLink="/media/actor" routerLinkActive="active">MediaActors</a>
|
||||||
|
<a routerLink="/media/video" routerLinkActive="active">MediaVideos</a>
|
||||||
|
</div>
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { MediaNavigationComponent } from './media-navigation.component';
|
||||||
|
|
||||||
|
describe('MediaNavigationComponent', () => {
|
||||||
|
let component: MediaNavigationComponent;
|
||||||
|
let fixture: ComponentFixture<MediaNavigationComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [MediaNavigationComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(MediaNavigationComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { RouterLink, RouterLinkActive } from '@angular/router';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-media-navigation',
|
||||||
|
imports: [RouterLink, RouterLinkActive],
|
||||||
|
templateUrl: './media-navigation.component.html',
|
||||||
|
styleUrl: './media-navigation.component.css'
|
||||||
|
})
|
||||||
|
export class MediaNavigationComponent {
|
||||||
|
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user