diff --git a/src/app/app.component.html b/src/app/app.component.html index 3f517a3..b990763 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,4 +1,7 @@
- + +
+ +
diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 7a1422d..f9e6fc0 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -7,7 +7,7 @@ import {FormsModule} from '@angular/forms'; import {BarComponent} from './component/bar/bar.component'; import {ScrollingModule} from '@angular/cdk/scrolling'; import {ApiModule} from './openapi'; -import {ItemsComponent} from './component/items/items.component'; +import {RouterOutlet} from '@angular/router'; @Component({ selector: 'app-root', @@ -21,8 +21,7 @@ import {ItemsComponent} from './component/items/items.component'; BarComponent, ScrollingModule, ApiModule, - ItemsComponent, - ItemsComponent + RouterOutlet ], templateUrl: './app.component.html', styleUrl: './app.component.css', diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index 1b22f31..f04a267 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -1,6 +1,8 @@ -import { Routes } from '@angular/router'; -import {ItemsComponent} from './component/items/items.component'; +import {Routes} from '@angular/router'; +import {DecksComponent} from './views/decks/decks.component'; +import {CardsComponent} from './views/cards/cards.component'; export const routes: Routes = [ - { path: '', component: ItemsComponent }, + { path: 'cards', component: CardsComponent }, + { path: 'decks', component: DecksComponent }, ]; diff --git a/src/app/component/bar/bar.component.ts b/src/app/component/bar/bar.component.ts index 4cb31ef..3b81b83 100644 --- a/src/app/component/bar/bar.component.ts +++ b/src/app/component/bar/bar.component.ts @@ -30,51 +30,22 @@ export class BarComponent implements OnInit { ngOnInit() { this.items = [ { - label: 'Home (Cards)', - icon: 'pi pi-home', - command: () => { - this.router.navigate(['/cards']); + label: 'Home (TBD)', + icon: 'pi pi-home' + }, + { + label: 'Decks', + icon: 'pi pi-star', + command: async () => { + await this.router.navigate(['/decks']); } }, { - label: 'Features', - icon: 'pi pi-star' - }, - { - label: 'Projects', + label: 'Cards', icon: 'pi pi-search', - items: [ - { - label: 'Components', - icon: 'pi pi-bolt' - }, - { - label: 'Blocks', - icon: 'pi pi-server' - }, - { - label: 'UI Kit', - icon: 'pi pi-pencil' - }, - { - label: 'Templates', - icon: 'pi pi-palette', - items: [ - { - label: 'Apollo', - icon: 'pi pi-palette' - }, - { - label: 'Ultima', - icon: 'pi pi-palette' - } - ] - } - ] - }, - { - label: 'Contact', - icon: 'pi pi-envelope' + command: async () => { + await this.router.navigate(['/cards']); + } } ] } diff --git a/src/app/component/items/items.component.html b/src/app/component/items/items.component.html index 11d52f1..6e5f7c2 100644 --- a/src/app/component/items/items.component.html +++ b/src/app/component/items/items.component.html @@ -2,17 +2,19 @@ @if (cards.length > 0) {
@for (card of options.items; track card.id) { - +
lastIndexInPage ) { - return; - } - console.log("piss3") - this.lazyLoading = true + + if (lastIndexInPage % this.pageSize !== 0) { + this.lazyLoading = false + return; + } + this.page++; this.cardService.getCards( @@ -118,10 +115,13 @@ export class ItemsComponent implements OnInit, AfterViewInit { this.pageSize, ).subscribe({ next: cards => { - console.log(`pageSize ${this.pageSize}`); - console.log(`response size ${cards.length}`); - this.cards = this.cards.concat(cards); - console.log(`total cards ${this.cards.length}`); + for (const card of cards) { + this.cards.push(card) + } + this.lazyLoading = false; + }, + error: error => { + console.log(error); this.lazyLoading = false; } }); diff --git a/src/app/openapi/.openapi-generator/FILES b/src/app/openapi/.openapi-generator/FILES index 2633287..996c7a7 100644 --- a/src/app/openapi/.openapi-generator/FILES +++ b/src/app/openapi/.openapi-generator/FILES @@ -4,7 +4,7 @@ api.base.service.ts api.module.ts api/api.ts api/card.service.ts -api/deck-controller.service.ts +api/deck.service.ts configuration.ts encoder.ts git_push.sh @@ -12,5 +12,6 @@ index.ts model/card.ts model/deck.ts model/models.ts +model/page-deck.ts param.ts variables.ts diff --git a/src/app/openapi/api/api.ts b/src/app/openapi/api/api.ts index e5aa814..e158fb6 100644 --- a/src/app/openapi/api/api.ts +++ b/src/app/openapi/api/api.ts @@ -1,5 +1,5 @@ export * from './card.service'; import { CardService } from './card.service'; -export * from './deck-controller.service'; -import { DeckControllerService } from './deck-controller.service'; -export const APIS = [CardService, DeckControllerService]; +export * from './deck.service'; +import { DeckService } from './deck.service'; +export const APIS = [CardService, DeckService]; diff --git a/src/app/openapi/api/card.service.ts b/src/app/openapi/api/card.service.ts index 7327fa7..7afeb72 100644 --- a/src/app/openapi/api/card.service.ts +++ b/src/app/openapi/api/card.service.ts @@ -1,7 +1,7 @@ /** * dex API * - * + * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). * https://openapi-generator.tech @@ -11,7 +11,7 @@ import { Inject, Injectable, Optional } from '@angular/core'; import { HttpClient, HttpHeaders, HttpParams, - HttpResponse, HttpEvent, HttpParameterCodec, HttpContext + HttpResponse, HttpEvent, HttpParameterCodec, HttpContext } from '@angular/common/http'; import { CustomHttpParameterCodec } from '../encoder'; import { Observable } from 'rxjs'; @@ -37,7 +37,7 @@ export class CardService extends BaseService { /** * Get a singular Card by its ID - * @param id + * @param id * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ @@ -90,7 +90,7 @@ export class CardService extends BaseService { /** * Get the image of a Card by its ID - * @param id + * @param id * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ @@ -132,9 +132,9 @@ export class CardService extends BaseService { /** * Get a page of Cards with optional name query parameter - * @param name - * @param page - * @param pageSize + * @param name + * @param page + * @param pageSize * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. * @param reportProgress flag to report request and response progress. */ @@ -142,6 +142,7 @@ export class CardService extends BaseService { public getCards(name?: string, page?: number, pageSize?: number, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>>; public getCards(name?: string, page?: number, pageSize?: number, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>>; public getCards(name?: string, page?: number, pageSize?: number, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable { + let localVarQueryParameters = new HttpParams({encoder: this.encoder}); localVarQueryParameters = this.addToHttpParams(localVarQueryParameters, name, 'name'); diff --git a/src/app/openapi/api/deck.service.ts b/src/app/openapi/api/deck.service.ts new file mode 100644 index 0000000..64c9e05 --- /dev/null +++ b/src/app/openapi/api/deck.service.ts @@ -0,0 +1,272 @@ +/** + * dex API + * + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ +/* tslint:disable:no-unused-variable member-ordering */ + +import { Inject, Injectable, Optional } from '@angular/core'; +import { HttpClient, HttpHeaders, HttpParams, + HttpResponse, HttpEvent, HttpParameterCodec, HttpContext + } from '@angular/common/http'; +import { CustomHttpParameterCodec } from '../encoder'; +import { Observable } from 'rxjs'; + +// @ts-ignore +import { Deck } from '../model/deck'; +// @ts-ignore +import { PageDeck } from '../model/page-deck'; + +// @ts-ignore +import { BASE_PATH, COLLECTION_FORMATS } from '../variables'; +import { Configuration } from '../configuration'; +import { BaseService } from '../api.base.service'; + + + +@Injectable({ + providedIn: 'root' +}) +export class DeckService extends BaseService { + + constructor(protected httpClient: HttpClient, @Optional() @Inject(BASE_PATH) basePath: string|string[], @Optional() configuration?: Configuration) { + super(basePath, configuration); + } + + /** + * Add a Card by its ID to a Deck by its name + * @param cardId + * @param deckName + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public addCardToDeck(cardId: number, deckName: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable; + public addCardToDeck(cardId: number, deckName: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable>; + public addCardToDeck(cardId: number, deckName: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable>; + public addCardToDeck(cardId: number, deckName: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable { + if (cardId === null || cardId === undefined) { + throw new Error('Required parameter cardId was null or undefined when calling addCardToDeck.'); + } + if (deckName === null || deckName === undefined) { + throw new Error('Required parameter deckName was null or undefined when calling addCardToDeck.'); + } + + let localVarHeaders = this.defaultHeaders; + + const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([ + ]); + if (localVarHttpHeaderAcceptSelected !== undefined) { + localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected); + } + + const localVarHttpContext: HttpContext = options?.context ?? new HttpContext(); + + const localVarTransferCache: boolean = options?.transferCache ?? true; + + + let responseType_: 'text' | 'json' | 'blob' = 'json'; + if (localVarHttpHeaderAcceptSelected) { + if (localVarHttpHeaderAcceptSelected.startsWith('text')) { + responseType_ = 'text'; + } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) { + responseType_ = 'json'; + } else { + responseType_ = 'blob'; + } + } + + let localVarPath = `/api/decks/${this.configuration.encodeParam({name: "deckName", value: deckName, in: "path", style: "simple", explode: false, dataType: "string", dataFormat: undefined})}/${this.configuration.encodeParam({name: "cardId", value: cardId, in: "path", style: "simple", explode: false, dataType: "number", dataFormat: "int64"})}`; + return this.httpClient.request('post', `${this.configuration.basePath}${localVarPath}`, + { + context: localVarHttpContext, + responseType: responseType_, + withCredentials: this.configuration.withCredentials, + headers: localVarHeaders, + observe: observe, + transferCache: localVarTransferCache, + reportProgress: reportProgress + } + ); + } + + /** + * Create a Deck with a given name + * @param deck + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public createDeck(deck: Deck, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable; + public createDeck(deck: Deck, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable>; + public createDeck(deck: Deck, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable>; + public createDeck(deck: Deck, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: undefined, context?: HttpContext, transferCache?: boolean}): Observable { + if (deck === null || deck === undefined) { + throw new Error('Required parameter deck was null or undefined when calling createDeck.'); + } + + let localVarHeaders = this.defaultHeaders; + + const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([ + ]); + if (localVarHttpHeaderAcceptSelected !== undefined) { + localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected); + } + + const localVarHttpContext: HttpContext = options?.context ?? new HttpContext(); + + const localVarTransferCache: boolean = options?.transferCache ?? true; + + + // to determine the Content-Type header + const consumes: string[] = [ + 'application/json' + ]; + const httpContentTypeSelected: string | undefined = this.configuration.selectHeaderContentType(consumes); + if (httpContentTypeSelected !== undefined) { + localVarHeaders = localVarHeaders.set('Content-Type', httpContentTypeSelected); + } + + let responseType_: 'text' | 'json' | 'blob' = 'json'; + if (localVarHttpHeaderAcceptSelected) { + if (localVarHttpHeaderAcceptSelected.startsWith('text')) { + responseType_ = 'text'; + } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) { + responseType_ = 'json'; + } else { + responseType_ = 'blob'; + } + } + + let localVarPath = `/api/decks`; + return this.httpClient.request('post', `${this.configuration.basePath}${localVarPath}`, + { + context: localVarHttpContext, + body: deck, + responseType: responseType_, + withCredentials: this.configuration.withCredentials, + headers: localVarHeaders, + observe: observe, + transferCache: localVarTransferCache, + reportProgress: reportProgress + } + ); + } + + /** + * Get a singular Deck by its name + * @param name + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public getDeckByName(name: string, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable; + public getDeckByName(name: string, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>; + public getDeckByName(name: string, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>; + public getDeckByName(name: string, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable { + if (name === null || name === undefined) { + throw new Error('Required parameter name was null or undefined when calling getDeckByName.'); + } + + let localVarHeaders = this.defaultHeaders; + + const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([ + 'application/json' + ]); + if (localVarHttpHeaderAcceptSelected !== undefined) { + localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected); + } + + const localVarHttpContext: HttpContext = options?.context ?? new HttpContext(); + + const localVarTransferCache: boolean = options?.transferCache ?? true; + + + let responseType_: 'text' | 'json' | 'blob' = 'json'; + if (localVarHttpHeaderAcceptSelected) { + if (localVarHttpHeaderAcceptSelected.startsWith('text')) { + responseType_ = 'text'; + } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) { + responseType_ = 'json'; + } else { + responseType_ = 'blob'; + } + } + + let localVarPath = `/api/decks/${this.configuration.encodeParam({name: "name", value: name, in: "path", style: "simple", explode: false, dataType: "string", dataFormat: undefined})}`; + return this.httpClient.request('get', `${this.configuration.basePath}${localVarPath}`, + { + context: localVarHttpContext, + responseType: responseType_, + withCredentials: this.configuration.withCredentials, + headers: localVarHeaders, + observe: observe, + transferCache: localVarTransferCache, + reportProgress: reportProgress + } + ); + } + + /** + * Get a page of Decks with optional name query parameter + * @param name + * @param page + * @param pageSize + * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body. + * @param reportProgress flag to report request and response progress. + */ + public getDecks(name?: string, page?: number, pageSize?: number, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable; + public getDecks(name?: string, page?: number, pageSize?: number, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>; + public getDecks(name?: string, page?: number, pageSize?: number, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable>; + public getDecks(name?: string, page?: number, pageSize?: number, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json', context?: HttpContext, transferCache?: boolean}): Observable { + + let localVarQueryParameters = new HttpParams({encoder: this.encoder}); + localVarQueryParameters = this.addToHttpParams(localVarQueryParameters, + name, 'name'); + localVarQueryParameters = this.addToHttpParams(localVarQueryParameters, + page, 'page'); + localVarQueryParameters = this.addToHttpParams(localVarQueryParameters, + pageSize, 'pageSize'); + + let localVarHeaders = this.defaultHeaders; + + const localVarHttpHeaderAcceptSelected: string | undefined = options?.httpHeaderAccept ?? this.configuration.selectHeaderAccept([ + 'application/json' + ]); + if (localVarHttpHeaderAcceptSelected !== undefined) { + localVarHeaders = localVarHeaders.set('Accept', localVarHttpHeaderAcceptSelected); + } + + const localVarHttpContext: HttpContext = options?.context ?? new HttpContext(); + + const localVarTransferCache: boolean = options?.transferCache ?? true; + + + let responseType_: 'text' | 'json' | 'blob' = 'json'; + if (localVarHttpHeaderAcceptSelected) { + if (localVarHttpHeaderAcceptSelected.startsWith('text')) { + responseType_ = 'text'; + } else if (this.configuration.isJsonMime(localVarHttpHeaderAcceptSelected)) { + responseType_ = 'json'; + } else { + responseType_ = 'blob'; + } + } + + let localVarPath = `/api/decks`; + return this.httpClient.request('get', `${this.configuration.basePath}${localVarPath}`, + { + context: localVarHttpContext, + params: localVarQueryParameters, + responseType: responseType_, + withCredentials: this.configuration.withCredentials, + headers: localVarHeaders, + observe: observe, + transferCache: localVarTransferCache, + reportProgress: reportProgress + } + ); + } + +} diff --git a/src/app/openapi/model/models.ts b/src/app/openapi/model/models.ts index d8c7b36..4410d68 100644 --- a/src/app/openapi/model/models.ts +++ b/src/app/openapi/model/models.ts @@ -1,2 +1,3 @@ export * from './card'; export * from './deck'; +export * from './page-deck'; diff --git a/src/app/openapi/model/page-deck.ts b/src/app/openapi/model/page-deck.ts new file mode 100644 index 0000000..9d97c42 --- /dev/null +++ b/src/app/openapi/model/page-deck.ts @@ -0,0 +1,19 @@ +/** + * dex API + * + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ +import { Deck } from './deck'; + + +export interface PageDeck { + content: Array; + page?: number; + pageSize?: number; + totalPages?: number; +} + diff --git a/src/app/views/card-item/card-item.component.css b/src/app/views/card-item/card-item.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/views/card-item/card-item.component.html b/src/app/views/card-item/card-item.component.html new file mode 100644 index 0000000..96e04b8 --- /dev/null +++ b/src/app/views/card-item/card-item.component.html @@ -0,0 +1 @@ +

card-item works!

diff --git a/src/app/views/card-item/card-item.component.spec.ts b/src/app/views/card-item/card-item.component.spec.ts new file mode 100644 index 0000000..2705481 --- /dev/null +++ b/src/app/views/card-item/card-item.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CardItemComponent } from './card-item.component'; + +describe('CardItemComponent', () => { + let component: CardItemComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [CardItemComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(CardItemComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/views/card-item/card-item.component.ts b/src/app/views/card-item/card-item.component.ts new file mode 100644 index 0000000..4727c9f --- /dev/null +++ b/src/app/views/card-item/card-item.component.ts @@ -0,0 +1,11 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-card-item', + imports: [], + templateUrl: './card-item.component.html', + styleUrl: './card-item.component.css' +}) +export class CardItemComponent { + +} diff --git a/src/app/views/cards/cards.component.css b/src/app/views/cards/cards.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/views/cards/cards.component.html b/src/app/views/cards/cards.component.html new file mode 100644 index 0000000..e1e2b12 --- /dev/null +++ b/src/app/views/cards/cards.component.html @@ -0,0 +1,60 @@ +
+ @if (initialized) { + + +
+ @for (card of options.items; track card.id) { + + +
+
+ + Card +
+
+
+ {{ card.name }} + Card subtitle + +
+ + +
+
+

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Inventore sed consequuntur error repudiandae + numquam deserunt + quisquam repellat libero asperiores earum nam nobis, culpa ratione quam perferendis esse, cupiditate + neque + quas! +

+
+ } +
+
+
+ } @else { + hold on im pogging rn + } +
diff --git a/src/app/views/cards/cards.component.spec.ts b/src/app/views/cards/cards.component.spec.ts new file mode 100644 index 0000000..285f31d --- /dev/null +++ b/src/app/views/cards/cards.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CardsComponent } from './cards.component'; + +describe('CardsComponent', () => { + let component: CardsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [CardsComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(CardsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/views/cards/cards.component.ts b/src/app/views/cards/cards.component.ts new file mode 100644 index 0000000..d6e86c4 --- /dev/null +++ b/src/app/views/cards/cards.component.ts @@ -0,0 +1,118 @@ +// noinspection DuplicatedCode + +import {AfterViewInit, Component, ElementRef, OnInit, ViewChild} from '@angular/core'; +import {Scroller, ScrollerLazyLoadEvent} from 'primeng/scroller'; +import {Card as CardModel, CardService, PageDeck} from '../../openapi'; +import {BehaviorSubject, catchError, debounceTime, of, Subject, switchMap} from 'rxjs'; +import {Button} from 'primeng/button'; +import {Card} from 'primeng/card'; +import {PrimeTemplate} from 'primeng/api'; +import {VirtualScrollDirective} from '../../directives/virtual-scroll.directive'; + +@Component({ + selector: 'app-cards', + imports: [ + Button, + Card, + PrimeTemplate, + Scroller, + VirtualScrollDirective + ], + templateUrl: './cards.component.html', + styleUrl: './cards.component.css' +}) +export class CardsComponent implements AfterViewInit { + @ViewChild('container') containerRef!: ElementRef; + + constructor(private cardService: CardService) { + } + + private readonly FONT_SIZE_PX = 14; + + private pageSubject = new BehaviorSubject(0); + private resizeObserver!: ResizeObserver; + + pageSize: number = 5; + page: number = 0; + // noinspection PointlessArithmeticExpressionJS Card height + ( margin top/bottom) (rem) + rowHeight: number = 36 + (0.5 * 2); + itemWidth: number = 24; // Card width (rem) + cards: CardModel[] = []; + lazyLoading: boolean = false; + itemsPerRow!: number; + rowsInPage!: number; + itemSize: number = this.rowHeight; + numberOfToleratedItems: number = 5; + initialized: boolean = false; + + ngAfterViewInit(): void { + this.setupResizeObserver(); + + this.pageSubject + .pipe( + debounceTime(150), + switchMap(pageNumber => { + this.lazyLoading = true; + + return this.cardService.getCards(undefined, pageNumber, this.pageSize).pipe( + catchError(error => { + console.log(`Error: ${error}`); + + return of([]); + }) + ) + }) + ) + .subscribe(cards => { + this.lazyLoading = false; + if (!this.initialized) { + this.initialized = true; + } + + this.cards = [ + ...this.cards, + ...cards + ] + }) + } + + private setupResizeObserver(): void { + this.resizeObserver = new ResizeObserver(entries => { + for (const entry of entries) { + this.calculateRowsInPage(entry.contentRect.height); + this.calculateItemsPerRow(entry.contentRect.width); + + const itemsInViewPort = this.itemsPerRow * this.rowsInPage; + this.pageSize = itemsInViewPort; + this.numberOfToleratedItems = itemsInViewPort; + } + }); + this.resizeObserver.observe(this.containerRef.nativeElement); + } + + private calculateItemsPerRow(containerWidth: number): void { + const newItemsPerRow = Math.floor(containerWidth / (this.itemWidth * this.FONT_SIZE_PX)) || 1; + + if (newItemsPerRow !== this.itemsPerRow) { + this.itemsPerRow = newItemsPerRow; + } + this.updateVirtualScrollerSettings(); + } + + private calculateRowsInPage(containerHeight: number) { + this.rowsInPage = Math.ceil(containerHeight / (this.rowHeight * this.FONT_SIZE_PX)); + } + + private updateVirtualScrollerSettings(): void { + this.itemSize = (this.rowHeight * this.FONT_SIZE_PX) / this.itemsPerRow; + } + + onLazyLoad(event: ScrollerLazyLoadEvent) { + if (!event || event.last % this.pageSize !== 0) { + return; + } + + this.pageSubject.next(++this.page); + } + +} diff --git a/src/app/views/deck-item/deck-item.component.css b/src/app/views/deck-item/deck-item.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/views/deck-item/deck-item.component.html b/src/app/views/deck-item/deck-item.component.html new file mode 100644 index 0000000..d59f82c --- /dev/null +++ b/src/app/views/deck-item/deck-item.component.html @@ -0,0 +1 @@ +

deck-item works!

diff --git a/src/app/views/deck-item/deck-item.component.spec.ts b/src/app/views/deck-item/deck-item.component.spec.ts new file mode 100644 index 0000000..75f2ae3 --- /dev/null +++ b/src/app/views/deck-item/deck-item.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DeckItemComponent } from './deck-item.component'; + +describe('DeckItemComponent', () => { + let component: DeckItemComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [DeckItemComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(DeckItemComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/views/deck-item/deck-item.component.ts b/src/app/views/deck-item/deck-item.component.ts new file mode 100644 index 0000000..4cab6e9 --- /dev/null +++ b/src/app/views/deck-item/deck-item.component.ts @@ -0,0 +1,11 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-deck-item', + imports: [], + templateUrl: './deck-item.component.html', + styleUrl: './deck-item.component.css' +}) +export class DeckItemComponent { + +} diff --git a/src/app/views/decks/decks.component.css b/src/app/views/decks/decks.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/views/decks/decks.component.html b/src/app/views/decks/decks.component.html new file mode 100644 index 0000000..fee8ca2 --- /dev/null +++ b/src/app/views/decks/decks.component.html @@ -0,0 +1,26 @@ +
+ + +
+ Decks + +
+
+ + + Name + No. of Cards + + + + + {{ deck.name }} + {{ getCardsInDecks(deck) }} + + + In total there are 0 Decks. +
+
diff --git a/src/app/views/decks/decks.component.spec.ts b/src/app/views/decks/decks.component.spec.ts new file mode 100644 index 0000000..3f8e6ec --- /dev/null +++ b/src/app/views/decks/decks.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DecksComponent } from './decks.component'; + +describe('DecksComponent', () => { + let component: DecksComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [DecksComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(DecksComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/views/decks/decks.component.ts b/src/app/views/decks/decks.component.ts new file mode 100644 index 0000000..4095157 --- /dev/null +++ b/src/app/views/decks/decks.component.ts @@ -0,0 +1,71 @@ +import {Component, OnInit} from '@angular/core'; +import {TableModule} from 'primeng/table'; +import {Button} from 'primeng/button'; +import {FormsModule} from '@angular/forms'; +import {Deck, DeckService, PageDeck} from '../../openapi'; +import {BehaviorSubject, catchError, Observable, of, switchMap} from 'rxjs'; +import {LazyLoadEvent} from 'primeng/api'; + +@Component({ + selector: 'app-decks', + imports: [ + TableModule, + Button, + FormsModule + ], + templateUrl: './decks.component.html', + styleUrl: './decks.component.css' +}) +export class DecksComponent implements OnInit { + + constructor(private deckService: DeckService) { + } + + protected loading: boolean = true; + protected decks: Deck[] = []; + private readonly pageSubject: BehaviorSubject = new BehaviorSubject(0); + private page: number = 0; + private pageSize: number = 5; + + ngOnInit() { + this.pageSubject.pipe( + switchMap(pageNumber => { + this.loading = true; + + return this.deckService.getDecks(undefined, pageNumber, this.pageSize).pipe( + catchError(error => { + console.log(`Error: ${error}`); + + return of({ + content: [], + page: pageNumber, + pageSize: this.pageSize, + totalPages: 1 + } as PageDeck); + }) + ) + } + ) + ).subscribe(pageDeck => { + this.loading = false + + this.decks = [ + ...this.decks, + ...pageDeck.content + ] + }); + } + + lazyLoad(event: LazyLoadEvent) { + if (!event.last || event.last % this.pageSize !== 0) { + return; + } + + this.pageSubject.next(++this.page); + } + + getCardsInDecks(deck: Deck): number { + return Object.keys(deck.cards).length + } + +} diff --git a/src/styles.css b/src/styles.css index 9233e06..edfaa4b 100644 --- a/src/styles.css +++ b/src/styles.css @@ -1,7 +1,7 @@ /* You can add global styles to this file, and also import other style files */ @import "tailwindcss"; @import "primeicons/primeicons.css"; -@plugin "tailwindcss-primeui"; +@import "tailwindcss-primeui"; html { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica,