Merged PR 2018: add reward points (Prämie) display and label

Related work items: #5413
This commit is contained in:
Lorenz Hilpert
2025-11-11 09:48:26 +00:00
committed by Nino Righi
parent 9c989055cb
commit 4a7b74a6c5
10 changed files with 410 additions and 308 deletions

View File

@@ -1,113 +1,151 @@
<div class="page-customer-order-item-list-item__card-content grid grid-cols-[6rem_1fr] gap-4">
<div>
<img class="rounded shadow mx-auto w-[5.9rem]" [src]="orderItem?.product?.ean | productImage" [alt]="orderItem?.product?.name" />
</div>
<div class="grid grid-flow-row gap-2">
<div class="grid grid-flow-col justify-between items-end">
<span>{{ orderItem.product?.contributors }}</span>
@if (orderDetailsHistoryRoute$ | async; as orderDetailsHistoryRoute) {
<a
[routerLink]="orderDetailsHistoryRoute.path"
[queryParams]="orderDetailsHistoryRoute.urlTree.queryParams"
[queryParamsHandling]="'merge'"
class="text-brand font-bold text-xl"
>
Historie
</a>
}
</div>
<div class="font-bold text-lg">
{{ orderItem?.product?.name }}
</div>
<div>
<span class="isa-label">
{{ processingStatus$ | async | orderItemProcessingStatus }}
</span>
</div>
@if (orderItemSubsetItem$ | async; as orderItemSubsetItem) {
<div class="grid grid-flow-row gap-2">
<div class="col-data">
<div class="col-label">Menge</div>
<div class="col-value">{{ orderItem?.quantity?.quantity }}x</div>
</div>
<div class="col-data">
<div class="col-label">Format</div>
<div class="col-value grid-flow-col grid gap-3 items-center justify-start">
<shared-icon [icon]="orderItem?.product?.format"></shared-icon>
<span>{{ orderItem?.product?.formatDetail }}</span>
</div>
</div>
<div class="col-data">
<div class="col-label">ISBN/EAN</div>
<div class="col-value">{{ orderItem?.product?.ean }}</div>
</div>
<div class="col-data">
<div class="col-label">Preis</div>
<div class="col-value">{{ orderItem?.unitPrice?.value?.value | currency: orderItem?.unitPrice?.value?.currency : 'code' }}</div>
</div>
<div class="col-data">
<div class="col-label">MwSt</div>
<div class="col-value">{{ orderItem?.unitPrice?.vat?.inPercent }}%</div>
</div>
<hr />
<div class="col-data">
<div class="col-label">Lieferant</div>
<div class="col-value">
{{ orderItemSubsetItem?.supplier?.data?.name }}
</div>
</div>
<div class="col-data">
<div class="col-label">Meldenummer</div>
<div class="col-value">{{ orderItemSubsetItem?.ssc }} - {{ orderItemSubsetItem?.sscText }}</div>
</div>
<div class="col-data">
<div class="col-label">Vsl. Lieferdatum</div>
<div class="col-value">
{{ orderItemSubsetItem?.estimatedShippingDate | date: 'dd.MM.yyyy' }}
</div>
</div>
@if (orderItemSubsetItem?.preferredPickUpDate) {
<div class="col-data">
<div class="col-label">Zurücklegen bis</div>
<div class="col-value">
{{ orderItemSubsetItem?.preferredPickUpDate | date: 'dd.MM.yyyy' }}
</div>
</div>
}
<hr />
@if (orderItemSubsetItem?.compartmentCode) {
<div class="col-data">
<div class="col-label">Abholfachnummer</div>
<div class="col-value">
<span>{{ orderItemSubsetItem?.compartmentCode }}</span>
@if (orderItemSubsetItem?.compartmentInfo) {
<span>_{{ orderItemSubsetItem?.compartmentInfo }}</span>
}
</div>
</div>
}
<div class="col-data">
<div class="col-label">Vormerker</div>
<div class="col-value">{{ isPrebooked$ | async }}</div>
</div>
<hr />
<div class="col-data">
<div class="col-label">Zahlungsweg</div>
<div class="col-value">-</div>
</div>
<div class="col-data">
<div class="col-label">Zahlungsart</div>
<div class="col-value">
{{ orderPaymentType$ | async | paymentType }}
</div>
</div>
<div class="col-data">
<div class="col-label">Anmerkung</div>
<div class="col-value">
{{ orderItemSubsetItem?.specialComment || '-' }}
</div>
</div>
</div>
}
</div>
</div>
<div
class="page-customer-order-item-list-item__card-content grid grid-cols-[6rem_1fr] gap-4"
>
<div class="flex flex-col gap-2 justify-start items-center">
@let ean = orderItem?.product?.ean;
@let name = orderItem?.product?.name;
@if (ean && name) {
<img
class="rounded shadow mx-auto w-[5.9rem]"
[src]="ean | productImage"
[alt]="name"
/>
}
@if (hasRewardPoints$ | async) {
<ui-label [type]="Labeltype.Tag" [priority]="LabelPriority.High">
Prämie
</ui-label>
}
</div>
<div class="grid grid-flow-row gap-2">
<div class="grid grid-flow-col justify-between items-end">
<span>{{ orderItem.product?.contributors }}</span>
@if (orderDetailsHistoryRoute$ | async; as orderDetailsHistoryRoute) {
<a
[routerLink]="orderDetailsHistoryRoute.path"
[queryParams]="orderDetailsHistoryRoute.urlTree.queryParams"
[queryParamsHandling]="'merge'"
class="text-brand font-bold text-xl"
>
Historie
</a>
}
</div>
<div class="font-bold text-lg">
{{ orderItem?.product?.name }}
</div>
<div>
<span class="isa-label">
{{ processingStatus$ | async | orderItemProcessingStatus }}
</span>
</div>
@if (orderItemSubsetItem$ | async; as orderItemSubsetItem) {
<div class="grid grid-flow-row gap-2">
<div class="col-data">
<div class="col-label">Menge</div>
<div class="col-value">{{ orderItem?.quantity?.quantity }}x</div>
</div>
<div class="col-data">
<div class="col-label">Format</div>
<div
class="col-value grid-flow-col grid gap-3 items-center justify-start"
>
@let format = orderItem?.product?.format;
@if (format) {
<shared-icon [icon]="orderItem?.product?.format"></shared-icon>
}
<span>{{ orderItem?.product?.formatDetail }}</span>
</div>
</div>
<div class="col-data">
<div class="col-label">ISBN/EAN</div>
<div class="col-value">{{ orderItem?.product?.ean }}</div>
</div>
<div class="col-data">
@if (hasRewardPoints$ | async) {
<div class="col-label">Prämie</div>
<div class="col-value">{{ rewardPoints$ | async | number: '1.0-0' }} Lesepunkte</div>
} @else {
<div class="col-label">Preis</div>
<div class="col-value">
{{
orderItem?.unitPrice?.value?.value
| currency: orderItem?.unitPrice?.value?.currency : 'code'
}}
</div>
}
</div>
<div class="col-data">
<div class="col-label">MwSt</div>
<div class="col-value">
{{ orderItem?.unitPrice?.vat?.inPercent }}%
</div>
</div>
<hr />
<div class="col-data">
<div class="col-label">Lieferant</div>
<div class="col-value">
{{ orderItemSubsetItem?.supplier?.data?.name }}
</div>
</div>
<div class="col-data">
<div class="col-label">Meldenummer</div>
<div class="col-value">
{{ orderItemSubsetItem?.ssc }} - {{ orderItemSubsetItem?.sscText }}
</div>
</div>
<div class="col-data">
<div class="col-label">Vsl. Lieferdatum</div>
<div class="col-value">
{{
orderItemSubsetItem?.estimatedShippingDate | date: 'dd.MM.yyyy'
}}
</div>
</div>
@if (orderItemSubsetItem?.preferredPickUpDate) {
<div class="col-data">
<div class="col-label">Zurücklegen bis</div>
<div class="col-value">
{{
orderItemSubsetItem?.preferredPickUpDate | date: 'dd.MM.yyyy'
}}
</div>
</div>
}
<hr />
@if (orderItemSubsetItem?.compartmentCode) {
<div class="col-data">
<div class="col-label">Abholfachnummer</div>
<div class="col-value">
<span>{{ orderItemSubsetItem?.compartmentCode }}</span>
@if (orderItemSubsetItem?.compartmentInfo) {
<span>_{{ orderItemSubsetItem?.compartmentInfo }}</span>
}
</div>
</div>
}
<div class="col-data">
<div class="col-label">Vormerker</div>
<div class="col-value">{{ isPrebooked$ | async }}</div>
</div>
<hr />
<div class="col-data">
<div class="col-label">Zahlungsweg</div>
<div class="col-value">-</div>
</div>
<div class="col-data">
<div class="col-label">Zahlungsart</div>
<div class="col-value">
{{ orderPaymentType$ | async | paymentType }}
</div>
</div>
<div class="col-data">
<div class="col-label">Anmerkung</div>
<div class="col-value">
{{ orderItemSubsetItem?.specialComment || '-' }}
</div>
</div>
</div>
}
</div>
</div>

