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