mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
Kulturpass - loader anzeige
This commit is contained in:
6
apps/shared/components/loader/ng-package.json
Normal file
6
apps/shared/components/loader/ng-package.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"$schema": "../../../../node_modules/ng-packagr/ng-package.schema.json",
|
||||
"lib": {
|
||||
"entryFile": "src/public-api.ts"
|
||||
}
|
||||
}
|
||||
11
apps/shared/components/loader/src/lib/loader.component.css
Normal file
11
apps/shared/components/loader/src/lib/loader.component.css
Normal file
@@ -0,0 +1,11 @@
|
||||
.shared-loader {
|
||||
@apply relative block h-full;
|
||||
}
|
||||
|
||||
.shared-loader__spinner {
|
||||
@apply absolute inset-0 flex items-center justify-center;
|
||||
}
|
||||
|
||||
.shared-loader__background {
|
||||
@apply bg-white bg-opacity-75;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<div class="shared-loader__spinner" *ngIf="showLoader" [class.shared-loader__background]="showBackground">
|
||||
<ui-icon class="animate-spin" icon="spinner" [size]="spinnerSizeInRem"></ui-icon>
|
||||
</div>
|
||||
<div [class.invisible]="showLoader && contentHidden">
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
43
apps/shared/components/loader/src/lib/loader.component.ts
Normal file
43
apps/shared/components/loader/src/lib/loader.component.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { BooleanInput, NumberInput, coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';
|
||||
import { NgIf } from '@angular/common';
|
||||
import { Component, ChangeDetectionStrategy, ViewEncapsulation, Input } from '@angular/core';
|
||||
import { UiIconModule } from '@ui/icon';
|
||||
|
||||
@Component({
|
||||
selector: 'shared-loader',
|
||||
templateUrl: 'loader.component.html',
|
||||
styleUrls: ['loader.component.css'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
host: { class: 'shared-loader' },
|
||||
standalone: true,
|
||||
imports: [NgIf, UiIconModule],
|
||||
})
|
||||
export class LoaderComponent {
|
||||
@Input() loading: BooleanInput;
|
||||
|
||||
@Input() background: BooleanInput;
|
||||
|
||||
@Input() spinnerSize: NumberInput = 24;
|
||||
|
||||
@Input() hideContent: BooleanInput;
|
||||
|
||||
get showLoader() {
|
||||
console.log('showLoader', this.loading);
|
||||
return coerceBooleanProperty(this.loading);
|
||||
}
|
||||
|
||||
get showBackground() {
|
||||
return coerceBooleanProperty(this.background);
|
||||
}
|
||||
|
||||
get spinnerSizeInRem() {
|
||||
return coerceNumberProperty(this.spinnerSize) / 16 + 'rem';
|
||||
}
|
||||
|
||||
get contentHidden() {
|
||||
return coerceBooleanProperty(this.hideContent);
|
||||
}
|
||||
|
||||
constructor() {}
|
||||
}
|
||||
9
apps/shared/components/loader/src/lib/loader.module.ts
Normal file
9
apps/shared/components/loader/src/lib/loader.module.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
import { LoaderComponent } from './loader.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [LoaderComponent],
|
||||
exports: [LoaderComponent],
|
||||
})
|
||||
export class LoaderModule {}
|
||||
2
apps/shared/components/loader/src/public-api.ts
Normal file
2
apps/shared/components/loader/src/public-api.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './lib/loader.component';
|
||||
export * from './lib/loader.module';
|
||||
@@ -1,4 +1,8 @@
|
||||
:host {
|
||||
@apply block;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
@apply grid max-h-[calc(100vh-8rem)] h-[calc(100vh-8rem)];
|
||||
grid-template-rows: repeat(4, auto) 1fr auto;
|
||||
}
|
||||
|
||||
@@ -1,64 +1,70 @@
|
||||
<div>
|
||||
<h1 class="text-center text-2xl font-bold">Kulturpass Warenkorb</h1>
|
||||
<p class="text-center text-lg">Bitte scannen Sie Artikel, um den Code einzulösen.</p>
|
||||
</div>
|
||||
<div>
|
||||
<shared-kulturpass-order-searchbox (addToShoppingCart)="addToShoppingCart($event)"></shared-kulturpass-order-searchbox>
|
||||
</div>
|
||||
<div class="border-y border-solid border-[#EFF1F5] py-3 px-4 -mx-4 flex flex-row items-center">
|
||||
<div class="w-[6.875rem]">
|
||||
Wertgutschein
|
||||
</div>
|
||||
<div class="ml-4 font-bold">
|
||||
{{ kulturpassCode$ | async }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="border-b border-solid border-[#EFF1F5] py-3 px-4 -mx-4 flex flex-row items-center">
|
||||
<div class="w-[6.875rem]">
|
||||
Filiale
|
||||
</div>
|
||||
<div class="ml-4 font-bold">
|
||||
{{ branch$ | async | branchName: 'name-address' }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="overflow-y-auto -mx-4 scroll-bar">
|
||||
<div *ngIf="emptyShoppingCart$ | async" class="h-full grid items-center justify-center">
|
||||
<h3 class="text-xl font-bold text-center text-gray-500">
|
||||
Warenkorb ist leer, bitte suchen oder scannen <br />
|
||||
Sie Artikel um den Warenkob zu füllen.
|
||||
</h3>
|
||||
</div>
|
||||
<shared-kulturpass-order-item
|
||||
class="border-b border-solid border-[#EFF1F5]"
|
||||
*ngFor="let item of items$ | async; trackBy: trackItemById"
|
||||
[item]="item"
|
||||
(quantityChange)="quantityChange($event)"
|
||||
>
|
||||
</shared-kulturpass-order-item>
|
||||
</div>
|
||||
<div class="flex flex-row justify-evenly items-stretch border-t border-solid border-[#EFF1F5] py-3 px-4 -mx-4">
|
||||
<div class="grid grid-flow-row text-sm">
|
||||
<div class="grid grid-cols-2 gap-5">
|
||||
<div>Guthaben</div>
|
||||
<div class="font-bold text-right">{{ credit$ | async | currency: 'EUR' }}</div>
|
||||
<shared-loader [loading]="fetchShoppingCart$ | async" background="true" spinnerSize="36">
|
||||
<div class="wrapper">
|
||||
<div>
|
||||
<h1 class="text-center text-2xl font-bold">Kulturpass Warenkorb</h1>
|
||||
<p class="text-center text-lg">Bitte scannen Sie Artikel, um den Code einzulösen.</p>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-5">
|
||||
<div>Summe</div>
|
||||
<div class="font-bold text-right">{{ total$ | async | currency: 'EUR' }}</div>
|
||||
<div>
|
||||
<shared-kulturpass-order-searchbox (addToShoppingCart)="addToShoppingCart($event)"></shared-kulturpass-order-searchbox>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-5">
|
||||
<div>Restbetrag</div>
|
||||
<div class="font-bold text-right" [class.text-brand]="negativeBalance$ | async">{{ balance$ | async | currency: 'EUR' }}</div>
|
||||
<div class="border-y border-solid border-[#EFF1F5] py-3 px-4 -mx-4 flex flex-row items-center">
|
||||
<div class="w-[6.875rem]">
|
||||
Wertgutschein
|
||||
</div>
|
||||
<div class="ml-4 font-bold">
|
||||
{{ kulturpassCode$ | async }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="border-b border-solid border-[#EFF1F5] py-3 px-4 -mx-4 flex flex-row items-center">
|
||||
<div class="w-[6.875rem]">
|
||||
Filiale
|
||||
</div>
|
||||
<div class="ml-4 font-bold">
|
||||
{{ branch$ | async | branchName: 'name-address' }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="overflow-y-auto -mx-4 scroll-bar">
|
||||
<div *ngIf="emptyShoppingCart$ | async" class="h-full grid items-center justify-center">
|
||||
<h3 class="text-xl font-bold text-center text-gray-500">
|
||||
Warenkorb ist leer, bitte suchen oder scannen <br />
|
||||
Sie Artikel um den Warenkob zu füllen.
|
||||
</h3>
|
||||
</div>
|
||||
<shared-kulturpass-order-item
|
||||
class="border-b border-solid border-[#EFF1F5]"
|
||||
*ngFor="let item of items$ | async; trackBy: trackItemById"
|
||||
[item]="item"
|
||||
(quantityChange)="quantityChange($event)"
|
||||
>
|
||||
</shared-kulturpass-order-item>
|
||||
</div>
|
||||
<div class="flex flex-row justify-evenly items-stretch border-t border-solid border-[#EFF1F5] py-3 px-4 -mx-4">
|
||||
<div class="grid grid-flow-row text-sm">
|
||||
<div class="grid grid-cols-2 gap-5">
|
||||
<div>Guthaben</div>
|
||||
<div class="font-bold text-right">{{ credit$ | async | currency: 'EUR' }}</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-5">
|
||||
<div>Summe</div>
|
||||
<div class="font-bold text-right">{{ total$ | async | currency: 'EUR' }}</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-5">
|
||||
<div>Restbetrag</div>
|
||||
<div class="font-bold text-right" [class.text-brand]="negativeBalance$ | async">{{ balance$ | async | currency: 'EUR' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid items-end justify-end">
|
||||
<button
|
||||
type="button"
|
||||
class="bg-brand text-white px-6 py-3 font-bold rounded-full disabled:bg-disabled-branch disabled:text-active-branch"
|
||||
[disabled]="orderButtonDisabled$ | async"
|
||||
(click)="order()"
|
||||
>
|
||||
<shared-loader [loading]="ordering$ | async" hideContent="true">
|
||||
Kauf abschließen und Rechnung drucken
|
||||
</shared-loader>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid items-end justify-end">
|
||||
<button
|
||||
type="button"
|
||||
class="bg-brand text-white px-6 py-3 font-bold rounded-full disabled:bg-disabled-branch disabled:text-active-branch"
|
||||
[disabled]="invalidShoppingCart$ | async"
|
||||
(click)="order()"
|
||||
>
|
||||
Kauf abschließen und Rechnung drucken
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</shared-loader>
|
||||
|
||||
@@ -10,6 +10,7 @@ import { CommonModule } from '@angular/common';
|
||||
import { BranchNamePipe } from '@shared/pipes/branch';
|
||||
import { combineLatest } from 'rxjs';
|
||||
import { KulturpassOrderItemComponent } from './kulturpass-order-item/kulturpass-order-item.component';
|
||||
import { LoaderComponent } from '@shared/components/loader';
|
||||
|
||||
@Component({
|
||||
selector: 'shared-kulturpass-order-modal',
|
||||
@@ -18,12 +19,14 @@ import { KulturpassOrderItemComponent } from './kulturpass-order-item/kulturpass
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
host: { class: 'shared-kulturpass-order-modal' },
|
||||
standalone: true,
|
||||
imports: [KulturpassOrderSearchboxComponent, CommonModule, BranchNamePipe, KulturpassOrderItemComponent],
|
||||
imports: [KulturpassOrderSearchboxComponent, CommonModule, BranchNamePipe, KulturpassOrderItemComponent, LoaderComponent],
|
||||
providers: [provideComponentStore(KulturpassOrderModalStore)],
|
||||
})
|
||||
export class KulturpassOrderModalComponent implements OnInit {
|
||||
branch$ = this._store.branch$;
|
||||
|
||||
fetchShoppingCart$ = this._store.fetchShoppingCart$;
|
||||
|
||||
items$ = this._store.shoppingCart$.pipe(map((shoppingCart) => shoppingCart?.items?.map((item) => item.data) ?? []));
|
||||
|
||||
trackItemById = (index: number, item: ShoppingCartItemDTO) => item.id;
|
||||
@@ -40,8 +43,10 @@ export class KulturpassOrderModalComponent implements OnInit {
|
||||
|
||||
emptyShoppingCart$ = this.items$.pipe(map((items) => items.length === 0));
|
||||
|
||||
invalidShoppingCart$ = combineLatest([this.emptyShoppingCart$, this.negativeBalance$]).pipe(
|
||||
map(([emptyShoppingCart, negativeBalance]) => emptyShoppingCart || negativeBalance)
|
||||
ordering$ = this._store.ordering$;
|
||||
|
||||
orderButtonDisabled$ = combineLatest([this.emptyShoppingCart$, this.negativeBalance$, this.ordering$]).pipe(
|
||||
map(([emptyShoppingCart, negativeBalance, ordering]) => emptyShoppingCart || negativeBalance || ordering)
|
||||
);
|
||||
|
||||
constructor(private _modalRef: UiModalRef<void, KulturpassOrderModalData>, private _store: KulturpassOrderModalStore) {}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { ComponentStore, OnStoreInit, tapResponse } from '@ngrx/component-store'
|
||||
import { AddToShoppingCartDTO, BranchDTO, CheckoutDTO, ShoppingCartDTO, ShoppingCartItemDTO } from '@swagger/checkout';
|
||||
import { DomainCheckoutService } from '@domain/checkout';
|
||||
import { switchMap, tap, withLatestFrom } from 'rxjs/operators';
|
||||
import { BranchService, BuyerDTO, OrderDTO, OrderItemListItemDTO, ResponseArgsOfIEnumerableOfBranchDTO } from '@swagger/oms';
|
||||
import { BranchService, OrderDTO, OrderItemListItemDTO, ResponseArgsOfIEnumerableOfBranchDTO } from '@swagger/oms';
|
||||
import { Observable } from 'rxjs';
|
||||
import { AuthService } from '@core/auth';
|
||||
import { UiModalService } from '@ui/modal';
|
||||
@@ -11,8 +11,10 @@ import { UiModalService } from '@ui/modal';
|
||||
export interface KulturpassOrderModalState {
|
||||
orderItemListItem?: OrderItemListItemDTO;
|
||||
shoppingCart?: ShoppingCartDTO;
|
||||
fetchShoppingCart?: boolean;
|
||||
branch?: BranchDTO;
|
||||
order?: OrderDTO;
|
||||
ordering?: boolean;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
@@ -35,6 +37,10 @@ export class KulturpassOrderModalStore extends ComponentStore<KulturpassOrderMod
|
||||
return this.get((s) => s.order);
|
||||
}
|
||||
|
||||
get fetchShoppingCart() {
|
||||
return this.get((s) => s.fetchShoppingCart);
|
||||
}
|
||||
|
||||
constructor(
|
||||
private _checkoutService: DomainCheckoutService,
|
||||
private _branchService: BranchService,
|
||||
@@ -60,6 +66,12 @@ export class KulturpassOrderModalStore extends ComponentStore<KulturpassOrderMod
|
||||
|
||||
readonly updateOrder = this.updater((state, order: OrderDTO) => ({ ...state, order }));
|
||||
|
||||
readonly fetchShoppingCart$ = this.select((state) => state.fetchShoppingCart);
|
||||
|
||||
readonly updateFetchShoppingCart = this.updater((state, fetchShoppingCart: boolean) => ({ ...state, fetchShoppingCart }));
|
||||
|
||||
readonly ordering$ = this.select((state) => state.ordering);
|
||||
|
||||
loadBranch = this.effect(($) =>
|
||||
$.pipe(switchMap(() => this._branchService.BranchGetBranches({}).pipe(tapResponse(this.handleBranchResponse, this.handleBranchError))))
|
||||
);
|
||||
@@ -78,6 +90,7 @@ export class KulturpassOrderModalStore extends ComponentStore<KulturpassOrderMod
|
||||
orderItemListItem$.pipe(
|
||||
tap((orderItemListItem) => {
|
||||
this.patchState({ orderItemListItem });
|
||||
this.updateFetchShoppingCart(true);
|
||||
}),
|
||||
switchMap((orderItemListItem) =>
|
||||
this._checkoutService
|
||||
@@ -91,32 +104,16 @@ export class KulturpassOrderModalStore extends ComponentStore<KulturpassOrderMod
|
||||
this.patchState({ shoppingCart: res });
|
||||
this._checkoutService.setBuyer({ processId: this.processId, buyer: this.order.buyer });
|
||||
this._checkoutService.setPayer({ processId: this.processId, payer: this.order.billing?.data });
|
||||
|
||||
this.updateFetchShoppingCart(false);
|
||||
};
|
||||
|
||||
handleCreateShoppingCartError = (err: any) => {
|
||||
this._modal.error('Fehler beim Laden des Warenkorbs', err);
|
||||
|
||||
this.updateFetchShoppingCart(false);
|
||||
};
|
||||
|
||||
// startCheckoutProcess = this.effect((orderItemListItem$: Observable<OrderItemListItemDTO>) =>
|
||||
// orderItemListItem$.pipe(
|
||||
// tap((orderItemListItem) => {
|
||||
// this._checkoutService.removeProcess({ processId: this.processId });
|
||||
// this.patchState({ orderItemListItem });
|
||||
// }),
|
||||
// switchMap((orderItemListItem) =>
|
||||
// this._checkoutService
|
||||
// .getCheckout({ processId: this.processId, refresh: true })
|
||||
// .pipe(tapResponse(this.handleCreateCheckoutResponse, this.handleCreateCheckoutError))
|
||||
// )
|
||||
// )
|
||||
// );
|
||||
|
||||
// handleCreateCheckoutResponse = (res: CheckoutDTO) => {
|
||||
// this.updateCheckout(res);
|
||||
// };
|
||||
|
||||
// handleCreateCheckoutError = (err: any) => {};
|
||||
|
||||
addItem = this.effect((add$: Observable<AddToShoppingCartDTO>) =>
|
||||
add$.pipe(
|
||||
withLatestFrom(this.orderItemListItem$),
|
||||
@@ -160,6 +157,7 @@ export class KulturpassOrderModalStore extends ComponentStore<KulturpassOrderMod
|
||||
|
||||
orderItems = this.effect(($: Observable<void>) =>
|
||||
$.pipe(
|
||||
tap(() => this.patchState({ ordering: true })),
|
||||
withLatestFrom(this.orderItemListItem$),
|
||||
switchMap(([_, orderItemListItem]) =>
|
||||
this._checkoutService
|
||||
@@ -172,11 +170,10 @@ export class KulturpassOrderModalStore extends ComponentStore<KulturpassOrderMod
|
||||
)
|
||||
);
|
||||
|
||||
handleOrderResponse = (res: any) => {
|
||||
console.log(res);
|
||||
};
|
||||
handleOrderResponse = (res: any) => {};
|
||||
|
||||
handleOrderError = (err: any) => {
|
||||
this._modal.error('Fehler beim Bestellen', err);
|
||||
this.patchState({ ordering: false });
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import { Observable, Subject } from 'rxjs';
|
||||
import { DomainAvailabilityService } from '@domain/availability';
|
||||
import { DomainCatalogService } from '@domain/catalog';
|
||||
import { AddToShoppingCartDTO, AvailabilityDTO, BranchDTO } from '@swagger/checkout';
|
||||
import { tap } from 'rxjs/operators';
|
||||
import { UiModalService } from '@ui/modal';
|
||||
|
||||
export interface KulturpassOrderSearchboxState {
|
||||
query: string;
|
||||
@@ -31,7 +33,11 @@ export class KulturpassOrderSearchboxStore extends ComponentStore<KulturpassOrde
|
||||
|
||||
hint$ = new Subject<string>();
|
||||
|
||||
constructor(private _catalogService: DomainCatalogService, private _availabilityService: DomainAvailabilityService) {
|
||||
constructor(
|
||||
private _catalogService: DomainCatalogService,
|
||||
private _availabilityService: DomainAvailabilityService,
|
||||
private _modal: UiModalService
|
||||
) {
|
||||
super({ query: '', fetching: false });
|
||||
}
|
||||
|
||||
@@ -62,11 +68,12 @@ export class KulturpassOrderSearchboxStore extends ComponentStore<KulturpassOrde
|
||||
};
|
||||
|
||||
handleBranchError = (err) => {
|
||||
console.log(err);
|
||||
this._modal.error('Fehler beim Laden der Filiale', err);
|
||||
};
|
||||
|
||||
readonly search = this.effect(($) =>
|
||||
$.pipe(
|
||||
tap(() => this.patchState({ fetching: true })),
|
||||
withLatestFrom(this.query$),
|
||||
switchMap(([_, query]) =>
|
||||
this._catalogService.getDetailsByEan({ ean: query }).pipe(tapResponse(this.handleSearchResponse, this.handleSearchError))
|
||||
@@ -86,7 +93,7 @@ export class KulturpassOrderSearchboxStore extends ComponentStore<KulturpassOrde
|
||||
|
||||
handleSearchError = (err) => {
|
||||
this.hint$.next('Artikel nicht gefunden');
|
||||
this.patchState({ query: '' });
|
||||
this.patchState({ query: '', fetching: false });
|
||||
};
|
||||
|
||||
fetchAvailability = this.effect((item$: Observable<ItemDTO>) =>
|
||||
@@ -109,7 +116,7 @@ export class KulturpassOrderSearchboxStore extends ComponentStore<KulturpassOrde
|
||||
handleAvailabilityResponse = (item: ItemDTO) => (availability: AvailabilityDTO) => {
|
||||
if (!this._availabilityService.isAvailable({ availability }) || availability.inStock < 1) {
|
||||
this.hint$.next('Artikel nicht vorrätig');
|
||||
this.patchState({ query: '' });
|
||||
this.patchState({ query: '', fetching: false });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -131,11 +138,11 @@ export class KulturpassOrderSearchboxStore extends ComponentStore<KulturpassOrde
|
||||
|
||||
this.addToShoppingCart$.next(addToShoppingCartDTO);
|
||||
|
||||
this.patchState({ query: '' });
|
||||
this.patchState({ query: '', fetching: false });
|
||||
};
|
||||
|
||||
handleAvailabilityError = (err) => {
|
||||
this.hint$.next('Artikel nicht vorrätig');
|
||||
this.patchState({ query: '' });
|
||||
this._modal.error('Fehler beim Laden der Verfügbarkeit', err);
|
||||
this.patchState({ query: '', fetching: false });
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user