View File

@@ -1,88 +1,136 @@
import { AsyncPipe, CurrencyPipe, DatePipe } from '@angular/common';
import { Component, ChangeDetectionStrategy, Input, OnDestroy, OnInit, inject } from '@angular/core';
import { ActivatedRoute, RouterLink } from '@angular/router';
import { ProductImagePipe } from '@cdn/product-image';
import { OrderItemProcessingStatusPipe } from '@shared/pipes/order';
import { OrderItemDTO } from '@generated/swagger/oms-api';
import { BehaviorSubject, Subject, combineLatest } from 'rxjs';
import { map, takeUntil } from 'rxjs/operators';
import { CustomerSearchStore } from '../../store';
import { CustomerSearchNavigation } from '@shared/services/navigation';
import { PaymentTypePipe } from '@shared/pipes/customer';
@Component({
selector: 'page-customer-order-item-list-item',
templateUrl: 'order-item-list-item.component.html',
styleUrls: ['order-item-list-item.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush,
host: { class: 'page-customer-order-item-list-item' },
imports: [
AsyncPipe,
DatePipe,
ProductImagePipe,
CurrencyPipe,
RouterLink,
PaymentTypePipe,
OrderItemProcessingStatusPipe
],
})
export class CustomerOrderItemListItemComponent implements OnInit, OnDestroy {
private _activatedRoute = inject(ActivatedRoute);
private _store = inject(CustomerSearchStore);
private _navigation = inject(CustomerSearchNavigation);
private _onDestroy = new Subject<void>();
private _orderItemSub = new BehaviorSubject<OrderItemDTO>(undefined);
@Input()
get orderItem() {
return this._orderItemSub.getValue();
}
set orderItem(value: OrderItemDTO) {
this._orderItemSub.next(value);
}
orderId$ = this._activatedRoute.params.pipe(map((params) => Number(params.orderId)));
order$ = this._store.order$;
orderPaymentType$ = this.order$.pipe(map((order) => order?.paymentType));
customerId$ = this._activatedRoute.params.pipe(map((params) => Number(params.customerId)));
orderItemOrderType$ = this._orderItemSub.pipe(map((orderItem) => orderItem?.features?.orderType));
orderItemSubsetItem$ = this._orderItemSub.pipe(map((orderItem) => orderItem?.subsetItems?.[0]?.data));
orderDetailsHistoryRoute$ = combineLatest([
this.customerId$,
this._store.processId$,
this.orderId$,
this._orderItemSub,
]).pipe(
map(([customerId, processId, orderId, orderItem]) =>
this._navigation.orderDetailsHistoryRoute({ processId, customerId, orderId, orderItemId: orderItem?.id }),
),
);
isPrebooked$ = this.orderItemSubsetItem$.pipe(map((subsetItem) => (subsetItem?.isPrebooked ? 'Ja' : 'Nein')));
processingStatus$ = this.orderItemSubsetItem$.pipe(map((subsetItem) => subsetItem?.processingStatus));
ngOnInit() {
this.customerId$.pipe(takeUntil(this._onDestroy)).subscribe((customerId) => {
this._store.selectCustomer({ customerId });
});
this.orderId$.pipe(takeUntil(this._onDestroy)).subscribe((orderId) => {
this._store.selectOrder(+orderId);
});
}
ngOnDestroy() {
this._onDestroy.next();
this._onDestroy.complete();
this._orderItemSub.complete();
}
}
import {
AsyncPipe,
CurrencyPipe,
DatePipe,
DecimalPipe,
} from '@angular/common';
import {
Component,
ChangeDetectionStrategy,
Input,
OnDestroy,
OnInit,
inject,
} from '@angular/core';
import { ActivatedRoute, RouterLink } from '@angular/router';
import { ProductImagePipe } from '@cdn/product-image';
import { OrderItemProcessingStatusPipe } from '@shared/pipes/order';
import { OrderItemDTO } from '@generated/swagger/oms-api';
import { BehaviorSubject, Subject, combineLatest } from 'rxjs';
import { map, takeUntil } from 'rxjs/operators';
import { CustomerSearchStore } from '../../store';
import { CustomerSearchNavigation } from '@shared/services/navigation';
import { PaymentTypePipe } from '@shared/pipes/customer';
import { LabelComponent, Labeltype, LabelPriority } from '@isa/ui/label';
import { getOrderItemRewardFeature } from '@isa/oms/data-access';
import { IconComponent } from '@shared/components/icon';
@Component({
selector: 'page-customer-order-item-list-item',
templateUrl: 'order-item-list-item.component.html',
styleUrls: ['order-item-list-item.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush,
host: { class: 'page-customer-order-item-list-item' },
imports: [
AsyncPipe,
DatePipe,
ProductImagePipe,
CurrencyPipe,
RouterLink,
PaymentTypePipe,
OrderItemProcessingStatusPipe,
LabelComponent,
IconComponent,
DecimalPipe,
],
})
export class CustomerOrderItemListItemComponent implements OnInit, OnDestroy {
private _activatedRoute = inject(ActivatedRoute);
private _store = inject(CustomerSearchStore);
private _navigation = inject(CustomerSearchNavigation);
private _onDestroy = new Subject<void>();
private _orderItemSub = new BehaviorSubject<OrderItemDTO>(undefined);
@Input()
get orderItem() {
return this._orderItemSub.getValue();
}
set orderItem(value: OrderItemDTO) {
this._orderItemSub.next(value);
}
orderId$ = this._activatedRoute.params.pipe(
map((params) => Number(params.orderId)),
);
order$ = this._store.order$;
orderPaymentType$ = this.order$.pipe(map((order) => order?.paymentType));
customerId$ = this._activatedRoute.params.pipe(
map((params) => Number(params.customerId)),
);
orderItemOrderType$ = this._orderItemSub.pipe(
map((orderItem) => orderItem?.features?.orderType),
);
orderItemSubsetItem$ = this._orderItemSub.pipe(
map((orderItem) => orderItem?.subsetItems?.[0]?.data),
);
orderDetailsHistoryRoute$ = combineLatest([
this.customerId$,
this._store.processId$,
this.orderId$,
this._orderItemSub,
]).pipe(
map(([customerId, processId, orderId, orderItem]) =>
this._navigation.orderDetailsHistoryRoute({
processId,
customerId,
orderId,
orderItemId: orderItem?.id,
}),
),
);
isPrebooked$ = this.orderItemSubsetItem$.pipe(
map((subsetItem) => (subsetItem?.isPrebooked ? 'Ja' : 'Nein')),
);
processingStatus$ = this.orderItemSubsetItem$.pipe(
map((subsetItem) => subsetItem?.processingStatus),
);
hasRewardPoints$ = this._orderItemSub.pipe(
map((orderItem) => getOrderItemRewardFeature(orderItem) !== undefined),
);
rewardPoints$ = this._orderItemSub.pipe(
map((orderItem) => getOrderItemRewardFeature(orderItem)),
);
Labeltype = Labeltype;
LabelPriority = LabelPriority;
ngOnInit() {
this.customerId$
.pipe(takeUntil(this._onDestroy))
.subscribe((customerId) => {
this._store.selectCustomer({ customerId });
});
this.orderId$.pipe(takeUntil(this._onDestroy)).subscribe((orderId) => {
this._store.selectOrder(+orderId);
});
}
ngOnDestroy() {
this._onDestroy.next();
this._onDestroy.complete();
this._orderItemSub.complete();
}
}