Merged PR 1944: feat(checkout-reward): #5258

- feat(loyalty): add loyalty program feature with list and navigation
- fix(isa-app-side-menu): Update customer expand to signals
- feat(catalogue-data-access): add searchLoyaltyItems method with comprehensive test coverage
- feat(project-structure): migrate loyalty system to reward-based architecture
- feat(checkout-reward): add query settings resolver and catalog resource
- feat(swagger-cat-search-api): Swagger Update
- feat(checkout-reward): update API call and prepare filter integration

Refs: #5258
This commit is contained in:
Nino Righi
2025-09-12 10:44:42 +00:00
committed by Lorenz Hilpert
parent e00de7598d
commit 707802ce0d
93 changed files with 1479 additions and 280 deletions

View File

@@ -185,6 +185,11 @@ const routes: Routes = [
resolve: { process: tabResolverFn, tab: tabResolverFn },
canActivate: [IsAuthenticatedGuard],
children: [
{
path: 'reward',
loadChildren: () =>
import('@isa/checkout/feature/reward-catalog').then((m) => m.routes),
},
{
path: 'return',
loadChildren: () =>

View File

@@ -16,14 +16,16 @@
</a>
<div class="side-menu-group-sub-item-wrapper">
@if (customerSearchRoute$ | async; as customerSearchRoute) {
@if (
customerSearchRoute() || customerCreateRoute() || customerRewardRoute()
) {
<a
class="side-menu-group-item"
(click)="closeSideMenu(); focusSearchBox()"
[routerLink]="customerSearchRoute.path"
[queryParams]="customerSearchRoute.queryParams"
[routerLink]="customerSearchRoute().path"
[queryParams]="customerSearchRoute().queryParams"
sharedRegexRouterLinkActive="active"
sharedRegexRouterLinkActiveTest="^\/kunde\/\d*\/customer"
sharedRegexRouterLinkActiveTest="^(\/kunde\/\d*\/customer|\/\d*\/reward)"
(isActiveChange)="customerActive($event); focusSearchBox()"
>
<span class="side-menu-group-item-icon">
@@ -32,11 +34,11 @@
<span class="side-menu-group-item-label">Kunden</span>
<button
class="side-menu-group-arrow"
[class.side-menu-item-rotate]="customerExpanded"
[class.side-menu-item-rotate]="customerExpanded()"
(click)="
$event.stopPropagation();
$event.preventDefault();
customerExpanded = !customerExpanded
toggleCustomerExpanded()
"
>
<shared-icon icon="keyboard-arrow-down"></shared-icon>
@@ -44,13 +46,16 @@
</a>
}
<div class="side-menu-group-sub-items" [class.hidden]="!customerExpanded">
@if (customerSearchRoute$ | async; as customerSearchRoute) {
<div
class="side-menu-group-sub-items"
[class.hidden]="!customerExpanded()"
>
@if (customerSearchRoute() || customerRewardRoute()) {
<a
class="side-menu-group-item"
(click)="closeSideMenu(); focusSearchBox()"
[routerLink]="customerSearchRoute.path"
[queryParams]="customerSearchRoute.queryParams"
[routerLink]="customerSearchRoute().path"
[queryParams]="customerSearchRoute().queryParams"
sharedRegexRouterLinkActive="active"
sharedRegexRouterLinkActiveTest="^\/kunde\/\d*\/customer\/(\(search|search)"
(isActiveChange)="focusSearchBox()"
@@ -59,12 +64,12 @@
<span class="side-menu-group-item-label">Suchen</span>
</a>
}
@if (customerCreateRoute$ | async; as customerCreateRoute) {
@if (customerCreateRoute() || customerRewardRoute()) {
<a
class="side-menu-group-item"
(click)="closeSideMenu()"
[routerLink]="customerCreateRoute.path"
[queryParams]="customerCreateRoute.queryParams"
[routerLink]="customerCreateRoute().path"
[queryParams]="customerCreateRoute().queryParams"
sharedRegexRouterLinkActive="active"
sharedRegexRouterLinkActiveTest="^\/kunde\/\d*\/customer\/(\(create|create)"
>
@@ -72,6 +77,19 @@
<span class="side-menu-group-item-label">Erfassen</span>
</a>
}
@if (customerRewardRoute()) {
<a
class="side-menu-group-item"
(click)="closeSideMenu(); focusSearchBox()"
[routerLink]="customerRewardRoute()"
(isActiveChange)="focusSearchBox()"
sharedRegexRouterLinkActive="active"
sharedRegexRouterLinkActiveTest="^\/\d*\/reward"
>
<span class="side-menu-group-item-icon"> </span>
<span class="side-menu-group-item-label">Prämienshop</span>
</a>
}
</div>
</div>
@@ -95,7 +113,7 @@
(click)="closeSideMenu(); focusSearchBox()"
[routerLink]="[
'/',
processService.activatedTab()?.id || processService.nextId(),
tabService.activatedTab()?.id || tabService.nextId(),
'return',
]"
(isActiveChange)="focusSearchBox()"
@@ -289,7 +307,7 @@
(click)="closeSideMenu(); focusSearchBox()"
[routerLink]="[
'/',
processService.activatedTab()?.id || processService.nextId(),
tabService.activatedTab()?.id || tabService.nextId(),
'remission',
]"
(isActiveChange)="focusSearchBox(); remissionExpanded.set($event)"
@@ -319,7 +337,7 @@
(click)="closeSideMenu(); focusSearchBox()"
[routerLink]="[
'/',
processService.activatedTab()?.id || processService.nextId(),
tabService.activatedTab()?.id || tabService.nextId(),
'remission',
]"
(isActiveChange)="focusSearchBox()"
@@ -334,7 +352,7 @@
(click)="closeSideMenu(); focusSearchBox()"
[routerLink]="[
'/',
processService.activatedTab()?.id || processService.nextId(),
tabService.activatedTab()?.id || tabService.nextId(),
'remission',
'return-receipt',
]"

View File

@@ -1,7 +1,7 @@
import {
Component,
ChangeDetectionStrategy,
Inject,
computed,
ChangeDetectorRef,
inject,
DOCUMENT,
@@ -29,6 +29,7 @@ import {
PickUpShelfOutNavigationService,
ProductCatalogNavigationService,
} from '@shared/services/navigation';
import { toSignal } from '@angular/core/rxjs-interop';
import { TabService } from '@isa/core/tabs';
import { NgIconComponent, provideIcons } from '@ng-icons/core';
@@ -68,7 +69,7 @@ export class ShellSideMenuComponent {
#pickUpShelfInNavigation = inject(PickupShelfInNavigationService);
#cdr = inject(ChangeDetectorRef);
#document = inject(DOCUMENT);
processService = inject(TabService);
tabService = inject(TabService);
branchKey$ = this.#stockService.StockCurrentBranch().pipe(
retry(3),
@@ -109,18 +110,32 @@ export class ShellSideMenuComponent {
}),
);
customerSearchRoute$ = this.getLastActivatedCustomerProcessId$().pipe(
map((processId) => {
return this.#customerSearchNavigation.defaultRoute({ processId });
}),
customerSearchRoute = toSignal(
this.getLastActivatedCustomerProcessId$().pipe(
map((processId) => {
return this.#customerSearchNavigation.defaultRoute({ processId });
}),
),
);
customerCreateRoute$ = this.getLastActivatedCustomerProcessId$().pipe(
map((processId) => {
return this.#customerCreateNavigation.defaultRoute({ processId });
}),
customerCreateRoute = toSignal(
this.getLastActivatedCustomerProcessId$().pipe(
map((processId) => {
return this.#customerCreateNavigation.defaultRoute({ processId });
}),
),
);
customerRewardRoute = computed(() => {
const routeName = 'reward';
const tabId = this.tabService.activatedTab()?.id;
return this.#router.createUrlTree([
'/',
tabId || this.tabService.nextId(),
routeName,
]);
});
pickUpShelfOutRoutePath$ = this.getLastActivatedCustomerProcessId$().pipe(
map((processId) => {
if (processId) {
@@ -212,26 +227,25 @@ export class ShellSideMenuComponent {
}
shelfExpanded = false;
customerExpanded = false;
customerExpanded = signal(false);
remissionExpanded = signal(false);
customerActive(isActive: boolean) {
if (isActive) {
this.expandCustomer();
this.customerExpanded.set(true);
}
}
toggleCustomerExpanded() {
this.customerExpanded.set(!this.customerExpanded());
}
shelfActive(isActive: boolean) {
if (isActive) {
this.expandShelf();
}
}
expandCustomer() {
this.customerExpanded = true;
this.#cdr.markForCheck();
}
expandShelf() {
this.shelfExpanded = true;
this.#cdr.markForCheck();

View File

@@ -31,8 +31,9 @@ const PARAMETER_CODEC = new ParameterCodec();
export class BaseService {
constructor(
protected config: CatConfiguration,
protected http: HttpClient,
) {}
protected http: HttpClient
) {
}
private _rootUrl: string = '';
@@ -56,7 +57,7 @@ export class BaseService {
*/
protected newParams(): HttpParams {
return new HttpParams({
encoder: PARAMETER_CODEC,
encoder: PARAMETER_CODEC
});
}
}

View File

@@ -4,6 +4,7 @@
* Auocomplete-Ergebnis
*/
export interface AutocompleteDTO {
/**
* Anzeige / Bezeichner
*/

View File

@@ -5,6 +5,7 @@ import { CatalogType } from './catalog-type';
* Suchabfrage
*/
export interface AutocompleteTokenDTO {
/**
* Katalogbereich
*/
@@ -13,7 +14,7 @@ export interface AutocompleteTokenDTO {
/**
* Filter
*/
filter?: { [key: string]: string };
filter?: {[key: string]: string};
/**
* Eingabe

View File

@@ -7,6 +7,7 @@ import { AvailabilityType } from './availability-type';
* Verfügbarkeit
*/
export interface AvailabilityDTO {
/**
* Voraussichtliches Lieferdatum
*/

View File

@@ -1,2 +1,2 @@
/* tslint:disable */
export type AvailabilityType = 0 | 1 | 2 | 32 | 256 | 512 | 1024 | 2048 | 4096 | 8192 | 16384;
export type AvailabilityType = 0 | 1 | 2 | 32 | 256 | 512 | 1024 | 2048 | 4096 | 8192 | 16384;

View File

@@ -1,2 +1,2 @@
/* tslint:disable */
export type Avoirdupois = 0 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128 | 256 | 512 | 1024 | 2048 | 4096;
export type Avoirdupois = 0 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128 | 256 | 512 | 1024 | 2048 | 4096;

View File

@@ -3,4 +3,4 @@
/**
* Katalogbereich
*/
export type CatalogType = 0 | 1 | 2 | 4;
export type CatalogType = 0 | 1 | 2 | 4;

View File

@@ -1,2 +1,2 @@
/* tslint:disable */
export type CRUDA = 0 | 1 | 2 | 4 | 8 | 16;
export type CRUDA = 0 | 1 | 2 | 4 | 8 | 16;

View File

@@ -1,2 +1,2 @@
/* tslint:disable */
export type DialogContentType = 0 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128;
export type DialogContentType = 0 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128;

View File

@@ -1,2 +1,2 @@
/* tslint:disable */
export type DialogSettings = 0 | 1 | 2 | 4;
export type DialogSettings = 0 | 1 | 2 | 4;

View File

@@ -2,7 +2,7 @@
import { TouchedBase } from './touched-base';
import { CRUDA } from './cruda';
import { EntityStatus } from './entity-status';
export interface EntityDTO extends TouchedBase {
export interface EntityDTO extends TouchedBase{
changed?: string;
created?: string;
cruda?: CRUDA;

View File

@@ -1,2 +1,2 @@
/* tslint:disable */
export type EntityStatus = 0 | 1 | 2 | 4 | 8;
export type EntityStatus = 0 | 1 | 2 | 4 | 8;

View File

@@ -4,6 +4,7 @@
* Bild
*/
export interface ImageDTO {
/**
* Copyright
*/

View File

@@ -1,2 +1,2 @@
/* tslint:disable */
export type InputType = 0 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128 | 256 | 512 | 1024 | 2048 | 3072 | 4096 | 8192 | 12288;
export type InputType = 0 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128 | 256 | 512 | 1024 | 2048 | 3072 | 4096 | 8192 | 12288;

View File

@@ -11,7 +11,8 @@ import { StockInfoDTO } from './stock-info-dto';
import { Successor } from './successor';
import { TextDTO } from './text-dto';
import { ItemType } from './item-type';
export interface ItemDTO extends EntityDTO {
export interface ItemDTO extends EntityDTO{
/**
* Verfügbarkeit laut Katalog
*/
@@ -30,7 +31,7 @@ export interface ItemDTO extends EntityDTO {
/**
* Weitere Artikel-IDs
*/
ids?: { [key: string]: number };
ids?: {[key: string]: number};
/**
* Primary image / Id des Hauptbilds
@@ -45,7 +46,7 @@ export interface ItemDTO extends EntityDTO {
/**
* Markierungen (Lesezeichen) wie (BOD, Prämie)
*/
labels?: { [key: string]: string };
labels?: {[key: string]: string};
/**
* Produkt-Stammdaten

View File

@@ -3,4 +3,4 @@
/**
* Artikel-/Produkttyp
*/
export type ItemType = 0 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128 | 256 | 512 | 1024 | 2048 | 4096 | 8192 | 16384 | 32768 | 65536;
export type ItemType = 0 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128 | 256 | 512 | 1024 | 2048 | 4096 | 8192 | 16384 | 32768 | 65536;

View File

@@ -1,5 +1,6 @@
/* tslint:disable */
export interface LesepunkteRequest {
/**
* Artikel ID
*/

View File

@@ -1,6 +1,6 @@
/* tslint:disable */
import { ResponseArgsOfIEnumerableOfAutocompleteDTO } from './response-args-of-ienumerable-of-autocomplete-dto';
export interface ListResponseArgsOfAutocompleteDTO extends ResponseArgsOfIEnumerableOfAutocompleteDTO {
export interface ListResponseArgsOfAutocompleteDTO extends ResponseArgsOfIEnumerableOfAutocompleteDTO{
completed?: boolean;
hits?: number;
skip?: number;

View File

@@ -1,6 +1,6 @@
/* tslint:disable */
import { ResponseArgsOfIEnumerableOfItemDTO } from './response-args-of-ienumerable-of-item-dto';
export interface ListResponseArgsOfItemDTO extends ResponseArgsOfIEnumerableOfItemDTO {
export interface ListResponseArgsOfItemDTO extends ResponseArgsOfIEnumerableOfItemDTO{
completed?: boolean;
hits?: number;
skip?: number;

View File

@@ -2,7 +2,7 @@
import { TouchedBase } from './touched-base';
import { PriceValueDTO } from './price-value-dto';
import { VATValueDTO } from './vatvalue-dto';
export interface PriceDTO extends TouchedBase {
export interface PriceDTO extends TouchedBase{
value?: PriceValueDTO;
vat?: VATValueDTO;
}

View File

@@ -1,6 +1,6 @@
/* tslint:disable */
import { TouchedBase } from './touched-base';
export interface PriceValueDTO extends TouchedBase {
export interface PriceValueDTO extends TouchedBase{
currency?: string;
currencySymbol?: string;
value?: number;

View File

@@ -1,7 +1,7 @@
/* tslint:disable */
export interface ProblemDetails {
detail?: string;
extensions: { [key: string]: any };
extensions: {[key: string]: any};
instance?: string;
status?: number;
title?: string;

View File

@@ -2,7 +2,7 @@
import { TouchedBase } from './touched-base';
import { SizeOfString } from './size-of-string';
import { WeightOfAvoirdupois } from './weight-of-avoirdupois';
export interface ProductDTO extends TouchedBase {
export interface ProductDTO extends TouchedBase{
additionalName?: string;
catalogProductNumber?: string;
contributors?: string;

View File

@@ -1,7 +1,8 @@
/* tslint:disable */
import { QueryTokenDTO2 } from './query-token-dto2';
import { CatalogType } from './catalog-type';
export interface QueryTokenDTO extends QueryTokenDTO2 {
export interface QueryTokenDTO extends QueryTokenDTO2{
/**
* Katalogbereich
*/

View File

@@ -1,13 +1,13 @@
/* tslint:disable */
import { OrderByDTO } from './order-by-dto';
export interface QueryTokenDTO2 {
filter?: { [key: string]: string };
filter?: {[key: string]: string};
friendlyName?: string;
fuzzy?: number;
hitsOnly?: boolean;
ids?: Array<number>;
input?: { [key: string]: string };
options?: { [key: string]: string };
input?: {[key: string]: string};
options?: {[key: string]: string};
orderBy?: Array<OrderByDTO>;
skip?: number;
take?: number;

View File

@@ -1,5 +1,5 @@
/* tslint:disable */
import { ResponseArgs } from './response-args';
export interface ResponseArgsOfIDictionaryOfLongAndNullableInteger extends ResponseArgs {
result?: { [key: string]: number };
export interface ResponseArgsOfIDictionaryOfLongAndNullableInteger extends ResponseArgs{
result?: {[key: string]: number};
}

View File

@@ -1,6 +1,6 @@
/* tslint:disable */
import { ResponseArgs } from './response-args';
import { AutocompleteDTO } from './autocomplete-dto';
export interface ResponseArgsOfIEnumerableOfAutocompleteDTO extends ResponseArgs {
export interface ResponseArgsOfIEnumerableOfAutocompleteDTO extends ResponseArgs{
result?: Array<AutocompleteDTO>;
}

View File

@@ -1,6 +1,6 @@
/* tslint:disable */
import { ResponseArgs } from './response-args';
import { InputGroupDTO } from './input-group-dto';
export interface ResponseArgsOfIEnumerableOfInputGroupDTO extends ResponseArgs {
export interface ResponseArgsOfIEnumerableOfInputGroupDTO extends ResponseArgs{
result?: Array<InputGroupDTO>;
}

View File

@@ -1,6 +1,6 @@
/* tslint:disable */
import { ResponseArgs } from './response-args';
import { ItemDTO } from './item-dto';
export interface ResponseArgsOfIEnumerableOfItemDTO extends ResponseArgs {
export interface ResponseArgsOfIEnumerableOfItemDTO extends ResponseArgs{
result?: Array<ItemDTO>;
}

View File

@@ -1,6 +1,6 @@
/* tslint:disable */
import { ResponseArgs } from './response-args';
import { OrderByDTO } from './order-by-dto';
export interface ResponseArgsOfIEnumerableOfOrderByDTO extends ResponseArgs {
export interface ResponseArgsOfIEnumerableOfOrderByDTO extends ResponseArgs{
result?: Array<OrderByDTO>;
}

View File

@@ -1,6 +1,6 @@
/* tslint:disable */
import { ResponseArgs } from './response-args';
import { QueryTokenDTO } from './query-token-dto';
export interface ResponseArgsOfIEnumerableOfQueryTokenDTO extends ResponseArgs {
export interface ResponseArgsOfIEnumerableOfQueryTokenDTO extends ResponseArgs{
result?: Array<QueryTokenDTO>;
}

View File

@@ -1,6 +1,6 @@
/* tslint:disable */
import { ResponseArgs } from './response-args';
import { ItemDTO } from './item-dto';
export interface ResponseArgsOfItemDTO extends ResponseArgs {
export interface ResponseArgsOfItemDTO extends ResponseArgs{
result?: ItemDTO;
}

View File

@@ -1,6 +1,6 @@
/* tslint:disable */
import { ResponseArgs } from './response-args';
import { UISettingsDTO } from './uisettings-dto';
export interface ResponseArgsOfUISettingsDTO extends ResponseArgs {
export interface ResponseArgsOfUISettingsDTO extends ResponseArgs{
result?: UISettingsDTO;
}

View File

@@ -3,7 +3,7 @@ import { DialogOfString } from './dialog-of-string';
export interface ResponseArgs {
dialog?: DialogOfString;
error: boolean;
invalidProperties?: { [key: string]: string };
invalidProperties?: {[key: string]: string};
message?: string;
requestId?: number;
}

View File

@@ -1,5 +1,6 @@
/* tslint:disable */
export interface ReviewDTO {
/**
* Autor
*/

View File

@@ -4,6 +4,7 @@
* Regalinfo
*/
export interface ShelfInfoDTO {
/**
* Sortiment
*/

View File

@@ -3,4 +3,5 @@
/**
* Shop
*/
export interface ShopDTO {}
export interface ShopDTO {
}

View File

@@ -4,6 +4,7 @@
* Eigenchaften
*/
export interface SpecDTO {
/**
* PK
*/

View File

@@ -5,6 +5,7 @@ import { StockStatus } from './stock-status';
* Bestandsinformation
*/
export interface StockInfoDTO {
/**
* Filiale PK
*/

View File

@@ -3,4 +3,4 @@
/**
* Dispositionsstatus
*/
export type StockStatus = 0 | 1 | 2 | 3 | 4;
export type StockStatus = 0 | 1 | 2 | 3 | 4;

View File

@@ -1,6 +1,7 @@
/* tslint:disable */
import { ProductDTO } from './product-dto';
export interface Successor extends ProductDTO {
export interface Successor extends ProductDTO{
/**
* PK
*/

View File

@@ -1,5 +1,6 @@
/* tslint:disable */
export interface TextDTO {
/**
* PK
*/

View File

@@ -1,2 +1,3 @@
/* tslint:disable */
export interface TouchedBase {}
export interface TouchedBase {
}

View File

@@ -1,5 +1,5 @@
/* tslint:disable */
export interface TranslationDTO {
target?: string;
values?: { [key: string]: string };
values?: {[key: string]: string};
}

View File

@@ -1,7 +1,8 @@
/* tslint:disable */
import { QuerySettingsDTO } from './query-settings-dto';
import { TranslationDTO } from './translation-dto';
export interface UISettingsDTO extends QuerySettingsDTO {
export interface UISettingsDTO extends QuerySettingsDTO{
/**
* Url Template für Detail-Bild
*/

View File

@@ -1,2 +1,2 @@
/* tslint:disable */
export type VATType = 0 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128;
export type VATType = 0 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128;

View File

@@ -1,7 +1,7 @@
/* tslint:disable */
import { TouchedBase } from './touched-base';
import { VATType } from './vattype';
export interface VATValueDTO extends TouchedBase {
export interface VATValueDTO extends TouchedBase{
inPercent?: number;
label?: string;
value?: number;

View File

@@ -16,7 +16,10 @@ class PromotionService extends __BaseService {
static readonly PromotionLesepunktePath = '/promotion/lesepunkte';
static readonly PromotionLesepunkte2Path = '/stock/{stockId}/promotion/lesepunkte';
constructor(config: __Configuration, http: HttpClient) {
constructor(
config: __Configuration,
http: HttpClient
) {
super(config, http);
}
@@ -24,24 +27,26 @@ class PromotionService extends __BaseService {
* Lesepunkte
* @param items Ids und Mengen
*/
PromotionLesepunkteResponse(
items: Array<LesepunkteRequest>,
): __Observable<__StrictHttpResponse<ResponseArgsOfIDictionaryOfLongAndNullableInteger>> {
PromotionLesepunkteResponse(items: Array<LesepunkteRequest>): __Observable<__StrictHttpResponse<ResponseArgsOfIDictionaryOfLongAndNullableInteger>> {
let __params = this.newParams();
let __headers = new HttpHeaders();
let __body: any = null;
__body = items;
let req = new HttpRequest<any>('POST', this.rootUrl + `/promotion/lesepunkte`, __body, {
headers: __headers,
params: __params,
responseType: 'json',
});
let req = new HttpRequest<any>(
'POST',
this.rootUrl + `/promotion/lesepunkte`,
__body,
{
headers: __headers,
params: __params,
responseType: 'json'
});
return this.http.request<any>(req).pipe(
__filter((_r) => _r instanceof HttpResponse),
__filter(_r => _r instanceof HttpResponse),
__map((_r) => {
return _r as __StrictHttpResponse<ResponseArgsOfIDictionaryOfLongAndNullableInteger>;
}),
})
);
}
/**
@@ -49,7 +54,9 @@ class PromotionService extends __BaseService {
* @param items Ids und Mengen
*/
PromotionLesepunkte(items: Array<LesepunkteRequest>): __Observable<ResponseArgsOfIDictionaryOfLongAndNullableInteger> {
return this.PromotionLesepunkteResponse(items).pipe(__map((_r) => _r.body as ResponseArgsOfIDictionaryOfLongAndNullableInteger));
return this.PromotionLesepunkteResponse(items).pipe(
__map(_r => _r.body as ResponseArgsOfIDictionaryOfLongAndNullableInteger)
);
}
/**
@@ -60,9 +67,7 @@ class PromotionService extends __BaseService {
*
* - `items`: Ids und Mengen
*/
PromotionLesepunkte2Response(
params: PromotionService.PromotionLesepunkte2Params,
): __Observable<__StrictHttpResponse<ResponseArgsOfIDictionaryOfLongAndNullableInteger>> {
PromotionLesepunkte2Response(params: PromotionService.PromotionLesepunkte2Params): __Observable<__StrictHttpResponse<ResponseArgsOfIDictionaryOfLongAndNullableInteger>> {
let __params = this.newParams();
let __headers = new HttpHeaders();
let __body: any = null;
@@ -75,15 +80,14 @@ class PromotionService extends __BaseService {
{
headers: __headers,
params: __params,
responseType: 'json',
},
);
responseType: 'json'
});
return this.http.request<any>(req).pipe(
__filter((_r) => _r instanceof HttpResponse),
__filter(_r => _r instanceof HttpResponse),
__map((_r) => {
return _r as __StrictHttpResponse<ResponseArgsOfIDictionaryOfLongAndNullableInteger>;
}),
})
);
}
/**
@@ -94,18 +98,20 @@ class PromotionService extends __BaseService {
*
* - `items`: Ids und Mengen
*/
PromotionLesepunkte2(
params: PromotionService.PromotionLesepunkte2Params,
): __Observable<ResponseArgsOfIDictionaryOfLongAndNullableInteger> {
return this.PromotionLesepunkte2Response(params).pipe(__map((_r) => _r.body as ResponseArgsOfIDictionaryOfLongAndNullableInteger));
PromotionLesepunkte2(params: PromotionService.PromotionLesepunkte2Params): __Observable<ResponseArgsOfIDictionaryOfLongAndNullableInteger> {
return this.PromotionLesepunkte2Response(params).pipe(
__map(_r => _r.body as ResponseArgsOfIDictionaryOfLongAndNullableInteger)
);
}
}
module PromotionService {
/**
* Parameters for PromotionLesepunkte2
*/
export interface PromotionLesepunkte2Params {
/**
* Lager PK (optional)
*/
@@ -118,4 +124,4 @@ module PromotionService {
}
}
export { PromotionService };
export { PromotionService }

View File

@@ -37,12 +37,16 @@ class SearchService extends __BaseService {
static readonly SearchDetailByEANPath = '/s/ean/{ean}';
static readonly SearchDetailByEAN2Path = '/stock/{stockId}/ean/{ean}';
static readonly SearchSettingsPath = '/s/settings';
static readonly SearchLoyaltySettingsPath = '/s/loyalty/settings';
static readonly SearchSearchFilterPath = '/s/filter';
static readonly SearchSearchSortPath = '/s/sort';
static readonly SearchHistoryPath = '/s/history';
static readonly SearchGetRecommendationsPath = '/s/recommendations/{digId}';
constructor(config: __Configuration, http: HttpClient) {
constructor(
config: __Configuration,
http: HttpClient
) {
super(config, http);
}
@@ -55,17 +59,21 @@ class SearchService extends __BaseService {
let __headers = new HttpHeaders();
let __body: any = null;
__body = queryToken;
let req = new HttpRequest<any>('POST', this.rootUrl + `/s/top`, __body, {
headers: __headers,
params: __params,
responseType: 'json',
});
let req = new HttpRequest<any>(
'POST',
this.rootUrl + `/s/top`,
__body,
{
headers: __headers,
params: __params,
responseType: 'json'
});
return this.http.request<any>(req).pipe(
__filter((_r) => _r instanceof HttpResponse),
__filter(_r => _r instanceof HttpResponse),
__map((_r) => {
return _r as __StrictHttpResponse<ListResponseArgsOfItemDTO>;
}),
})
);
}
/**
@@ -73,7 +81,9 @@ class SearchService extends __BaseService {
* @param queryToken Suchkriterien
*/
SearchTop(queryToken: QueryTokenDTO): __Observable<ListResponseArgsOfItemDTO> {
return this.SearchTopResponse(queryToken).pipe(__map((_r) => _r.body as ListResponseArgsOfItemDTO));
return this.SearchTopResponse(queryToken).pipe(
__map(_r => _r.body as ListResponseArgsOfItemDTO)
);
}
/**
@@ -90,17 +100,21 @@ class SearchService extends __BaseService {
let __body: any = null;
__body = params.queryToken;
let req = new HttpRequest<any>('POST', this.rootUrl + `/stock/${encodeURIComponent(String(params.stockId))}/s/top`, __body, {
headers: __headers,
params: __params,
responseType: 'json',
});
let req = new HttpRequest<any>(
'POST',
this.rootUrl + `/stock/${encodeURIComponent(String(params.stockId))}/s/top`,
__body,
{
headers: __headers,
params: __params,
responseType: 'json'
});
return this.http.request<any>(req).pipe(
__filter((_r) => _r instanceof HttpResponse),
__filter(_r => _r instanceof HttpResponse),
__map((_r) => {
return _r as __StrictHttpResponse<ListResponseArgsOfItemDTO>;
}),
})
);
}
/**
@@ -112,7 +126,9 @@ class SearchService extends __BaseService {
* - `queryToken`: Suchkriterien
*/
SearchTop2(params: SearchService.SearchTop2Params): __Observable<ListResponseArgsOfItemDTO> {
return this.SearchTop2Response(params).pipe(__map((_r) => _r.body as ListResponseArgsOfItemDTO));
return this.SearchTop2Response(params).pipe(
__map(_r => _r.body as ListResponseArgsOfItemDTO)
);
}
/**
@@ -124,17 +140,21 @@ class SearchService extends __BaseService {
let __headers = new HttpHeaders();
let __body: any = null;
__body = queryToken;
let req = new HttpRequest<any>('POST', this.rootUrl + `/s`, __body, {
headers: __headers,
params: __params,
responseType: 'json',
});
let req = new HttpRequest<any>(
'POST',
this.rootUrl + `/s`,
__body,
{
headers: __headers,
params: __params,
responseType: 'json'
});
return this.http.request<any>(req).pipe(
__filter((_r) => _r instanceof HttpResponse),
__filter(_r => _r instanceof HttpResponse),
__map((_r) => {
return _r as __StrictHttpResponse<ListResponseArgsOfItemDTO>;
}),
})
);
}
/**
@@ -142,7 +162,9 @@ class SearchService extends __BaseService {
* @param queryToken Suchkriterien
*/
SearchSearch(queryToken: QueryTokenDTO): __Observable<ListResponseArgsOfItemDTO> {
return this.SearchSearchResponse(queryToken).pipe(__map((_r) => _r.body as ListResponseArgsOfItemDTO));
return this.SearchSearchResponse(queryToken).pipe(
__map(_r => _r.body as ListResponseArgsOfItemDTO)
);
}
/**
@@ -159,17 +181,21 @@ class SearchService extends __BaseService {
let __body: any = null;
__body = params.queryToken;
let req = new HttpRequest<any>('POST', this.rootUrl + `/stock/${encodeURIComponent(String(params.stockId))}/s`, __body, {
headers: __headers,
params: __params,
responseType: 'json',
});
let req = new HttpRequest<any>(
'POST',
this.rootUrl + `/stock/${encodeURIComponent(String(params.stockId))}/s`,
__body,
{
headers: __headers,
params: __params,
responseType: 'json'
});
return this.http.request<any>(req).pipe(
__filter((_r) => _r instanceof HttpResponse),
__filter(_r => _r instanceof HttpResponse),
__map((_r) => {
return _r as __StrictHttpResponse<ListResponseArgsOfItemDTO>;
}),
})
);
}
/**
@@ -181,7 +207,9 @@ class SearchService extends __BaseService {
* - `queryToken`: Suchkriterien
*/
SearchSearch2(params: SearchService.SearchSearch2Params): __Observable<ListResponseArgsOfItemDTO> {
return this.SearchSearch2Response(params).pipe(__map((_r) => _r.body as ListResponseArgsOfItemDTO));
return this.SearchSearch2Response(params).pipe(
__map(_r => _r.body as ListResponseArgsOfItemDTO)
);
}
/**
@@ -193,17 +221,21 @@ class SearchService extends __BaseService {
let __headers = new HttpHeaders();
let __body: any = null;
__body = queryToken;
let req = new HttpRequest<any>('POST', this.rootUrl + `/s/complete`, __body, {
headers: __headers,
params: __params,
responseType: 'json',
});
let req = new HttpRequest<any>(
'POST',
this.rootUrl + `/s/complete`,
__body,
{
headers: __headers,
params: __params,
responseType: 'json'
});
return this.http.request<any>(req).pipe(
__filter((_r) => _r instanceof HttpResponse),
__filter(_r => _r instanceof HttpResponse),
__map((_r) => {
return _r as __StrictHttpResponse<ListResponseArgsOfAutocompleteDTO>;
}),
})
);
}
/**
@@ -211,7 +243,9 @@ class SearchService extends __BaseService {
* @param queryToken Suchbegriff
*/
SearchAutocomplete(queryToken: AutocompleteTokenDTO): __Observable<ListResponseArgsOfAutocompleteDTO> {
return this.SearchAutocompleteResponse(queryToken).pipe(__map((_r) => _r.body as ListResponseArgsOfAutocompleteDTO));
return this.SearchAutocompleteResponse(queryToken).pipe(
__map(_r => _r.body as ListResponseArgsOfAutocompleteDTO)
);
}
/**
@@ -222,25 +256,27 @@ class SearchService extends __BaseService {
*
* - `queryToken`: Suchbegriff
*/
SearchAutocomplete2Response(
params: SearchService.SearchAutocomplete2Params,
): __Observable<__StrictHttpResponse<ListResponseArgsOfAutocompleteDTO>> {
SearchAutocomplete2Response(params: SearchService.SearchAutocomplete2Params): __Observable<__StrictHttpResponse<ListResponseArgsOfAutocompleteDTO>> {
let __params = this.newParams();
let __headers = new HttpHeaders();
let __body: any = null;
__body = params.queryToken;
let req = new HttpRequest<any>('POST', this.rootUrl + `/stock/${encodeURIComponent(String(params.stockId))}/s/complete`, __body, {
headers: __headers,
params: __params,
responseType: 'json',
});
let req = new HttpRequest<any>(
'POST',
this.rootUrl + `/stock/${encodeURIComponent(String(params.stockId))}/s/complete`,
__body,
{
headers: __headers,
params: __params,
responseType: 'json'
});
return this.http.request<any>(req).pipe(
__filter((_r) => _r instanceof HttpResponse),
__filter(_r => _r instanceof HttpResponse),
__map((_r) => {
return _r as __StrictHttpResponse<ListResponseArgsOfAutocompleteDTO>;
}),
})
);
}
/**
@@ -252,7 +288,9 @@ class SearchService extends __BaseService {
* - `queryToken`: Suchbegriff
*/
SearchAutocomplete2(params: SearchService.SearchAutocomplete2Params): __Observable<ListResponseArgsOfAutocompleteDTO> {
return this.SearchAutocomplete2Response(params).pipe(__map((_r) => _r.body as ListResponseArgsOfAutocompleteDTO));
return this.SearchAutocomplete2Response(params).pipe(
__map(_r => _r.body as ListResponseArgsOfAutocompleteDTO)
);
}
/**
@@ -264,17 +302,21 @@ class SearchService extends __BaseService {
let __headers = new HttpHeaders();
let __body: any = null;
__body = ids;
let req = new HttpRequest<any>('POST', this.rootUrl + `/s/byid`, __body, {
headers: __headers,
params: __params,
responseType: 'json',
});
let req = new HttpRequest<any>(
'POST',
this.rootUrl + `/s/byid`,
__body,
{
headers: __headers,
params: __params,
responseType: 'json'
});
return this.http.request<any>(req).pipe(
__filter((_r) => _r instanceof HttpResponse),
__filter(_r => _r instanceof HttpResponse),
__map((_r) => {
return _r as __StrictHttpResponse<ListResponseArgsOfItemDTO>;
}),
})
);
}
/**
@@ -282,7 +324,9 @@ class SearchService extends __BaseService {
* @param ids PKs
*/
SearchById(ids: Array<number>): __Observable<ListResponseArgsOfItemDTO> {
return this.SearchByIdResponse(ids).pipe(__map((_r) => _r.body as ListResponseArgsOfItemDTO));
return this.SearchByIdResponse(ids).pipe(
__map(_r => _r.body as ListResponseArgsOfItemDTO)
);
}
/**
@@ -299,17 +343,21 @@ class SearchService extends __BaseService {
let __body: any = null;
__body = params.ids;
let req = new HttpRequest<any>('POST', this.rootUrl + `/stock/${encodeURIComponent(String(params.stockId))}/s/byid`, __body, {
headers: __headers,
params: __params,
responseType: 'json',
});
let req = new HttpRequest<any>(
'POST',
this.rootUrl + `/stock/${encodeURIComponent(String(params.stockId))}/s/byid`,
__body,
{
headers: __headers,
params: __params,
responseType: 'json'
});
return this.http.request<any>(req).pipe(
__filter((_r) => _r instanceof HttpResponse),
__filter(_r => _r instanceof HttpResponse),
__map((_r) => {
return _r as __StrictHttpResponse<ListResponseArgsOfItemDTO>;
}),
})
);
}
/**
@@ -321,7 +369,9 @@ class SearchService extends __BaseService {
* - `ids`: PKs
*/
SearchById2(params: SearchService.SearchById2Params): __Observable<ListResponseArgsOfItemDTO> {
return this.SearchById2Response(params).pipe(__map((_r) => _r.body as ListResponseArgsOfItemDTO));
return this.SearchById2Response(params).pipe(
__map(_r => _r.body as ListResponseArgsOfItemDTO)
);
}
/**
@@ -333,17 +383,21 @@ class SearchService extends __BaseService {
let __headers = new HttpHeaders();
let __body: any = null;
__body = eans;
let req = new HttpRequest<any>('POST', this.rootUrl + `/s/byean`, __body, {
headers: __headers,
params: __params,
responseType: 'json',
});
let req = new HttpRequest<any>(
'POST',
this.rootUrl + `/s/byean`,
__body,
{
headers: __headers,
params: __params,
responseType: 'json'
});
return this.http.request<any>(req).pipe(
__filter((_r) => _r instanceof HttpResponse),
__filter(_r => _r instanceof HttpResponse),
__map((_r) => {
return _r as __StrictHttpResponse<ListResponseArgsOfItemDTO>;
}),
})
);
}
/**
@@ -351,7 +405,9 @@ class SearchService extends __BaseService {
* @param eans EANs
*/
SearchByEAN(eans: Array<string>): __Observable<ListResponseArgsOfItemDTO> {
return this.SearchByEANResponse(eans).pipe(__map((_r) => _r.body as ListResponseArgsOfItemDTO));
return this.SearchByEANResponse(eans).pipe(
__map(_r => _r.body as ListResponseArgsOfItemDTO)
);
}
/**
@@ -368,17 +424,21 @@ class SearchService extends __BaseService {
let __body: any = null;
__body = params.eans;
let req = new HttpRequest<any>('POST', this.rootUrl + `/stock/${encodeURIComponent(String(params.stockId))}/s/byean`, __body, {
headers: __headers,
params: __params,
responseType: 'json',
});
let req = new HttpRequest<any>(
'POST',
this.rootUrl + `/stock/${encodeURIComponent(String(params.stockId))}/s/byean`,
__body,
{
headers: __headers,
params: __params,
responseType: 'json'
});
return this.http.request<any>(req).pipe(
__filter((_r) => _r instanceof HttpResponse),
__filter(_r => _r instanceof HttpResponse),
__map((_r) => {
return _r as __StrictHttpResponse<ListResponseArgsOfItemDTO>;
}),
})
);
}
/**
@@ -390,7 +450,9 @@ class SearchService extends __BaseService {
* - `eans`: EANs
*/
SearchByEAN2(params: SearchService.SearchByEAN2Params): __Observable<ListResponseArgsOfItemDTO> {
return this.SearchByEAN2Response(params).pipe(__map((_r) => _r.body as ListResponseArgsOfItemDTO));
return this.SearchByEAN2Response(params).pipe(
__map(_r => _r.body as ListResponseArgsOfItemDTO)
);
}
/**
@@ -407,17 +469,21 @@ class SearchService extends __BaseService {
let __body: any = null;
__body = params.eans;
let req = new HttpRequest<any>('POST', this.rootUrl + `/branch/${encodeURIComponent(String(params.branchNumber))}/s/byean`, __body, {
headers: __headers,
params: __params,
responseType: 'json',
});
let req = new HttpRequest<any>(
'POST',
this.rootUrl + `/branch/${encodeURIComponent(String(params.branchNumber))}/s/byean`,
__body,
{
headers: __headers,
params: __params,
responseType: 'json'
});
return this.http.request<any>(req).pipe(
__filter((_r) => _r instanceof HttpResponse),
__filter(_r => _r instanceof HttpResponse),
__map((_r) => {
return _r as __StrictHttpResponse<ListResponseArgsOfItemDTO>;
}),
})
);
}
/**
@@ -429,7 +495,9 @@ class SearchService extends __BaseService {
* - `branchNumber`: Filiale-Nr (optional)
*/
SearchByEAN3(params: SearchService.SearchByEAN3Params): __Observable<ListResponseArgsOfItemDTO> {
return this.SearchByEAN3Response(params).pipe(__map((_r) => _r.body as ListResponseArgsOfItemDTO));
return this.SearchByEAN3Response(params).pipe(
__map(_r => _r.body as ListResponseArgsOfItemDTO)
);
}
/**
@@ -446,17 +514,21 @@ class SearchService extends __BaseService {
let __body: any = null;
if (params.doNotTrack != null) __params = __params.set('doNotTrack', params.doNotTrack.toString());
let req = new HttpRequest<any>('GET', this.rootUrl + `/s/${encodeURIComponent(String(params.id))}`, __body, {
headers: __headers,
params: __params,
responseType: 'json',
});
let req = new HttpRequest<any>(
'GET',
this.rootUrl + `/s/${encodeURIComponent(String(params.id))}`,
__body,
{
headers: __headers,
params: __params,
responseType: 'json'
});
return this.http.request<any>(req).pipe(
__filter((_r) => _r instanceof HttpResponse),
__filter(_r => _r instanceof HttpResponse),
__map((_r) => {
return _r as __StrictHttpResponse<ResponseArgsOfItemDTO>;
}),
})
);
}
/**
@@ -468,7 +540,9 @@ class SearchService extends __BaseService {
* - `doNotTrack`:
*/
SearchDetail(params: SearchService.SearchDetailParams): __Observable<ResponseArgsOfItemDTO> {
return this.SearchDetailResponse(params).pipe(__map((_r) => _r.body as ResponseArgsOfItemDTO));
return this.SearchDetailResponse(params).pipe(
__map(_r => _r.body as ResponseArgsOfItemDTO)
);
}
/**
@@ -486,6 +560,7 @@ class SearchService extends __BaseService {
let __headers = new HttpHeaders();
let __body: any = null;
if (params.doNotTrack != null) __params = __params.set('doNotTrack', params.doNotTrack.toString());
let req = new HttpRequest<any>(
'GET',
@@ -494,15 +569,14 @@ class SearchService extends __BaseService {
{
headers: __headers,
params: __params,
responseType: 'json',
},
);
responseType: 'json'
});
return this.http.request<any>(req).pipe(
__filter((_r) => _r instanceof HttpResponse),
__filter(_r => _r instanceof HttpResponse),
__map((_r) => {
return _r as __StrictHttpResponse<ResponseArgsOfItemDTO>;
}),
})
);
}
/**
@@ -516,7 +590,9 @@ class SearchService extends __BaseService {
* - `doNotTrack`:
*/
SearchDetail2(params: SearchService.SearchDetail2Params): __Observable<ResponseArgsOfItemDTO> {
return this.SearchDetail2Response(params).pipe(__map((_r) => _r.body as ResponseArgsOfItemDTO));
return this.SearchDetail2Response(params).pipe(
__map(_r => _r.body as ResponseArgsOfItemDTO)
);
}
/**
@@ -528,17 +604,21 @@ class SearchService extends __BaseService {
let __headers = new HttpHeaders();
let __body: any = null;
let req = new HttpRequest<any>('GET', this.rootUrl + `/s/ean/${encodeURIComponent(String(ean))}`, __body, {
headers: __headers,
params: __params,
responseType: 'json',
});
let req = new HttpRequest<any>(
'GET',
this.rootUrl + `/s/ean/${encodeURIComponent(String(ean))}`,
__body,
{
headers: __headers,
params: __params,
responseType: 'json'
});
return this.http.request<any>(req).pipe(
__filter((_r) => _r instanceof HttpResponse),
__filter(_r => _r instanceof HttpResponse),
__map((_r) => {
return _r as __StrictHttpResponse<ResponseArgsOfItemDTO>;
}),
})
);
}
/**
@@ -546,7 +626,9 @@ class SearchService extends __BaseService {
* @param ean EAN
*/
SearchDetailByEAN(ean: string): __Observable<ResponseArgsOfItemDTO> {
return this.SearchDetailByEANResponse(ean).pipe(__map((_r) => _r.body as ResponseArgsOfItemDTO));
return this.SearchDetailByEANResponse(ean).pipe(
__map(_r => _r.body as ResponseArgsOfItemDTO)
);
}
/**
@@ -562,6 +644,7 @@ class SearchService extends __BaseService {
let __headers = new HttpHeaders();
let __body: any = null;
let req = new HttpRequest<any>(
'GET',
this.rootUrl + `/stock/${encodeURIComponent(String(params.stockId))}/ean/${encodeURIComponent(String(params.ean))}`,
@@ -569,15 +652,14 @@ class SearchService extends __BaseService {
{
headers: __headers,
params: __params,
responseType: 'json',
},
);
responseType: 'json'
});
return this.http.request<any>(req).pipe(
__filter((_r) => _r instanceof HttpResponse),
__filter(_r => _r instanceof HttpResponse),
__map((_r) => {
return _r as __StrictHttpResponse<ResponseArgsOfItemDTO>;
}),
})
);
}
/**
@@ -589,7 +671,9 @@ class SearchService extends __BaseService {
* - `ean`: EAN
*/
SearchDetailByEAN2(params: SearchService.SearchDetailByEAN2Params): __Observable<ResponseArgsOfItemDTO> {
return this.SearchDetailByEAN2Response(params).pipe(__map((_r) => _r.body as ResponseArgsOfItemDTO));
return this.SearchDetailByEAN2Response(params).pipe(
__map(_r => _r.body as ResponseArgsOfItemDTO)
);
}
/**
@@ -599,24 +683,63 @@ class SearchService extends __BaseService {
let __params = this.newParams();
let __headers = new HttpHeaders();
let __body: any = null;
let req = new HttpRequest<any>('GET', this.rootUrl + `/s/settings`, __body, {
headers: __headers,
params: __params,
responseType: 'json',
});
let req = new HttpRequest<any>(
'GET',
this.rootUrl + `/s/settings`,
__body,
{
headers: __headers,
params: __params,
responseType: 'json'
});
return this.http.request<any>(req).pipe(
__filter((_r) => _r instanceof HttpResponse),
__filter(_r => _r instanceof HttpResponse),
__map((_r) => {
return _r as __StrictHttpResponse<ResponseArgsOfUISettingsDTO>;
}),
})
);
}
/**
* Settings
*/
SearchSettings(): __Observable<ResponseArgsOfUISettingsDTO> {
return this.SearchSettingsResponse().pipe(__map((_r) => _r.body as ResponseArgsOfUISettingsDTO));
return this.SearchSettingsResponse().pipe(
__map(_r => _r.body as ResponseArgsOfUISettingsDTO)
);
}
/**
* Settings
*/
SearchLoyaltySettingsResponse(): __Observable<__StrictHttpResponse<ResponseArgsOfUISettingsDTO>> {
let __params = this.newParams();
let __headers = new HttpHeaders();
let __body: any = null;
let req = new HttpRequest<any>(
'GET',
this.rootUrl + `/s/loyalty/settings`,
__body,
{
headers: __headers,
params: __params,
responseType: 'json'
});
return this.http.request<any>(req).pipe(
__filter(_r => _r instanceof HttpResponse),
__map((_r) => {
return _r as __StrictHttpResponse<ResponseArgsOfUISettingsDTO>;
})
);
}
/**
* Settings
*/
SearchLoyaltySettings(): __Observable<ResponseArgsOfUISettingsDTO> {
return this.SearchLoyaltySettingsResponse().pipe(
__map(_r => _r.body as ResponseArgsOfUISettingsDTO)
);
}
/**
@@ -626,24 +749,30 @@ class SearchService extends __BaseService {
let __params = this.newParams();
let __headers = new HttpHeaders();
let __body: any = null;
let req = new HttpRequest<any>('GET', this.rootUrl + `/s/filter`, __body, {
headers: __headers,
params: __params,
responseType: 'json',
});
let req = new HttpRequest<any>(
'GET',
this.rootUrl + `/s/filter`,
__body,
{
headers: __headers,
params: __params,
responseType: 'json'
});
return this.http.request<any>(req).pipe(
__filter((_r) => _r instanceof HttpResponse),
__filter(_r => _r instanceof HttpResponse),
__map((_r) => {
return _r as __StrictHttpResponse<ResponseArgsOfIEnumerableOfInputGroupDTO>;
}),
})
);
}
/**
* Filter
*/
SearchSearchFilter(): __Observable<ResponseArgsOfIEnumerableOfInputGroupDTO> {
return this.SearchSearchFilterResponse().pipe(__map((_r) => _r.body as ResponseArgsOfIEnumerableOfInputGroupDTO));
return this.SearchSearchFilterResponse().pipe(
__map(_r => _r.body as ResponseArgsOfIEnumerableOfInputGroupDTO)
);
}
/**
@@ -653,24 +782,30 @@ class SearchService extends __BaseService {
let __params = this.newParams();
let __headers = new HttpHeaders();
let __body: any = null;
let req = new HttpRequest<any>('GET', this.rootUrl + `/s/sort`, __body, {
headers: __headers,
params: __params,
responseType: 'json',
});
let req = new HttpRequest<any>(
'GET',
this.rootUrl + `/s/sort`,
__body,
{
headers: __headers,
params: __params,
responseType: 'json'
});
return this.http.request<any>(req).pipe(
__filter((_r) => _r instanceof HttpResponse),
__filter(_r => _r instanceof HttpResponse),
__map((_r) => {
return _r as __StrictHttpResponse<ResponseArgsOfIEnumerableOfOrderByDTO>;
}),
})
);
}
/**
* Filter
*/
SearchSearchSort(): __Observable<ResponseArgsOfIEnumerableOfOrderByDTO> {
return this.SearchSearchSortResponse().pipe(__map((_r) => _r.body as ResponseArgsOfIEnumerableOfOrderByDTO));
return this.SearchSearchSortResponse().pipe(
__map(_r => _r.body as ResponseArgsOfIEnumerableOfOrderByDTO)
);
}
/**
@@ -682,17 +817,21 @@ class SearchService extends __BaseService {
let __headers = new HttpHeaders();
let __body: any = null;
if (take != null) __params = __params.set('take', take.toString());
let req = new HttpRequest<any>('GET', this.rootUrl + `/s/history`, __body, {
headers: __headers,
params: __params,
responseType: 'json',
});
let req = new HttpRequest<any>(
'GET',
this.rootUrl + `/s/history`,
__body,
{
headers: __headers,
params: __params,
responseType: 'json'
});
return this.http.request<any>(req).pipe(
__filter((_r) => _r instanceof HttpResponse),
__filter(_r => _r instanceof HttpResponse),
__map((_r) => {
return _r as __StrictHttpResponse<ResponseArgsOfIEnumerableOfQueryTokenDTO>;
}),
})
);
}
/**
@@ -700,7 +839,9 @@ class SearchService extends __BaseService {
* @param take Take
*/
SearchHistory(take?: null | number): __Observable<ResponseArgsOfIEnumerableOfQueryTokenDTO> {
return this.SearchHistoryResponse(take).pipe(__map((_r) => _r.body as ResponseArgsOfIEnumerableOfQueryTokenDTO));
return this.SearchHistoryResponse(take).pipe(
__map(_r => _r.body as ResponseArgsOfIEnumerableOfQueryTokenDTO)
);
}
/**
@@ -713,25 +854,27 @@ class SearchService extends __BaseService {
*
* @return ResponseArgs of Recomendations
*/
SearchGetRecommendationsResponse(
params: SearchService.SearchGetRecommendationsParams,
): __Observable<__StrictHttpResponse<ResponseArgsOfIEnumerableOfItemDTO>> {
SearchGetRecommendationsResponse(params: SearchService.SearchGetRecommendationsParams): __Observable<__StrictHttpResponse<ResponseArgsOfIEnumerableOfItemDTO>> {
let __params = this.newParams();
let __headers = new HttpHeaders();
let __body: any = null;
if (params.sessionId != null) __params = __params.set('sessionId', params.sessionId.toString());
let req = new HttpRequest<any>('GET', this.rootUrl + `/s/recommendations/${encodeURIComponent(String(params.digId))}`, __body, {
headers: __headers,
params: __params,
responseType: 'json',
});
let req = new HttpRequest<any>(
'GET',
this.rootUrl + `/s/recommendations/${encodeURIComponent(String(params.digId))}`,
__body,
{
headers: __headers,
params: __params,
responseType: 'json'
});
return this.http.request<any>(req).pipe(
__filter((_r) => _r instanceof HttpResponse),
__filter(_r => _r instanceof HttpResponse),
__map((_r) => {
return _r as __StrictHttpResponse<ResponseArgsOfIEnumerableOfItemDTO>;
}),
})
);
}
/**
@@ -745,15 +888,19 @@ class SearchService extends __BaseService {
* @return ResponseArgs of Recomendations
*/
SearchGetRecommendations(params: SearchService.SearchGetRecommendationsParams): __Observable<ResponseArgsOfIEnumerableOfItemDTO> {
return this.SearchGetRecommendationsResponse(params).pipe(__map((_r) => _r.body as ResponseArgsOfIEnumerableOfItemDTO));
return this.SearchGetRecommendationsResponse(params).pipe(
__map(_r => _r.body as ResponseArgsOfIEnumerableOfItemDTO)
);
}
}
module SearchService {
/**
* Parameters for SearchTop2
*/
export interface SearchTop2Params {
/**
* Lager PK (optional)
*/
@@ -769,6 +916,7 @@ module SearchService {
* Parameters for SearchSearch2
*/
export interface SearchSearch2Params {
/**
* Lager PK (optional)
*/
@@ -784,6 +932,7 @@ module SearchService {
* Parameters for SearchAutocomplete2
*/
export interface SearchAutocomplete2Params {
/**
* Lager PK (optional)
*/
@@ -799,6 +948,7 @@ module SearchService {
* Parameters for SearchById2
*/
export interface SearchById2Params {
/**
* Lager PK (optional)
*/
@@ -814,6 +964,7 @@ module SearchService {
* Parameters for SearchByEAN2
*/
export interface SearchByEAN2Params {
/**
* Lager PK (optional)
*/
@@ -829,6 +980,7 @@ module SearchService {
* Parameters for SearchByEAN3
*/
export interface SearchByEAN3Params {
/**
* EANs
*/
@@ -844,6 +996,7 @@ module SearchService {
* Parameters for SearchDetail
*/
export interface SearchDetailParams {
/**
* PK
*/
@@ -855,6 +1008,7 @@ module SearchService {
* Parameters for SearchDetail2
*/
export interface SearchDetail2Params {
/**
* Lager PK (optional)
*/
@@ -871,6 +1025,7 @@ module SearchService {
* Parameters for SearchDetailByEAN2
*/
export interface SearchDetailByEAN2Params {
/**
* Lager PK (optional)
*/
@@ -895,4 +1050,4 @@ module SearchService {
}
}
export { SearchService };
export { SearchService }

View File

@@ -6,4 +6,4 @@ import { HttpResponse } from '@angular/common/http';
*/
export type StrictHttpResponse<T> = HttpResponse<T> & {
readonly body: T;
};
}

View File

@@ -1 +1,2 @@
export * from './catalouge-search.schemas';
export * from './query-token.schema';

View File

@@ -0,0 +1,31 @@
import { z } from 'zod';
/**
* Schema for defining order-by parameters in queries.
* Used for sorting search results.
*/
export const OrderBySchema = z.object({
by: z.string(), // Field name to sort by
label: z.string(), // Display label for the sort option
desc: z.boolean(), // Whether sorting is descending
selected: z.boolean(), // Whether this sort option is currently selected
});
/**
* Schema for validating and parsing query tokens.
* Used for search operations to ensure consistent query structure.
*/
export const QueryTokenSchema = z.object({
filter: z.record(z.any()).default({}), // Filter criteria as key-value pairs
input: z.record(z.string()).default({}).optional(),
orderBy: z.array(OrderBySchema).default([]).optional(), // Sorting parameters
skip: z.number().int().min(0).default(0),
take: z.number().int().min(1).max(100).default(20),
});
/**
* Type representing the structure of a query token.
* Generated from the Zod schema for type safety.
*/
export type QueryToken = z.infer<typeof QueryTokenSchema>;
export type QueryTokenInput = z.input<typeof QueryTokenSchema>;

View File

@@ -4,6 +4,7 @@ import { CatalougeSearchService } from './catalouge-search.service';
import { SearchService } from '@generated/swagger/cat-search-api';
import { Item } from '../models';
import { SearchByTermInput } from '../schemas/catalouge-search.schemas';
import { QueryTokenInput } from '../schemas';
describe('CatalougeSearchService', () => {
let service: CatalougeSearchService;
@@ -23,7 +24,9 @@ describe('CatalougeSearchService', () => {
});
service = TestBed.inject(CatalougeSearchService);
searchServiceSpy = TestBed.inject(SearchService) as jest.Mocked<SearchService>;
searchServiceSpy = TestBed.inject(
SearchService,
) as jest.Mocked<SearchService>;
});
it('should be created', () => {
@@ -34,8 +37,16 @@ describe('CatalougeSearchService', () => {
it('should return items when search is successful', (done) => {
// Arrange
const mockItems: Item[] = [
{ id: 1, product: { name: 'Item 1' }, catalogAvailability: { available: true } } as unknown as Item,
{ id: 2, product: { name: 'Item 2' }, catalogAvailability: { available: true } } as unknown as Item,
{
id: 1,
product: { name: 'Item 1' },
catalogAvailability: { available: true },
} as unknown as Item,
{
id: 2,
product: { name: 'Item 2' },
catalogAvailability: { available: true },
} as unknown as Item,
];
const mockResponse = {
error: false,
@@ -48,7 +59,10 @@ describe('CatalougeSearchService', () => {
next: (result) => {
// Assert
expect(result).toEqual(mockItems);
expect(searchServiceSpy.SearchByEAN).toHaveBeenCalledWith(['123456789', '987654321']);
expect(searchServiceSpy.SearchByEAN).toHaveBeenCalledWith([
'123456789',
'987654321',
]);
done();
},
error: done.fail,
@@ -77,7 +91,13 @@ describe('CatalougeSearchService', () => {
it('should handle single EAN', (done) => {
// Arrange
const mockItems: Item[] = [{ id: 1, product: { name: 'Item 1' }, catalogAvailability: { available: true } } as unknown as Item];
const mockItems: Item[] = [
{
id: 1,
product: { name: 'Item 1' },
catalogAvailability: { available: true },
} as unknown as Item,
];
const mockResponse = {
error: false,
result: mockItems,
@@ -89,7 +109,9 @@ describe('CatalougeSearchService', () => {
next: (result) => {
// Assert
expect(result).toEqual(mockItems);
expect(searchServiceSpy.SearchByEAN).toHaveBeenCalledWith(['123456789']);
expect(searchServiceSpy.SearchByEAN).toHaveBeenCalledWith([
'123456789',
]);
done();
},
error: done.fail,
@@ -121,7 +143,11 @@ describe('CatalougeSearchService', () => {
it('should return search results when successful', async () => {
// Arrange
const mockItems: Item[] = [
{ id: 1, product: { name: 'Test Item' }, catalogAvailability: { available: true } } as unknown as Item,
{
id: 1,
product: { name: 'Test Item' },
catalogAvailability: { available: true },
} as unknown as Item,
];
const mockResponse = {
error: false,
@@ -166,9 +192,9 @@ describe('CatalougeSearchService', () => {
const abortController = new AbortController();
// Act & Assert
await expect(service.searchByTerm(params, abortController.signal))
.rejects
.toThrow('Search failed');
await expect(
service.searchByTerm(params, abortController.signal),
).rejects.toThrow('Search failed');
});
it('should handle abort signal', async () => {
@@ -219,4 +245,167 @@ describe('CatalougeSearchService', () => {
});
});
});
});
describe('searchLoyaltyItems', () => {
it('should return loyalty items when search is successful', async () => {
// Arrange
const mockItems: Item[] = [
{
id: 1,
product: { name: 'Loyalty Item 1' },
catalogAvailability: { available: true },
} as unknown as Item,
{
id: 2,
product: { name: 'Loyalty Item 2' },
catalogAvailability: { available: true },
} as unknown as Item,
];
const mockResponse = {
error: false,
result: mockItems,
total: 2,
};
searchServiceSpy.SearchSearch.mockReturnValue(of(mockResponse));
const params: QueryTokenInput = {
filter: { category: 'electronics' },
input: { brand: 'test-brand' },
orderBy: [{ by: 'name', label: 'Name', desc: false, selected: true }],
skip: 0,
take: 10,
};
const abortController = new AbortController();
// Act
const result = await service.searchLoyaltyItems(
params,
abortController.signal,
);
// Assert
expect(result).toEqual(mockResponse);
expect(searchServiceSpy.SearchSearch).toHaveBeenCalledWith({
filter: {
format: '!eb;!dl',
praemie: '1-',
category: 'electronics',
},
input: { brand: 'test-brand' },
orderBy: [{ by: 'name', label: 'Name', desc: false, selected: true }],
skip: 0,
take: 10,
});
});
it('should apply default loyalty filter when no custom filter provided', async () => {
// Arrange
const mockItems: Item[] = [
{
id: 1,
product: { name: 'Loyalty Item' },
catalogAvailability: { available: true },
} as unknown as Item,
];
const mockResponse = {
error: false,
result: mockItems,
total: 1,
};
searchServiceSpy.SearchSearch.mockReturnValue(of(mockResponse));
const params: QueryTokenInput = {
input: { search: 'test' },
};
const abortController = new AbortController();
// Act
const result = await service.searchLoyaltyItems(
params,
abortController.signal,
);
// Assert
expect(result).toEqual(mockResponse);
expect(searchServiceSpy.SearchSearch).toHaveBeenCalledWith({
filter: {
format: '!eb;!dl',
praemie: '1-',
},
input: { search: 'test' },
orderBy: undefined,
skip: 0,
take: 20,
});
});
it('should merge custom filter with loyalty filter', async () => {
// Arrange
const mockResponse = {
error: false,
result: [],
total: 0,
};
searchServiceSpy.SearchSearch.mockReturnValue(of(mockResponse));
const params: QueryTokenInput = {
filter: {
category: 'books',
format: 'custom-format', // This should override the default format
},
input: { title: 'test-book' },
skip: 5,
take: 15,
};
const abortController = new AbortController();
// Act
await service.searchLoyaltyItems(params, abortController.signal);
// Assert
expect(searchServiceSpy.SearchSearch).toHaveBeenCalledWith({
filter: {
format: 'custom-format', // Custom filter should override default
praemie: '1-',
category: 'books',
},
input: { title: 'test-book' },
orderBy: undefined,
skip: 5,
take: 15,
});
});
it('should handle minimal parameters with defaults', async () => {
// Arrange
const mockResponse = {
error: false,
result: [],
total: 0,
};
searchServiceSpy.SearchSearch.mockReturnValue(of(mockResponse));
const params: QueryTokenInput = {};
const abortController = new AbortController();
// Act
const result = await service.searchLoyaltyItems(
params,
abortController.signal,
);
// Assert
expect(result).toEqual(mockResponse);
expect(searchServiceSpy.SearchSearch).toHaveBeenCalledWith({
filter: {
format: '!eb;!dl',
praemie: '1-',
},
input: undefined,
orderBy: undefined,
skip: 0,
take: 20,
});
});
});
});

View File

@@ -8,6 +8,7 @@ import {
SearchByTermSchema,
} from '../schemas/catalouge-search.schemas';
import { ListResponseArgs } from '@isa/common/data-access';
import { QueryTokenInput, QueryTokenSchema } from '../schemas';
@Injectable({ providedIn: 'root' })
export class CatalougeSearchService {
@@ -50,4 +51,33 @@ export class CatalougeSearchService {
return res as ListResponseArgs<Item>;
}
async searchLoyaltyItems(params: QueryTokenInput, abortSignal: AbortSignal) {
const { filter, input, orderBy, skip, take } =
QueryTokenSchema.parse(params);
const loyaltyFilter = {
format: '!eb;!dl',
praemie: '1-',
...filter,
};
const req$ = this.#searchService
.SearchSearch({
filter: loyaltyFilter,
input,
orderBy,
skip,
take,
})
.pipe(takeUntilAborted(abortSignal));
const res = await firstValueFrom(req$);
if (res.error) {
throw new Error(res.message);
}
return res as ListResponseArgs<Item>;
}
}

View File

@@ -0,0 +1,7 @@
# data-access
This library was generated with [Nx](https://nx.dev).
## Running unit tests
Run `nx test data-access` to execute the unit tests.

View File

@@ -0,0 +1,34 @@
const nx = require('@nx/eslint-plugin');
const baseConfig = require('../../../eslint.config.js');
module.exports = [
...baseConfig,
...nx.configs['flat/angular'],
...nx.configs['flat/angular-template'],
{
files: ['**/*.ts'],
rules: {
'@angular-eslint/directive-selector': [
'error',
{
type: 'attribute',
prefix: 'lib',
style: 'camelCase',
},
],
'@angular-eslint/component-selector': [
'error',
{
type: 'element',
prefix: 'lib',
style: 'kebab-case',
},
],
},
},
{
files: ['**/*.html'],
// Override or add rules here
rules: {},
},
];

View File

@@ -0,0 +1,20 @@
{
"name": "data-access",
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "libs/checkout/data-access/src",
"prefix": "lib",
"projectType": "library",
"tags": [],
"targets": {
"test": {
"executor": "@nx/vite:test",
"outputs": ["{options.reportsDirectory}"],
"options": {
"reportsDirectory": "../../../coverage/libs/checkout/data-access"
}
},
"lint": {
"executor": "@nx/eslint:lint"
}
}
}

View File

@@ -0,0 +1,2 @@
export * from './lib/services';
export * from './lib/models';

View File

@@ -0,0 +1 @@
export * from './query-settings';

View File

@@ -0,0 +1,3 @@
import { UISettingsDTO } from '@generated/swagger/cat-search-api';
export type QuerySettings = UISettingsDTO;

View File

@@ -0,0 +1 @@
export * from './reward-checkout.service';

View File

@@ -0,0 +1,28 @@
import { Injectable, inject } from '@angular/core';
import { logger } from '@isa/core/logging';
import { SearchService } from '@generated/swagger/cat-search-api';
import { firstValueFrom } from 'rxjs';
import { QuerySettings } from '../models';
@Injectable({ providedIn: 'root' })
export class RewardCheckoutService {
#searchService = inject(SearchService);
#logger = logger(() => ({ service: 'RewardCheckoutService' }));
async fetchQuerySettings(): Promise<QuerySettings> {
this.#logger.info('Fetching query settings from API');
const req$ = this.#searchService.SearchLoyaltySettings();
const res = await firstValueFrom(req$);
if (res.error || !res.result) {
const error = new Error(res.message || 'Failed to fetch Query Settings');
this.#logger.error('Failed to fetch query settings', error);
throw error;
}
this.#logger.debug('Successfully fetched query settings');
return res.result as QuerySettings;
}
}

View File

@@ -0,0 +1,13 @@
import '@angular/compiler';
import '@analogjs/vitest-angular/setup-zone';
import {
BrowserTestingModule,
platformBrowserTesting,
} from '@angular/platform-browser/testing';
import { getTestBed } from '@angular/core/testing';
getTestBed().initTestEnvironment(
BrowserTestingModule,
platformBrowserTesting(),
);

View File

@@ -0,0 +1,30 @@
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"importHelpers": true,
"moduleResolution": "bundler",
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"module": "preserve"
},
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"typeCheckHostBindings": true,
"strictTemplates": true
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
},
{
"path": "./tsconfig.spec.json"
}
]
}

View File

@@ -0,0 +1,27 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../../dist/out-tsc",
"declaration": true,
"declarationMap": true,
"inlineSources": true,
"types": []
},
"exclude": [
"src/**/*.spec.ts",
"src/test-setup.ts",
"jest.config.ts",
"src/**/*.test.ts",
"vite.config.ts",
"vite.config.mts",
"vitest.config.ts",
"vitest.config.mts",
"src/**/*.test.tsx",
"src/**/*.spec.tsx",
"src/**/*.test.js",
"src/**/*.spec.js",
"src/**/*.test.jsx",
"src/**/*.spec.jsx"
],
"include": ["src/**/*.ts"]
}

View File

@@ -0,0 +1,29 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../../dist/out-tsc",
"types": [
"vitest/globals",
"vitest/importMeta",
"vite/client",
"node",
"vitest"
]
},
"include": [
"vite.config.ts",
"vite.config.mts",
"vitest.config.ts",
"vitest.config.mts",
"src/**/*.test.ts",
"src/**/*.spec.ts",
"src/**/*.test.tsx",
"src/**/*.spec.tsx",
"src/**/*.test.js",
"src/**/*.spec.js",
"src/**/*.test.jsx",
"src/**/*.spec.jsx",
"src/**/*.d.ts"
],
"files": ["src/test-setup.ts"]
}

View File

@@ -0,0 +1,27 @@
/// <reference types='vitest' />
import { defineConfig } from 'vite';
import angular from '@analogjs/vite-plugin-angular';
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
import { nxCopyAssetsPlugin } from '@nx/vite/plugins/nx-copy-assets.plugin';
export default defineConfig(() => ({
root: __dirname,
cacheDir: '../../../node_modules/.vite/libs/checkout/data-access',
plugins: [angular(), nxViteTsPaths(), nxCopyAssetsPlugin(['*.md'])],
// Uncomment this if you are using workers.
// worker: {
// plugins: [ nxViteTsPaths() ],
// },
test: {
watch: false,
globals: true,
environment: 'jsdom',
include: ['{src,tests}/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
setupFiles: ['src/test-setup.ts'],
reporters: ['default'],
coverage: {
reportsDirectory: '../../../coverage/libs/checkout/data-access',
provider: 'v8' as const,
},
},
}));

View File

@@ -0,0 +1,7 @@
# reward-catalog
This library was generated with [Nx](https://nx.dev).
## Running unit tests
Run `nx test reward-catalog` to execute the unit tests.

View File

@@ -0,0 +1,34 @@
const nx = require('@nx/eslint-plugin');
const baseConfig = require('../../../../eslint.config.js');
module.exports = [
...baseConfig,
...nx.configs['flat/angular'],
...nx.configs['flat/angular-template'],
{
files: ['**/*.ts'],
rules: {
'@angular-eslint/directive-selector': [
'error',
{
type: 'attribute',
prefix: 'lib',
style: 'camelCase',
},
],
'@angular-eslint/component-selector': [
'error',
{
type: 'element',
prefix: 'lib',
style: 'kebab-case',
},
],
},
},
{
files: ['**/*.html'],
// Override or add rules here
rules: {},
},
];

View File

@@ -0,0 +1,20 @@
{
"name": "reward-catalog",
"$schema": "../../../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "libs/checkout/feature/reward-catalog/src",
"prefix": "lib",
"projectType": "library",
"tags": [],
"targets": {
"test": {
"executor": "@nx/vite:test",
"outputs": ["{options.reportsDirectory}"],
"options": {
"reportsDirectory": "../../../../coverage/libs/checkout/feature/reward-catalog"
}
},
"lint": {
"executor": "@nx/eslint:lint"
}
}
}

View File

@@ -0,0 +1 @@
export * from './lib/routes';

View File

@@ -0,0 +1,9 @@
import { inject } from '@angular/core';
import { ResolveFn } from '@angular/router';
import {
QuerySettings,
RewardCheckoutService,
} from '@isa/checkout/data-access';
export const querySettingsResolverFn: ResolveFn<QuerySettings> = () =>
inject(RewardCheckoutService).fetchQuerySettings();

View File

@@ -0,0 +1 @@
export * from './reward-catalog.resource';

View File

@@ -0,0 +1,33 @@
import { inject, resource } from '@angular/core';
import {
CatalougeSearchService,
QueryTokenInput,
} from '@isa/catalogue/data-access';
import { ResponseArgsError } from '@isa/common/data-access';
import { SearchTrigger } from '@isa/shared/filter';
export const createRewardCatalogResource = (
params: () => {
queryToken: QueryTokenInput;
searchTrigger: SearchTrigger | 'reload' | 'initial';
},
) => {
const catalogSearchService = inject(CatalougeSearchService);
return resource({
params,
loader: async ({ abortSignal, params }) => {
const fetchCatalogResponse =
await catalogSearchService.searchLoyaltyItems(
params.queryToken,
abortSignal,
);
if (fetchCatalogResponse?.error) {
throw new ResponseArgsError(fetchCatalogResponse);
}
return fetchCatalogResponse;
},
});
};

View File

@@ -0,0 +1,9 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
@Component({
selector: 'reward-catalog-item',
templateUrl: './reward-catalog-item.component.html',
styleUrl: './reward-catalog-item.component.css',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class RewardCatalogItemComponent {}

View File

@@ -0,0 +1,70 @@
<reward-start-card></reward-start-card>
<!-- <filter-controls-panel (triggerSearch)="search($event)"></filter-controls-panel> -->
<span
class="text-isa-neutral-900 isa-text-body-2-regular self-start"
data-what="result-count"
>
<!-- {{ hits() }} Einträge -->
XXX Einträge
</span>
<div class="flex flex-col gap-4 w-full items-center justify-center mb-24">
<!-- @if (hits()) {
<!-- @for (item of items(); track item.id) {
@defer (on viewport) {
<reward-catalog-item
#listElement
[item]="item"
[stock]="getStockForItem(item)"
[stockFetching]="inStockFetching()"
[productGroupValue]="getProductGroupValueForItem(item)"
(inProgressChange)="onListItemActionInProgress($event)"
></reward-catalog-item>
} @placeholder {
<div class="h-[7.75rem] w-full flex items-center justify-center">
<ui-icon-button
[pending]="true"
[color]="'tertiary'"
data-what="load-spinner"
data-which="item-placeholder"
></ui-icon-button>
</div>
}
}
} @else {
<ui-empty-state
class="w-full justify-self-center"
title="Keine Suchergebnisse"
description="Bitte prüfen Sie die Schreibweise oder ändern Sie die Filtereinstellungen."
>
</ui-empty-state>
} -->
<ui-empty-state
class="w-full justify-self-center"
title="Keine Suchergebnisse"
description="Bitte prüfen Sie die Schreibweise oder ändern Sie die Filtereinstellungen."
>
</ui-empty-state>
</div>
<!--
<ui-stateful-button
class="fixed right-6 bottom-6"
(clicked)="remitItems()"
(action)="remitItems()"
[(state)]="remitItemsState"
defaultContent="Prämie auswählen"
defaultWidth="13rem"
[errorContent]="remitItemsError()"
errorWidth="32rem"
errorAction="Erneut versuchen"
successContent="Hinzugefügt"
successWidth="20rem"
size="large"
color="brand"
[pending]="remitItemsInProgress()"
[disabled]="!hasSelectedItems() || listItemActionInProgress()"
>
</ui-stateful-button>} -->

View File

@@ -0,0 +1,131 @@
import {
ChangeDetectionStrategy,
Component,
computed,
inject,
signal,
} from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import {
provideFilter,
withQuerySettingsFactory,
withQueryParamsSync,
FilterControlsPanelComponent,
SearchTrigger,
FilterService,
} from '@isa/shared/filter';
import { IconButtonComponent } from '@isa/ui/buttons';
import { injectRestoreScrollPosition } from '@isa/utils/scroll-position';
import { RewardStartCardComponent } from './reward-start-card/reward-start-card.component';
import { logger } from '@isa/core/logging';
import { createRewardCatalogResource } from './resources';
import { EmptyStateComponent } from '@isa/ui/empty-state';
import { RewardCatalogItemComponent } from './reward-catalog-item/reward-catalog-item.component';
/**
* Factory function to retrieve query settings from the activated route data.
* @returns The query settings from the activated route data.
*/
function querySettingsFactory() {
return inject(ActivatedRoute).snapshot.data['querySettings'];
}
@Component({
selector: 'reward-catalog',
templateUrl: './reward-catalog.component.html',
styleUrl: './reward-catalog.component.css',
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [
provideFilter(
withQuerySettingsFactory(querySettingsFactory),
withQueryParamsSync(),
),
],
imports: [
FilterControlsPanelComponent,
IconButtonComponent,
EmptyStateComponent,
RewardStartCardComponent,
RewardCatalogItemComponent,
],
host: {
'[class]':
'"w-full flex flex-col gap-4 mt-5 isa-desktop:mt-6 overflow-x-hidden"',
},
})
export class RewardCatalogComponent {
/**
* Signal to trigger searches in the reward catalog.
* Can be 'reload', 'initial', or a specific SearchTrigger value.
*/
searchTrigger = signal<SearchTrigger | 'reload' | 'initial'>('initial');
/**
* Logger instance for logging component events and errors.
* @private
*/
#logger = logger(() => ({
component: 'RewardCatalogComponent',
}));
/**
* FilterService instance for managing filter state and queries.
* @private
*/
// #filterService = inject(FilterService);
/**
* Restores scroll position when navigating back to this component.
*/
restoreScrollPosition = injectRestoreScrollPosition();
/**
* Resource for fetching and managing the reward catalog data.
* Uses the FilterService to get the current query token and reacts to search triggers.
* @private
* @returns The reward catalog resource.
*/
// rewardCatalogResource = createRewardCatalogResource(() => {
// return {
// queryToken: this.#filterService.query(),
// searchTrigger: this.searchTrigger(),
// };
// });
/**
* Computed signal for the current reward catalog response.
* @returns The latest reward catalog response or undefined.
*/
// listResponseValue = computed(() => this.rewardCatalogResource.value());
/**
* Computed signal indicating whether the reward catalog resource is currently fetching data.
* @returns True if fetching, false otherwise.
*/
// listFetching = computed(
// () => this.rewardCatalogResource.status() === 'loading',
// );
/**
* Computed signal for the reward catalog items to display.
* @returns Array of Item.
*/
// items = computed(() => {
// const value = this.listResponseValue();
// return value?.result ? value.result : [];
// });
/**
* Computed signal for the total number of hits in the reward catalog.
* @returns Number of hits, or 0 if unavailable.
*/
// hits = computed(() => {
// const value = this.listResponseValue();
// return value?.hits ? value.hits : 0;
// });
search(trigger: SearchTrigger): void {
// this.#filterService.commit();
this.searchTrigger.set(trigger);
}
}

View File

@@ -0,0 +1,11 @@
:host {
@apply w-full grid grid-cols-[1fr,auto] gap-6 rounded-2xl bg-isa-neutral-400 p-6 justify-between;
}
.reward-start-card__title-container {
@apply flex flex-col gap-4 text-isa-neutral-900;
}
.reward-start-card__select-cta {
@apply h-12 w-[13rem] justify-self-end;
}

View File

@@ -0,0 +1,20 @@
<div class="reward-start-card__title-container">
<h2 class="isa-text-subtitle-1-regular">Prämienshop</h2>
<p class="isa-text-body-1-regular">
Sie können die Prämien nach Punkten sortieren, filter oder direkt über die
Suche auswählen
</p>
</div>
<button
class="reward-start-card__select-cta"
data-which="select-customer"
data-what="select-customer"
uiButton
color="tertiary"
size="large"
(click)="selectCustomer()"
>
Kund*in auswählen
</button>

View File

@@ -0,0 +1,15 @@
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import { ButtonComponent } from '@isa/ui/buttons';
@Component({
selector: 'reward-start-card',
templateUrl: './reward-start-card.component.html',
styleUrl: './reward-start-card.component.css',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [ButtonComponent],
})
export class RewardStartCardComponent {
selectCustomer() {
console.log('Kunde auswählen');
}
}

View File

@@ -0,0 +1,19 @@
import { Routes } from '@angular/router';
import { RewardCatalogComponent } from './reward-catalog.component';
import { querySettingsResolverFn } from './resolvers/query-settings.resolver-fn';
export const routes: Routes = [
{
path: '',
component: RewardCatalogComponent,
resolve: { querySettings: querySettingsResolverFn },
data: {
scrollPositionRestoration: true,
},
},
{
path: '',
redirectTo: '',
pathMatch: 'full',
},
];

View File

@@ -0,0 +1,13 @@
import '@angular/compiler';
import '@analogjs/vitest-angular/setup-zone';
import {
BrowserTestingModule,
platformBrowserTesting,
} from '@angular/platform-browser/testing';
import { getTestBed } from '@angular/core/testing';
getTestBed().initTestEnvironment(
BrowserTestingModule,
platformBrowserTesting(),
);

View File

@@ -0,0 +1,30 @@
{
"extends": "../../../../tsconfig.base.json",
"compilerOptions": {
"importHelpers": true,
"moduleResolution": "bundler",
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"module": "preserve"
},
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"typeCheckHostBindings": true,
"strictTemplates": true
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
},
{
"path": "./tsconfig.spec.json"
}
]
}

View File

@@ -0,0 +1,27 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../../../dist/out-tsc",
"declaration": true,
"declarationMap": true,
"inlineSources": true,
"types": []
},
"exclude": [
"src/**/*.spec.ts",
"src/test-setup.ts",
"jest.config.ts",
"src/**/*.test.ts",
"vite.config.ts",
"vite.config.mts",
"vitest.config.ts",
"vitest.config.mts",
"src/**/*.test.tsx",
"src/**/*.spec.tsx",
"src/**/*.test.js",
"src/**/*.spec.js",
"src/**/*.test.jsx",
"src/**/*.spec.jsx"
],
"include": ["src/**/*.ts"]
}

View File

@@ -0,0 +1,29 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../../../dist/out-tsc",
"types": [
"vitest/globals",
"vitest/importMeta",
"vite/client",
"node",
"vitest"
]
},
"include": [
"vite.config.ts",
"vite.config.mts",
"vitest.config.ts",
"vitest.config.mts",
"src/**/*.test.ts",
"src/**/*.spec.ts",
"src/**/*.test.tsx",
"src/**/*.spec.tsx",
"src/**/*.test.js",
"src/**/*.spec.js",
"src/**/*.test.jsx",
"src/**/*.spec.jsx",
"src/**/*.d.ts"
],
"files": ["src/test-setup.ts"]
}

View File

@@ -0,0 +1,28 @@
/// <reference types='vitest' />
import { defineConfig } from 'vite';
import angular from '@analogjs/vite-plugin-angular';
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
import { nxCopyAssetsPlugin } from '@nx/vite/plugins/nx-copy-assets.plugin';
export default defineConfig(() => ({
root: __dirname,
cacheDir: '../../../../node_modules/.vite/libs/loyalty/feature/loyalty-list',
plugins: [angular(), nxViteTsPaths(), nxCopyAssetsPlugin(['*.md'])],
// Uncomment this if you are using workers.
// worker: {
// plugins: [ nxViteTsPaths() ],
// },
test: {
watch: false,
globals: true,
environment: 'jsdom',
include: ['{src,tests}/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
setupFiles: ['src/test-setup.ts'],
reporters: ['default'],
coverage: {
reportsDirectory:
'../../../../coverage/libs/loyalty/feature/loyalty-list',
provider: 'v8' as const,
},
},
}));

View File

@@ -40,6 +40,10 @@
"@generated/swagger/wws-api": ["generated/swagger/wws-api/src/index.ts"],
"@hub/*": ["apps/isa-app/src/hub/*/index.ts"],
"@isa/catalogue/data-access": ["libs/catalogue/data-access/src/index.ts"],
"@isa/checkout/data-access": ["libs/checkout/data-access/src/index.ts"],
"@isa/checkout/feature/reward-catalog": [
"libs/checkout/feature/reward-catalog/src/index.ts"
],
"@isa/common/data-access": ["libs/common/data-access/src/index.ts"],
"@isa/common/decorators": ["libs/common/decorators/src/index.ts"],
"@isa/common/print": ["libs/common/print/src/index.ts"],