mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
Compare commits
72 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c162c00c8d | ||
|
|
5997e67322 | ||
|
|
cfee8e571e | ||
|
|
b4a7d3c879 | ||
|
|
9c1565dfcc | ||
|
|
a8b8573734 | ||
|
|
c09e559430 | ||
|
|
cbc1b85d92 | ||
|
|
d6f3ae7179 | ||
|
|
12c87aaa25 | ||
|
|
67a0230b7b | ||
|
|
539a6d420e | ||
|
|
f1baa7c0d2 | ||
|
|
36ac3776b7 | ||
|
|
a6b50631dc | ||
|
|
71fdedb2d9 | ||
|
|
f4be47a0d7 | ||
|
|
295400892a | ||
|
|
5c3d28033f | ||
|
|
229cb55b46 | ||
|
|
99fea8c2df | ||
|
|
1d7fd4fab1 | ||
|
|
76e1b7d320 | ||
|
|
0d4ca1b6a6 | ||
|
|
9050f33fcc | ||
|
|
8a1b151740 | ||
|
|
f088098159 | ||
|
|
d9462a2d17 | ||
|
|
eec72499e7 | ||
|
|
93016af766 | ||
|
|
5d699dff8f | ||
|
|
12e4f48460 | ||
|
|
68ad3cd407 | ||
|
|
c19c4d471d | ||
|
|
9cf5f23b5a | ||
|
|
b71d383c74 | ||
|
|
5086c3c82d | ||
|
|
d98c30506e | ||
|
|
6a4e5a121f | ||
|
|
15a8d2151e | ||
|
|
025672bd91 | ||
|
|
a925050912 | ||
|
|
a4a4fc344a | ||
|
|
bc1fc27a5d | ||
|
|
acfd88ec55 | ||
|
|
3745583e1c | ||
|
|
60f1a89984 | ||
|
|
cbfa2fc98c | ||
|
|
7c37a0bb05 | ||
|
|
699745526e | ||
|
|
ef9573a74d | ||
|
|
30bdc0ee54 | ||
|
|
8c7217ad56 | ||
|
|
4f15bc257d | ||
|
|
4c25c5769a | ||
|
|
30a9225227 | ||
|
|
4a0d79b138 | ||
|
|
8856c984dc | ||
|
|
0198e635b3 | ||
|
|
f477411667 | ||
|
|
963aaa974e | ||
|
|
49bb23805e | ||
|
|
a1dea02498 | ||
|
|
e7eef7f169 | ||
|
|
072045ed59 | ||
|
|
9078c57909 | ||
|
|
abb15a9e28 | ||
|
|
2766751c79 | ||
|
|
d9df337ff0 | ||
|
|
5d2cfaf269 | ||
|
|
4a9b5ace01 | ||
|
|
e475ab6047 |
@@ -3,7 +3,7 @@ import { ItemDTO } from '@swagger/cat';
|
||||
import { AvailabilityDTO, BranchDTO, OLAAvailabilityDTO, StoreCheckoutService, SupplierDTO } from '@swagger/checkout';
|
||||
import { Observable } from 'rxjs';
|
||||
import { AvailabilityService as SwaggerAvailabilityService } from '@swagger/availability';
|
||||
import { map, shareReplay, switchMap, withLatestFrom, mergeMap } from 'rxjs/operators';
|
||||
import { map, shareReplay, switchMap, withLatestFrom, mergeMap, timeout } from 'rxjs/operators';
|
||||
import { isArray, memorize } from '@utils/common';
|
||||
import { OrderService } from '@swagger/oms';
|
||||
import { ResponseArgsOfIEnumerableOfStockInfoDTO, StockDTO, StockInfoDTO, StockService } from '@swagger/remi';
|
||||
@@ -184,7 +184,9 @@ export class DomainAvailabilityService {
|
||||
estimatedShippingDate: preferred?.at,
|
||||
price: preferred?.price,
|
||||
inStock: totalAvailable,
|
||||
supplierProductNumber: preferred.supplierProductNumber,
|
||||
supplierProductNumber: preferred?.supplierProductNumber,
|
||||
supplierInfo: preferred?.requestStatusCode,
|
||||
lastRequest: preferred?.requested,
|
||||
};
|
||||
return availability;
|
||||
}
|
||||
@@ -205,6 +207,7 @@ export class DomainAvailabilityService {
|
||||
},
|
||||
])
|
||||
.pipe(
|
||||
timeout(5000),
|
||||
map((r) => {
|
||||
const availabilities = r.result;
|
||||
const preferred = availabilities.find((f) => f.preferred === 1);
|
||||
@@ -216,7 +219,9 @@ export class DomainAvailabilityService {
|
||||
isPrebooked: preferred?.isPrebooked,
|
||||
estimatedShippingDate: preferred?.at,
|
||||
price: preferred?.price,
|
||||
supplierProductNumber: preferred.supplierProductNumber,
|
||||
supplierProductNumber: preferred?.supplierProductNumber,
|
||||
supplierInfo: preferred?.requestStatusCode,
|
||||
lastRequest: preferred?.requested,
|
||||
};
|
||||
return availability;
|
||||
}),
|
||||
@@ -236,6 +241,7 @@ export class DomainAvailabilityService {
|
||||
},
|
||||
])
|
||||
.pipe(
|
||||
timeout(5000),
|
||||
map((r) => {
|
||||
const availabilities = r.result;
|
||||
const preferred = availabilities.find((f) => f.preferred === 1);
|
||||
@@ -249,7 +255,9 @@ export class DomainAvailabilityService {
|
||||
estimatedShippingDate: preferred?.at,
|
||||
price: preferred?.price,
|
||||
logistician: { id: preferred?.logisticianId },
|
||||
supplierProductNumber: preferred.supplierProductNumber,
|
||||
supplierProductNumber: preferred?.supplierProductNumber,
|
||||
supplierInfo: preferred?.requestStatusCode,
|
||||
lastRequest: preferred?.requested,
|
||||
};
|
||||
return availability;
|
||||
}),
|
||||
@@ -266,6 +274,7 @@ export class DomainAvailabilityService {
|
||||
const currentBranch$ = this.getCurrentBranch();
|
||||
|
||||
return currentBranch$.pipe(
|
||||
timeout(5000),
|
||||
mergeMap((branch) =>
|
||||
this.getPickUpAvailability({ item, quantity, branch }).pipe(
|
||||
mergeMap((availability) => logistician$.pipe(map((logistician) => ({ ...availability, logistician: { id: logistician.id } })))),
|
||||
@@ -299,8 +308,10 @@ export class DomainAvailabilityService {
|
||||
isPrebooked: preferred?.isPrebooked,
|
||||
estimatedShippingDate: preferred?.at,
|
||||
price: preferred?.price,
|
||||
supplierProductNumber: preferred.supplierProductNumber,
|
||||
supplierProductNumber: preferred?.supplierProductNumber,
|
||||
logistician: { id: preferred?.logisticianId },
|
||||
supplierInfo: preferred?.requestStatusCode,
|
||||
lastRequest: preferred?.requested,
|
||||
};
|
||||
return availability;
|
||||
}),
|
||||
|
||||
@@ -395,11 +395,20 @@ export class DomainCheckoutService {
|
||||
|
||||
return availability$.pipe(
|
||||
mergeMap((availability) => {
|
||||
let updatedAvailability = availability;
|
||||
// Bei Preisupdate der Versandoptionen soll immer der Preis genommen werden, der im Warenkorb steht
|
||||
if (orderType === 'DIG-Versand' || orderType === 'B2B-Versand') {
|
||||
const itemPrice = itemToUpdate.data.availability.price;
|
||||
updatedAvailability = {
|
||||
...availability,
|
||||
price: itemPrice,
|
||||
};
|
||||
}
|
||||
return this.updateItemInShoppingCart({
|
||||
processId,
|
||||
shoppingCartItemId: itemToUpdate.id,
|
||||
update: {
|
||||
availability,
|
||||
availability: updatedAvailability,
|
||||
},
|
||||
});
|
||||
})
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActionHandler } from '@core/command';
|
||||
import { OrderService } from '@swagger/oms';
|
||||
import { OrderItemsContext } from './order-items.context';
|
||||
|
||||
@Injectable()
|
||||
export class CollectOnDeliveryNoteActionHandler extends ActionHandler<OrderItemsContext> {
|
||||
constructor(private orderService: OrderService) {
|
||||
super('COLLECT_ON_DELIVERYNOTE');
|
||||
}
|
||||
|
||||
async handler(data: OrderItemsContext): Promise<OrderItemsContext> {
|
||||
const orderItemSubsetIds = data.items.map((item) => item.orderItemSubsetId);
|
||||
|
||||
const response = await this.orderService
|
||||
.OrderCollectOnDeliveryNote({
|
||||
data: orderItemSubsetIds,
|
||||
})
|
||||
.toPromise();
|
||||
|
||||
// Für korrekte Navigation nach Aufruf, da ProcessingStatus Serverseitig auf abgeholt gesetzt wird
|
||||
data.items?.forEach((i) => (i.processingStatus = 256));
|
||||
|
||||
return {
|
||||
...data,
|
||||
receipts: response.result,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -34,4 +34,5 @@ export * from './reserved.action-handler';
|
||||
export * from './returned-by-buyer.action-handler';
|
||||
export * from './shipping-note.action-handler';
|
||||
export * from './supplier-temporarily-out-of-stock.action-handler copy';
|
||||
export * from './collect-on-deliverynote.action-handler';
|
||||
// end:ng42.barrel
|
||||
|
||||
@@ -202,20 +202,25 @@ export class DomainOmsService {
|
||||
orderId,
|
||||
orderItemId,
|
||||
orderItemSubsetId,
|
||||
take,
|
||||
skip,
|
||||
}: {
|
||||
orderId: number;
|
||||
orderItemId: number;
|
||||
orderItemSubsetId: number;
|
||||
}): Observable<Record<string, Date>> {
|
||||
take?: number;
|
||||
skip?: number;
|
||||
}): Observable<Record<string, Date[]>> {
|
||||
return this.orderService
|
||||
.OrderGetOrderItemSubsetTasks({ orderId, orderItemId, orderItemSubsetId, completed: new Date(0).toISOString() })
|
||||
.OrderGetOrderItemSubsetTasks({ orderId, orderItemId, orderItemSubsetId, completed: new Date(0).toISOString(), take, skip })
|
||||
.pipe(
|
||||
map((res) =>
|
||||
res.result.reduce((data, result) => {
|
||||
data[result.name] = new Date(result.completed);
|
||||
|
||||
return data;
|
||||
}, {} as Record<string, Date>)
|
||||
res.result
|
||||
.sort((a, b) => new Date(b.completed).getTime() - new Date(a.completed).getTime())
|
||||
.reduce((data, result) => {
|
||||
(data[result.name] = data[result.name] || []).push(new Date(result.completed));
|
||||
return data;
|
||||
}, {} as Record<string, Date[]>)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,13 +2,20 @@
|
||||
<div class="customer">
|
||||
<div class="row">
|
||||
<span class="label">Kundenname</span>
|
||||
<strong *ngIf="ref.data.item.organisation">{{ ref.data.item.organisation }}, </strong>
|
||||
<strong>{{ ref.data.item.lastName }}, {{ ref.data.item.firstName }}</strong>
|
||||
<ng-container *ngIf="ref.data.mode === 'customer' || ref.data.mode === 'goods'">
|
||||
<strong *ngIf="ref.data.item.organisation">{{ ref.data.item.organisation }}, </strong>
|
||||
<strong>{{ ref.data.item.lastName }}, {{ ref.data.item.firstName }}</strong>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="ref.data.mode === 'order'">
|
||||
<strong *ngIf="ref.data.item.order.buyer.organisation">{{ ref.data.item.order.buyer.organisation }}, </strong>
|
||||
<strong>{{ ref.data.item.order.buyer.lastName }}, {{ ref.data.item.order.buyer.firstName }}</strong>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="label">Kundennummer</span>
|
||||
<strong *ngIf="ref.data.mode === 'customer'">{{ ref.data.item.customerNumber }}</strong>
|
||||
<strong *ngIf="ref.data.mode === 'goods'">{{ ref.data.item.buyerNumber }}</strong>
|
||||
<strong *ngIf="ref.data.mode === 'order'">{{ ref.data.item.order.buyer.buyerNumber }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
@@ -22,7 +29,7 @@
|
||||
<div *ngIf="history.changedBy">| {{ history.changedBy }}</div>
|
||||
</div>
|
||||
<div class="row details title">
|
||||
<ng-container *ngIf="ref?.data?.mode === 'goods'; else customer">
|
||||
<ng-container *ngIf="ref?.data?.mode === 'goods' || ref?.data?.mode === 'order'; else customer">
|
||||
Status der Bestellung wurde geändert
|
||||
</ng-container>
|
||||
<ng-template #customer>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { CrmCustomerService } from '@domain/crm';
|
||||
import { DomainOmsService } from '@domain/oms';
|
||||
import { CustomerDTO } from '@swagger/crm';
|
||||
import { HistoryDTO as CrmHistoryDTO } from '@swagger/crm';
|
||||
import { HistoryDTO as OmsHistoryDTO } from '@swagger/oms';
|
||||
import { HistoryDTO as OmsHistoryDTO, OrderDTO, OrderItemDTO } from '@swagger/oms';
|
||||
import { OrderItemListItemDTO } from '@swagger/oms';
|
||||
import { UiModalRef } from '@ui/modal';
|
||||
import { BehaviorSubject, Observable, of } from 'rxjs';
|
||||
@@ -23,7 +23,10 @@ export class HistoryComponent implements OnInit {
|
||||
history$: Observable<CrmHistoryDTO | OmsHistoryDTO>;
|
||||
|
||||
constructor(
|
||||
public ref: UiModalRef<any, { mode: 'goods' | 'customer'; item: CustomerDTO | OrderItemListItemDTO }>,
|
||||
public ref: UiModalRef<
|
||||
any,
|
||||
{ mode: 'goods' | 'customer' | 'order'; item: CustomerDTO | OrderItemListItemDTO | { order: OrderDTO; orderItemSubsetId: number } }
|
||||
>,
|
||||
private customerService: CrmCustomerService,
|
||||
private omsService: DomainOmsService
|
||||
) {}
|
||||
@@ -62,6 +65,24 @@ export class HistoryComponent implements OnInit {
|
||||
})
|
||||
);
|
||||
break;
|
||||
case 'order':
|
||||
this.history$ = this.omsService
|
||||
.getHistory((this.ref.data.item as { order: OrderDTO; orderItemSubsetId: number }).orderItemSubsetId)
|
||||
.pipe(
|
||||
map((history) => {
|
||||
this.spinner$.next(false);
|
||||
if ((history as any).length === 0) {
|
||||
this.historyEmpty$.next(true);
|
||||
}
|
||||
return history;
|
||||
}),
|
||||
catchError(() => {
|
||||
this.historyError$.next(true);
|
||||
this.spinner$.next(false);
|
||||
return of();
|
||||
})
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
Nicht lieferbar
|
||||
</ui-spinner>
|
||||
</button>
|
||||
<button class="cta-reorder cta-action-primary" [disabled]="ctaDisabled$ | async" (click)="reorder()">
|
||||
<button class="cta-reorder cta-action-primary" [disabled]="ctaReorderDisabled$ | async" (click)="reorder()">
|
||||
<ui-spinner [show]="ctaDisabled$ | async">
|
||||
Bestellen
|
||||
</ui-spinner>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { ComponentStore } from '@ngrx/component-store';
|
||||
import { AvailabilityDTO2, OrderItemListItemDTO } from '@swagger/oms';
|
||||
import { UiModalRef } from '@ui/modal';
|
||||
import { combineLatest } from 'rxjs';
|
||||
import { catchError, map, switchMap, tap } from 'rxjs/operators';
|
||||
import { catchError, map, shareReplay, switchMap, tap } from 'rxjs/operators';
|
||||
import { ReorderResult } from './reorder.result';
|
||||
|
||||
interface GoodsInListReorderModalState {
|
||||
@@ -37,6 +37,8 @@ export class ReorderModalComponent extends ComponentStore<GoodsInListReorderModa
|
||||
}
|
||||
}
|
||||
|
||||
readonly checkedAvailability$ = this.select((s) => s.checkedAvailability);
|
||||
|
||||
get ctaDisabled() {
|
||||
return this.get((s) => s.ctaDisabled);
|
||||
}
|
||||
@@ -47,8 +49,20 @@ export class ReorderModalComponent extends ComponentStore<GoodsInListReorderModa
|
||||
}
|
||||
readonly ctaDisabled$ = this.select((s) => s.ctaDisabled);
|
||||
|
||||
readonly storeAvailabilityError$ = this.select((s) => s.storeAvailabilityError);
|
||||
readonly takeAwayAvailabilityError$ = this.select((s) => s.takeAwayAvailabilityError);
|
||||
readonly storeAvailabilityError$ = this.select((s) => s.storeAvailabilityError).pipe(shareReplay());
|
||||
readonly takeAwayAvailabilityError$ = this.select((s) => s.takeAwayAvailabilityError).pipe(shareReplay());
|
||||
|
||||
readonly ctaReorderDisabled$ = combineLatest([
|
||||
this.ctaDisabled$,
|
||||
this.storeAvailabilityError$,
|
||||
this.takeAwayAvailabilityError$,
|
||||
this.checkedAvailability$,
|
||||
]).pipe(
|
||||
map(
|
||||
([ctaDisabled, storeAvailabilityError, takeAwayAvailabilityError, checkedAvailability]) =>
|
||||
ctaDisabled || !checkedAvailability || (storeAvailabilityError && takeAwayAvailabilityError)
|
||||
)
|
||||
);
|
||||
|
||||
readonly currentBranch$ = this.domainAvailabilityService.getCurrentBranch();
|
||||
|
||||
|
||||
@@ -1,14 +1,25 @@
|
||||
<div #detailsContainer class="product-card">
|
||||
<ng-container *ngIf="store.item$ | async; let item">
|
||||
<div class="product-details">
|
||||
<div class="bookmark" *ngIf="item?.features && (item?.features)[0]?.key === 'PFO'">
|
||||
<button class="bookmark-badge" (click)="showTooltip = !showTooltip">
|
||||
<img src="/assets/images/bookmark_subscription.svg" alt="Fortsetzungsartikel Badge" />
|
||||
</button>
|
||||
<page-tooltip *ngIf="showTooltip" (close)="showTooltip = false"
|
||||
>Dieser Artikel ist nur über die Abo <br />
|
||||
Abteilung bestellbar.</page-tooltip
|
||||
>
|
||||
<div class="bookmark">
|
||||
<div *ngIf="showSubscriptionBadge$ | async">
|
||||
<button [uiOverlayTrigger]="subscribtionTooltip" class="bookmark-badge">
|
||||
<img src="/assets/images/bookmark_subscription.svg" alt="Fortsetzungsartikel Badge" />
|
||||
</button>
|
||||
<ui-tooltip yPosition="above" xPosition="after" [yOffset]="-11" [xOffset]="-8" #subscribtionTooltip [closeable]="true"
|
||||
>Artikel ist ein Fortsetzungsartikel,<br />
|
||||
Artikel muss über eine Aboabteilung<br />
|
||||
bestellt werden.
|
||||
</ui-tooltip>
|
||||
</div>
|
||||
<div *ngIf="showPromotionBadge$ | async" class="promotion-badge">
|
||||
<button [uiOverlayTrigger]="promotionTooltip" class="bookmark-badge">
|
||||
<ui-icon-badge icon="gift" alt="Prämienkatalog Badge"></ui-icon-badge>
|
||||
<ui-tooltip yPosition="above" xPosition="after" [yOffset]="-11" [xOffset]="-8" #promotionTooltip [closeable]="true">
|
||||
Dieser Artikel befindet sich im Prämienkatalog.
|
||||
</ui-tooltip>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="product-image">
|
||||
@@ -25,7 +36,7 @@
|
||||
</div>
|
||||
|
||||
<div class="product-info">
|
||||
<div class="row" [class.bookmark-badge-gap]="item?.features && (item?.features)[0]?.key === 'PFO'">
|
||||
<div class="row" [class.bookmark-badge-gap]="isBadgeVisible$ | async">
|
||||
<div>
|
||||
<a
|
||||
*ngFor="let contributor of contributors$ | async; let last = last"
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
@apply flex flex-row p-5;
|
||||
|
||||
.bookmark {
|
||||
@apply absolute;
|
||||
@apply flex absolute;
|
||||
right: 37px;
|
||||
top: 57px;
|
||||
}
|
||||
@@ -20,6 +20,10 @@
|
||||
@apply p-0 m-0 outline-none border-none bg-transparent relative;
|
||||
}
|
||||
|
||||
.promotion-badge {
|
||||
margin-top: -1px;
|
||||
}
|
||||
|
||||
.bookmark-badge-gap {
|
||||
@apply mt-px-35;
|
||||
}
|
||||
@@ -258,9 +262,3 @@
|
||||
.autor {
|
||||
@apply text-active-customer !important font-bold no-underline;
|
||||
}
|
||||
|
||||
page-tooltip {
|
||||
@apply relative;
|
||||
bottom: 35px;
|
||||
left: 15px;
|
||||
}
|
||||
|
||||
@@ -57,8 +57,15 @@ export class ArticleDetailsComponent implements OnInit, OnDestroy {
|
||||
this.store.isDeliveryB2BAvailabilityAvailable$,
|
||||
]).pipe(map(([digDelivery, b2bDelivery]) => b2bDelivery && !digDelivery));
|
||||
|
||||
showSubscriptionBadge$ = this.store.item$.pipe(map((item) => item?.features?.find((i) => i.key === 'PFO')));
|
||||
|
||||
showPromotionBadge$ = this.store.item$.pipe(map((item) => item?.features?.find((i) => i.key === 'Promotion')));
|
||||
|
||||
isBadgeVisible$ = combineLatest([this.showSubscriptionBadge$, this.showPromotionBadge$]).pipe(
|
||||
map(([showSubscriptionBadge, showPromotionBadge]) => showSubscriptionBadge || showPromotionBadge)
|
||||
);
|
||||
|
||||
contributors$ = this.store.item$.pipe(map((item) => item?.product?.contributors?.split(';').map((m) => m.trim())));
|
||||
showTooltip = false;
|
||||
|
||||
constructor(
|
||||
private applicationService: ApplicationService,
|
||||
|
||||
@@ -7,11 +7,22 @@ import { RouterModule } from '@angular/router';
|
||||
import { UiStarsModule } from '@ui/stars';
|
||||
import { UiSliderModule } from '@ui/slider';
|
||||
import { ArticleRecommendationsComponent } from './recommendations/article-recommendations.component';
|
||||
import { TooltipModule } from '../shared/tooltip/tooltip.module';
|
||||
import { PipesModule } from '../shared/pipes/pipes.module';
|
||||
import { UiTooltipModule } from '@ui/tooltip';
|
||||
import { UiCommonModule } from '@ui/common';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, ProductImageModule, UiIconModule, RouterModule, UiStarsModule, UiSliderModule, TooltipModule, PipesModule],
|
||||
imports: [
|
||||
CommonModule,
|
||||
ProductImageModule,
|
||||
UiIconModule,
|
||||
RouterModule,
|
||||
UiStarsModule,
|
||||
UiSliderModule,
|
||||
UiCommonModule,
|
||||
UiTooltipModule,
|
||||
PipesModule,
|
||||
],
|
||||
exports: [ArticleDetailsComponent, ArticleRecommendationsComponent],
|
||||
declarations: [ArticleDetailsComponent, ArticleRecommendationsComponent],
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<button class="filter" [class.positionIpad]="isIpad" [class.active]="hasFilter$ | async" (click)="toggleFilterOverlay()">
|
||||
<button class="filter" [class.active]="hasFilter$ | async" (click)="toggleFilterOverlay()">
|
||||
<ui-icon size="20px" icon="filter_alit"></ui-icon>
|
||||
<span class="label">Filter</span>
|
||||
</button>
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
:host {
|
||||
@apply flex flex-col w-full box-content absolute;
|
||||
}
|
||||
|
||||
.filter {
|
||||
@apply absolute font-sans flex items-center font-bold bg-inactive-customer border-0 text-regular py-px-8 px-px-15 rounded-filter justify-center right-0 top-px-10;
|
||||
@apply absolute font-sans flex items-center font-bold bg-inactive-customer border-0 text-regular py-px-8 px-px-15 rounded-filter justify-center right-0;
|
||||
min-width: 106px;
|
||||
top: -52px;
|
||||
|
||||
.label {
|
||||
@apply ml-px-5;
|
||||
@@ -11,10 +16,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.positionIpad {
|
||||
@apply top-0 !important;
|
||||
}
|
||||
|
||||
page-article-search-filter {
|
||||
@apply fixed right-0 bottom-0 left-0 z-fixed;
|
||||
top: 136px;
|
||||
|
||||
@@ -5,7 +5,6 @@ import { ApplicationService } from '@core/application';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { UiFilterAutocompleteProvider, UiFilterScanProvider } from '@ui/filter';
|
||||
import { UiFilter } from 'apps/ui/filter/src/lib/next';
|
||||
import { NativeContainerService } from 'native-container';
|
||||
import { Observable, Subscription } from 'rxjs';
|
||||
import { filter, first, map } from 'rxjs/operators';
|
||||
import { ArticleSearchService } from './article-search.store';
|
||||
@@ -43,8 +42,6 @@ export class ArticleSearchComponent implements OnInit, OnDestroy {
|
||||
|
||||
subscriptions = new Subscription();
|
||||
|
||||
isIpad = this.nativeContainer.isUiWebview()?.isNative;
|
||||
|
||||
hasFilter$: Observable<boolean> = this.articleSearch.filter$.pipe(
|
||||
filter((filter: UiFilter) => !!filter?.getQueryToken()?.filter),
|
||||
map((filter: UiFilter) => {
|
||||
@@ -56,8 +53,7 @@ export class ArticleSearchComponent implements OnInit, OnDestroy {
|
||||
private breadcrumb: BreadcrumbService,
|
||||
private application: ApplicationService,
|
||||
private router: Router,
|
||||
private articleSearch: ArticleSearchService,
|
||||
private nativeContainer: NativeContainerService
|
||||
private articleSearch: ArticleSearchService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
|
||||
@@ -49,14 +49,14 @@
|
||||
</ui-searchbox-autocomplete>
|
||||
</ui-searchbox>
|
||||
<ng-container *ngIf="isFilter$ | async">
|
||||
<button class="info-tooltip-button" (click)="showTooltip = !showTooltip" type="button">
|
||||
<button class="info-tooltip-button" [uiOverlayTrigger]="infoTooltip" type="button">
|
||||
i
|
||||
</button>
|
||||
<page-tooltip *ngIf="showTooltip" (close)="showTooltip = false"
|
||||
<ui-tooltip yPosition="above" xPosition="after" [yOffset]="-11" [xOffset]="-8" #infoTooltip [closeable]="true"
|
||||
>Um Begriffe auszuschließen, geben Sie diese <br />
|
||||
mit einem ! oder ~ davor in das Suchfeld ein. <br />
|
||||
Um exakt einen Begriff zu suchen, wählen <br />
|
||||
Sie „Exakt“ aus.</page-tooltip
|
||||
Sie „Exakt“ aus.</ui-tooltip
|
||||
>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
@@ -5,12 +5,12 @@ import { UiCommonModule } from '@ui/common';
|
||||
import { UiIconModule } from '@ui/icon';
|
||||
import { UiInputModule } from '@ui/input';
|
||||
import { UiSearchboxModule } from '@ui/searchbox';
|
||||
import { TooltipModule } from '../../../shared/tooltip/tooltip.module';
|
||||
import { UiTooltipModule } from '@ui/tooltip';
|
||||
|
||||
import { ArticleSearchboxComponent } from './article-searchbox.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, FormsModule, UiIconModule, UiSearchboxModule, UiInputModule, UiCommonModule, TooltipModule],
|
||||
imports: [CommonModule, FormsModule, UiIconModule, UiSearchboxModule, UiInputModule, UiCommonModule, UiTooltipModule],
|
||||
exports: [ArticleSearchboxComponent],
|
||||
declarations: [ArticleSearchboxComponent],
|
||||
providers: [],
|
||||
|
||||
@@ -6,7 +6,7 @@ import { catchError, map } from 'rxjs/operators';
|
||||
|
||||
@Injectable()
|
||||
export class ArticleSearchMainAutocompleteProvider extends UiFilterAutocompleteProvider {
|
||||
for = 'main';
|
||||
for = 'catalog';
|
||||
|
||||
constructor(private domainCatalogSearch: DomainCatalogService) {
|
||||
super();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { UiFilterScanProvider } from '@ui/filter';
|
||||
import { NativeContainerService } from 'native-container';
|
||||
import { NEVER, Observable, of } from 'rxjs';
|
||||
import { catchError, delay, filter, map, mergeMap, tap } from 'rxjs/operators';
|
||||
import { Observable } from 'rxjs';
|
||||
import { catchError, filter, map } from 'rxjs/operators';
|
||||
|
||||
@Injectable()
|
||||
export class ArticleSearchMainScanProviderService extends UiFilterScanProvider {
|
||||
|
||||
@@ -6,10 +6,15 @@
|
||||
|
||||
<div class="catalog-search-filter-content-main">
|
||||
<h1 class="title">Filter</h1>
|
||||
<ui-filter [filter]="filter" [loading]="fetching$ | async" (search)="applyFilter(filter)" [hint]="searchboxHint$ | async"></ui-filter>
|
||||
<ui-filter
|
||||
[filter]="filter"
|
||||
[loading]="fetching$ | async"
|
||||
(search)="applyFilter(filter)"
|
||||
[hint]="searchboxHint$ | async"
|
||||
resizeInputOptionsToElement="page-article-search-filter .cta-wrapper"
|
||||
></ui-filter>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cta-wrapper">
|
||||
<button class="cta-reset-filter" (click)="resetFilter(filter)">
|
||||
Filter zurücksetzen
|
||||
|
||||
@@ -17,10 +17,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
::ng-deep ui-filter-input-options .input-options {
|
||||
max-height: calc(100vh - 555px);
|
||||
}
|
||||
|
||||
.cta-wrapper {
|
||||
@apply fixed bottom-8 whitespace-nowrap;
|
||||
left: 50%;
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
<div (click)="close.emit(true)" class="backdrop"></div>
|
||||
<div class="info-tooltip-box">
|
||||
<button class="close-btn" (click)="close.emit(true)">
|
||||
<ui-icon icon="close" size="15px"></ui-icon>
|
||||
</button>
|
||||
<div class="info-tooltip">
|
||||
<p>
|
||||
<ng-content></ng-content>
|
||||
</p>
|
||||
</div>
|
||||
<div class="info-tooltip-triangle"></div>
|
||||
</div>
|
||||
@@ -1,37 +0,0 @@
|
||||
:host {
|
||||
@apply flex self-center;
|
||||
}
|
||||
|
||||
.backdrop {
|
||||
@apply fixed w-screen h-screen z-tooltip left-0 top-0;
|
||||
}
|
||||
|
||||
.info-tooltip-box {
|
||||
@apply absolute z-modal;
|
||||
|
||||
.close-btn {
|
||||
@apply bg-transparent border-none text-white relative cursor-pointer z-modal;
|
||||
bottom: 115px;
|
||||
left: 50px;
|
||||
}
|
||||
|
||||
.info-tooltip {
|
||||
@apply relative bg-dark-cerulean rounded-card p-4;
|
||||
width: 375px;
|
||||
left: -325px;
|
||||
top: -146px;
|
||||
|
||||
p {
|
||||
@apply m-0 text-white font-bold text-base;
|
||||
}
|
||||
}
|
||||
|
||||
.info-tooltip-triangle {
|
||||
@apply relative bg-dark-cerulean;
|
||||
transform: rotate(45deg);
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
top: -159px;
|
||||
left: 6px;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, OnInit, Output } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'page-tooltip',
|
||||
templateUrl: 'tooltip.component.html',
|
||||
styleUrls: ['tooltip.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class TooltipComponent implements OnInit {
|
||||
@Output() close = new EventEmitter<boolean>();
|
||||
isOpen = false;
|
||||
constructor() {}
|
||||
|
||||
ngOnInit() {}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { UiIconModule } from '@ui/icon';
|
||||
|
||||
import { TooltipComponent } from './tooltip.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, UiIconModule],
|
||||
exports: [TooltipComponent],
|
||||
declarations: [TooltipComponent],
|
||||
providers: [],
|
||||
})
|
||||
export class TooltipModule {}
|
||||
@@ -79,7 +79,11 @@
|
||||
"
|
||||
></ui-icon>
|
||||
<div class="label" [class.dummy]="group.orderType === 'Dummy'">
|
||||
{{ group.orderType !== 'Dummy' ? group.orderType : 'Manuelle Anlage / Dummy Bestellung' }}
|
||||
<ng-container [ngSwitch]="group.orderType">
|
||||
<ng-container *ngSwitchCase="'Dummy'">Manuelle Anlage / Dummy Bestellung</ng-container>
|
||||
<ng-container *ngSwitchCase="'DIG-Versand'">Versand</ng-container>
|
||||
<ng-container *ngSwitchDefault> {{ group.orderType }} </ng-container>
|
||||
</ng-container>
|
||||
<a *ngIf="group.orderType === 'Dummy'" class="cta-secondary" [routerLink]="['/cart', 'dummy']">Hinzufügen</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -133,7 +137,7 @@
|
||||
*ngIf="group.orderType !== 'Rücklage' && group.orderType !== 'Download' && group.orderType !== 'Dummy'"
|
||||
class="product-delivery"
|
||||
>
|
||||
{{ group.orderType }} {{ group.orderType === 'Abholung' ? 'ab' : '' }}
|
||||
{{ group.orderType === 'DIG-Versand' ? 'Versand' : group.orderType }} {{ group.orderType === 'Abholung' ? 'ab' : '' }}
|
||||
{{ item?.availability?.estimatedShippingDate | date }}
|
||||
</div>
|
||||
<div *ngIf="group.orderType === 'Dummy'" class="product-delivery">
|
||||
|
||||
@@ -14,6 +14,7 @@ import { Subject, NEVER } from 'rxjs';
|
||||
import { DomainCatalogService } from '@domain/catalog';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { DomainPrinterService } from '@domain/printer';
|
||||
import { ResponseArgsOfItemDTO } from '@swagger/cat';
|
||||
|
||||
@Component({
|
||||
selector: 'page-checkout-review',
|
||||
@@ -49,13 +50,23 @@ export class CheckoutReviewComponent implements OnInit {
|
||||
takeUntil(this._orderCompleted),
|
||||
map((items) =>
|
||||
items.reduce((grouped, item) => {
|
||||
// let index = grouped.findIndex((g) => g?.orderType === item?.features?.orderType && g.destination?.id === item?.destination?.id);
|
||||
let index = grouped.findIndex((g) => g?.orderType === 'Dummy' || g?.orderType === item?.features?.orderType);
|
||||
let group = index !== -1 ? grouped[index] : undefined;
|
||||
let index = grouped.findIndex((g) =>
|
||||
item?.availability?.supplyChannel === 'MANUALLY'
|
||||
? g?.orderType === 'Dummy'
|
||||
: item?.features?.orderType === 'DIG-Versand'
|
||||
? g?.orderType === 'Versand'
|
||||
: g?.orderType === item?.features?.orderType
|
||||
);
|
||||
|
||||
let group = index !== -1 ? grouped[index] : undefined;
|
||||
if (!group) {
|
||||
group = {
|
||||
orderType: item?.availability?.supplyChannel === 'MANUALLY' ? 'Dummy' : item.features.orderType,
|
||||
orderType:
|
||||
item?.availability?.supplyChannel === 'MANUALLY'
|
||||
? 'Dummy'
|
||||
: item.features.orderType === 'DIG-Versand'
|
||||
? 'Versand'
|
||||
: item.features.orderType,
|
||||
destination: item.destination?.data,
|
||||
items: [],
|
||||
};
|
||||
@@ -199,16 +210,28 @@ export class CheckoutReviewComponent implements OnInit {
|
||||
.pipe(map((branches) => branches.find((branch) => (branchId ? branch.id === branchId : branch.branchNumber === branchNo))))
|
||||
.toPromise();
|
||||
|
||||
const takeAwayAvailability = await this.availabilityService
|
||||
.getTakeAwayAvailability({
|
||||
item: {
|
||||
itemId: Number(shoppingCartItem.product.catalogProductNumber),
|
||||
ean: shoppingCartItem.product.ean,
|
||||
price: shoppingCartItem.availability.price,
|
||||
},
|
||||
quantity,
|
||||
})
|
||||
.toPromise();
|
||||
let catalogItem: ResponseArgsOfItemDTO;
|
||||
if (Number.isInteger(shoppingCartItem?.product?.catalogProductNumber)) {
|
||||
catalogItem = await this.domainCatalogService
|
||||
.getDetailsById({ id: Number(shoppingCartItem.product.catalogProductNumber) })
|
||||
.toPromise();
|
||||
} else if (shoppingCartItem?.product?.ean) {
|
||||
catalogItem = await this.domainCatalogService.getDetailsByEan({ ean: shoppingCartItem.product.ean }).toPromise();
|
||||
}
|
||||
|
||||
let takeAwayAvailability: AvailabilityDTO;
|
||||
if (!!catalogItem?.result?.product) {
|
||||
takeAwayAvailability = await this.availabilityService
|
||||
.getTakeAwayAvailability({
|
||||
item: {
|
||||
itemId: catalogItem.result.id,
|
||||
ean: catalogItem.result.product.ean,
|
||||
price: catalogItem.result.catalogAvailability?.price,
|
||||
},
|
||||
quantity,
|
||||
})
|
||||
.toPromise();
|
||||
}
|
||||
|
||||
const pickupAvailability = await this.availabilityService
|
||||
.getPickUpAvailability({
|
||||
@@ -345,6 +368,8 @@ export class CheckoutReviewComponent implements OnInit {
|
||||
|
||||
async updateItemQuantity(shoppingCartItem: ShoppingCartItemDTO, quantity: number) {
|
||||
this.showQuantityControlSpinnerItemId = shoppingCartItem.id;
|
||||
|
||||
const shoppingCartItemPrice = shoppingCartItem?.availability?.price?.value?.value;
|
||||
const orderType = shoppingCartItem?.features?.orderType;
|
||||
let availability: AvailabilityDTO;
|
||||
|
||||
@@ -429,14 +454,25 @@ export class CheckoutReviewComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
if (quantity === 0 || availability) {
|
||||
if (quantity === 0) {
|
||||
await this.domainCheckoutService
|
||||
.updateItemInShoppingCart({
|
||||
processId: this.applicationService.activatedProcessId,
|
||||
shoppingCartItemId: shoppingCartItem.id,
|
||||
update: {
|
||||
quantity,
|
||||
availability,
|
||||
availability: null,
|
||||
},
|
||||
})
|
||||
.toPromise();
|
||||
} else if (availability) {
|
||||
await this.domainCheckoutService
|
||||
.updateItemInShoppingCart({
|
||||
processId: this.applicationService.activatedProcessId,
|
||||
shoppingCartItemId: shoppingCartItem.id,
|
||||
update: {
|
||||
quantity,
|
||||
availability: this.compareDeliveryAndCatalogPrice(availability, orderType, shoppingCartItemPrice),
|
||||
},
|
||||
})
|
||||
.toPromise();
|
||||
@@ -446,6 +482,23 @@ export class CheckoutReviewComponent implements OnInit {
|
||||
this.showQuantityControlSpinnerItemId = undefined;
|
||||
}
|
||||
|
||||
// Bei unbekannten Kunden und DIG Bestellung findet ein Vergleich der Preise statt
|
||||
compareDeliveryAndCatalogPrice(availability: AvailabilityDTO, orderType: string, shoppingCartItemPrice: number) {
|
||||
if (['Versand', 'DIG-Versand'].includes(orderType) && shoppingCartItemPrice < availability?.price?.value?.value) {
|
||||
return {
|
||||
...availability,
|
||||
price: {
|
||||
...availability.price,
|
||||
value: {
|
||||
...availability.price.value,
|
||||
value: shoppingCartItemPrice,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
return availability;
|
||||
}
|
||||
|
||||
async changeAddress() {
|
||||
const processId = this.applicationService.activatedProcessId;
|
||||
const customer = await this.domainCheckoutService.getBuyer({ processId }).pipe(first()).toPromise();
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
<ui-checkbox [ngModel]="ncs.hasEmail && (ncs.notificationChannelEmail$ | async)" (ngModelChange)="check($event, 'email')">
|
||||
E-Mail
|
||||
</ui-checkbox>
|
||||
<ui-checkbox [ngModel]="ncs.hasMobile && (ncs.notificationChannelMobile$ | async)" (ngModelChange)="check($event, 'sms')">
|
||||
<ui-checkbox
|
||||
[ngModel]="!(ncs.hasEmail && ncs.hasMobile) && ncs.hasMobile && (ncs.notificationChannelMobile$ | async)"
|
||||
(ngModelChange)="check($event, 'sms')"
|
||||
>
|
||||
SMS
|
||||
</ui-checkbox>
|
||||
</div>
|
||||
|
||||
@@ -20,6 +20,24 @@ export class NotificationCheckboxComponent implements OnInit {
|
||||
|
||||
ngOnInit() {
|
||||
this.ncs.checkEmailOrMobileExist();
|
||||
this.setInitialNotificationChannel();
|
||||
}
|
||||
|
||||
setInitialNotificationChannel() {
|
||||
if (this.ncs.hasEmail && this.ncs.hasMobile) {
|
||||
this.ncs.smsNotification = undefined;
|
||||
this.ncs.emailNotification = 1;
|
||||
} else if (this.ncs.hasEmail && !this.ncs.hasMobile) {
|
||||
this.ncs.smsNotification = undefined;
|
||||
this.ncs.emailNotification = 1;
|
||||
} else if (!this.ncs.hasEmail && this.ncs.hasMobile) {
|
||||
this.ncs.emailNotification = undefined;
|
||||
this.ncs.smsNotification = 2;
|
||||
} else {
|
||||
this.ncs.emailNotification = undefined;
|
||||
this.ncs.smsNotification = undefined;
|
||||
}
|
||||
this.onChangeNotificationChannel();
|
||||
}
|
||||
|
||||
check(event: true | undefined, option: 'email' | 'sms') {
|
||||
|
||||
@@ -7,7 +7,17 @@
|
||||
<p>
|
||||
Möchten Sie den Artikel geliefert bekommen?
|
||||
</p>
|
||||
<span class="price">{{ availability.price?.value?.value | currency: availability.price?.value?.currency:'code' }}</span>
|
||||
<div class="price-wrapper">
|
||||
<span class="price">{{ price$ | async | currency: availability.price?.value?.currency:'code' }}</span>
|
||||
<ng-container *ngIf="showTooltip$ | async">
|
||||
<button [uiOverlayTrigger]="tooltipContent" #tooltip="uiOverlayTrigger" class="info-tooltip-button" type="button">
|
||||
i
|
||||
</button>
|
||||
<ui-tooltip #tooltipContent yPosition="above" xPosition="after" [yOffset]="-16">
|
||||
Günstigerer Preis aus Hugendubel Katalog wird übernommen
|
||||
</ui-tooltip>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="grow"></div>
|
||||
<span class="delivery">Versandkostenfrei</span>
|
||||
<span class="date"
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
.price-wrapper {
|
||||
@apply mt-2;
|
||||
}
|
||||
|
||||
.info-tooltip-button {
|
||||
@apply border-font-customer bg-white rounded-full text-base font-bold;
|
||||
border-style: outset;
|
||||
width: 31px;
|
||||
height: 31px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Component, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { BehaviorSubject, combineLatest } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { PurchasingOptionsModalStore } from '../purchasing-options-modal.store';
|
||||
|
||||
@@ -13,6 +14,19 @@ export class DeliveryOptionComponent {
|
||||
|
||||
readonly availability$ = this.purchasingOptionsModalStore.selectAvailabilities.pipe(map((ava) => ava['delivery']));
|
||||
|
||||
showTooltip$ = new BehaviorSubject<boolean>(false);
|
||||
|
||||
price$ = combineLatest([this.availability$, this.item$]).pipe(
|
||||
map(([availability, item]) => {
|
||||
const shippingPrice = availability?.price?.value?.value;
|
||||
const catalogPrice = item?.catalogAvailability?.price?.value?.value;
|
||||
if (catalogPrice < shippingPrice) {
|
||||
this.showTooltip$.next(true);
|
||||
}
|
||||
return catalogPrice <= shippingPrice ? catalogPrice : shippingPrice;
|
||||
})
|
||||
);
|
||||
|
||||
constructor(private purchasingOptionsModalStore: PurchasingOptionsModalStore) {}
|
||||
|
||||
select() {
|
||||
|
||||
@@ -5,8 +5,17 @@
|
||||
</div>
|
||||
<h4>DIG Versand</h4>
|
||||
<p>Möchten Sie den Artikel geliefert bekommen?</p>
|
||||
<span class="price">{{ availability.price?.value?.value | currency: availability.price?.value?.currency:'code' }}</span>
|
||||
|
||||
<div class="price-wrapper">
|
||||
<span class="price">{{ price$ | async | currency: availability.price?.value?.currency:'code' }}</span>
|
||||
<ng-container *ngIf="showTooltip$ | async">
|
||||
<button [uiOverlayTrigger]="tooltipContent" #tooltip="uiOverlayTrigger" class="info-tooltip-button" type="button">
|
||||
i
|
||||
</button>
|
||||
<ui-tooltip #tooltipContent yPosition="above" xPosition="after" [yOffset]="-16">
|
||||
Günstigerer Preis aus Hugendubel Katalog wird übernommen
|
||||
</ui-tooltip>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="grow"></div>
|
||||
<span class="delivery">Versandkostenfrei</span>
|
||||
<span class="date"
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
.price-wrapper {
|
||||
@apply mt-2;
|
||||
}
|
||||
|
||||
.info-tooltip-button {
|
||||
@apply border-font-customer bg-white rounded-full text-base font-bold;
|
||||
border-style: outset;
|
||||
width: 31px;
|
||||
height: 31px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Component, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { BehaviorSubject, combineLatest } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { PurchasingOptionsModalStore } from '../purchasing-options-modal.store';
|
||||
|
||||
@@ -13,6 +14,19 @@ export class DigDeliveryOptionComponent {
|
||||
|
||||
readonly availability$ = this.purchasingOptionsModalStore.selectAvailabilities.pipe(map((ava) => ava['dig-delivery']));
|
||||
|
||||
showTooltip$ = new BehaviorSubject<boolean>(false);
|
||||
|
||||
price$ = combineLatest([this.availability$, this.item$]).pipe(
|
||||
map(([availability, item]) => {
|
||||
const shippingPrice = availability?.price?.value?.value;
|
||||
const catalogPrice = item?.catalogAvailability?.price?.value?.value;
|
||||
if (catalogPrice < shippingPrice) {
|
||||
this.showTooltip$.next(true);
|
||||
}
|
||||
return catalogPrice <= shippingPrice ? catalogPrice : shippingPrice;
|
||||
})
|
||||
);
|
||||
|
||||
constructor(private purchasingOptionsModalStore: PurchasingOptionsModalStore) {}
|
||||
|
||||
select() {
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
<div class="grow"></div>
|
||||
<div class="format">{{ item?.product?.formatDetail }}</div>
|
||||
<div class="price">
|
||||
{{ item?.catalogAvailability?.price?.value?.value | currency: item?.catalogAvailability?.price?.value?.currency || 'EUR':'code' }}
|
||||
{{ price$ | async | currency: item?.catalogAvailability?.price?.value?.currency || 'EUR':'code' }}
|
||||
</div>
|
||||
<div class="date" *ngIf="option$ | async; let option">
|
||||
<ng-container *ngIf="option === 'pick-up'">Abholung ab</ng-container>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { ApplicationService } from '@core/application';
|
||||
import { DomainCheckoutService } from '@domain/checkout';
|
||||
import { AddToShoppingCartDTO, AvailabilityDTO, VATType } from '@swagger/checkout';
|
||||
import { UiModalRef } from '@ui/modal';
|
||||
import { debounceTime, first, map, switchMap } from 'rxjs/operators';
|
||||
import { debounceTime, first, map, shareReplay, switchMap } from 'rxjs/operators';
|
||||
import { combineLatest, Observable } from 'rxjs';
|
||||
import { PurchasingOptionsModalData } from './purchasing-options-modal.data';
|
||||
import { PurchasingOptions, PurchasingOptionsModalStore } from './purchasing-options-modal.store';
|
||||
@@ -45,7 +45,9 @@ export class PurchasingOptionsModalComponent {
|
||||
|
||||
readonly quantityError$ = this.purchasingOptionsModalStore.selectQuantityError;
|
||||
|
||||
readonly showCustomPrice$ = this.item$.pipe(map((item) => !item?.catalogAvailability?.price?.value?.value));
|
||||
readonly showCustomPrice$ = this.purchasingOptionsModalStore.selectAvailabilities.pipe(
|
||||
map((availabilities) => Object.keys(availabilities)?.every((key) => !availabilities[key]?.price?.value?.value))
|
||||
);
|
||||
|
||||
readonly customPriceInvalid$ = combineLatest([
|
||||
this.showCustomPrice$,
|
||||
@@ -113,12 +115,34 @@ export class PurchasingOptionsModalComponent {
|
||||
map((buyer) => buyer.source)
|
||||
);
|
||||
|
||||
price$ = combineLatest([this.item$, this.purchasingOptionsModalStore.selectCustomPrice]).pipe(
|
||||
map(([item, customPrice]) => item?.catalogAvailability?.price?.value?.value ?? customPrice ?? 0)
|
||||
price$ = combineLatest([
|
||||
this.purchasingOptionsModalStore.selectAvailabilities,
|
||||
this.option$,
|
||||
this.purchasingOptionsModalStore.selectCustomPrice,
|
||||
]).pipe(
|
||||
map(([availabilities, option, customPrice]) => {
|
||||
if (option && !!availabilities[option]) {
|
||||
return availabilities[option]?.price?.value?.value ?? customPrice;
|
||||
} else {
|
||||
const key = Object.keys(availabilities).find((key) => !!availabilities[key]?.price?.value?.value);
|
||||
return availabilities[key]?.price?.value?.value ?? customPrice;
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
vat$ = combineLatest([this.item$, this.purchasingOptionsModalStore.selectCustomVat]).pipe(
|
||||
map(([item, customVat]) => item?.catalogAvailability?.price.vat ?? customVat)
|
||||
vat$ = combineLatest([
|
||||
this.purchasingOptionsModalStore.selectAvailabilities,
|
||||
this.option$,
|
||||
this.purchasingOptionsModalStore.selectCustomVat,
|
||||
]).pipe(
|
||||
map(([availabilities, option, customVat]) => {
|
||||
if (option && !!availabilities[option]) {
|
||||
return availabilities[option]?.price?.vat?.vatType ?? customVat;
|
||||
} else {
|
||||
const key = Object.keys(availabilities).find((key) => !!availabilities[key]?.price?.vat?.vatType);
|
||||
return availabilities[key]?.price?.vat?.vatType ?? customVat;
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
readonly promoPoints$ = combineLatest([this.item$, this.quantity$, this.price$]).pipe(
|
||||
@@ -224,6 +248,18 @@ export class PurchasingOptionsModalComponent {
|
||||
vatType: customVat,
|
||||
},
|
||||
};
|
||||
} else {
|
||||
const price = await this.price$.pipe(first()).toPromise();
|
||||
const vat = await this.vat$.pipe(first()).toPromise();
|
||||
newItem.availability.price = {
|
||||
value: {
|
||||
value: price,
|
||||
currency: 'EUR',
|
||||
},
|
||||
vat: {
|
||||
vatType: vat,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
switch (option) {
|
||||
|
||||
@@ -22,10 +22,13 @@ import { KeyNavigationModule } from '../../shared/key-navigation/key-navigation.
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { UiQuantityDropdownModule } from '@ui/quantity-dropdown';
|
||||
import { PurchasingOptionsModalPriceInputModule } from './price-input/purchasing-options-modal-price-input.module';
|
||||
import { UiTooltipModule } from '@ui/tooltip';
|
||||
import { UiCommonModule } from '@ui/common';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
UiCommonModule,
|
||||
FormsModule,
|
||||
UiIconModule,
|
||||
OverlayModule,
|
||||
@@ -36,6 +39,7 @@ import { PurchasingOptionsModalPriceInputModule } from './price-input/purchasing
|
||||
KeyNavigationModule,
|
||||
RouterModule,
|
||||
PurchasingOptionsModalPriceInputModule,
|
||||
UiTooltipModule,
|
||||
],
|
||||
exports: [PurchasingOptionsModalComponent],
|
||||
declarations: [
|
||||
|
||||
@@ -242,13 +242,29 @@ export class PurchasingOptionsModalStore extends ComponentStore<PurchasingOption
|
||||
});
|
||||
|
||||
readonly setAvailability = this.updater(
|
||||
(state, { availability, option }: { availability: AvailabilityDTO; option: PurchasingOptions }) => {
|
||||
(state, { availability, option, item }: { availability: AvailabilityDTO; option: PurchasingOptions; item: ItemDTO }) => {
|
||||
this.checkCanAdd();
|
||||
let updatedAvailability = availability;
|
||||
if ((option && option === 'delivery') || option === 'dig-delivery') {
|
||||
const catalogPrice = item?.catalogAvailability?.price?.value?.value;
|
||||
const availabilityPrice = availability?.price?.value?.value;
|
||||
const updatedPrice = catalogPrice <= availabilityPrice ? catalogPrice : availabilityPrice;
|
||||
updatedAvailability = {
|
||||
...availability,
|
||||
price: {
|
||||
...availability.price,
|
||||
value: {
|
||||
...availability.price.value,
|
||||
value: updatedPrice,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
return {
|
||||
...state,
|
||||
availabilities: {
|
||||
...state.availabilities,
|
||||
[option]: availability,
|
||||
[option]: updatedAvailability,
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -326,6 +342,7 @@ export class PurchasingOptionsModalStore extends ComponentStore<PurchasingOption
|
||||
});
|
||||
|
||||
readonly setAvailabilities = this.updater((state, availabilities: { [key: string]: AvailabilityDTO }) => {
|
||||
this.checkCanAdd();
|
||||
return {
|
||||
...state,
|
||||
availabilities: {
|
||||
@@ -440,7 +457,7 @@ export class PurchasingOptionsModalStore extends ComponentStore<PurchasingOption
|
||||
return availability$.pipe(
|
||||
tapResponse(
|
||||
(availability) => {
|
||||
this.setAvailability({ option, availability });
|
||||
this.setAvailability({ option, availability, item });
|
||||
this.patchState({ fetchingAvailability: false });
|
||||
},
|
||||
() => {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
</p>
|
||||
<page-customer-type-selector [ngModel]="cusotmers$ | async" (ngModelChange)="setType($event)"></page-customer-type-selector>
|
||||
<div class="router-outlet-wrapper">
|
||||
<form *ngIf="control" [formGroup]="control" (ngSubmit)="submit()">
|
||||
<form *ngIf="control" #formControl [formGroup]="control" (ngSubmit)="submit()">
|
||||
<ng-container formGroupName="organisation">
|
||||
<ui-form-control label="Firmenname" requiredMark="*">
|
||||
<input uiInput type="text" formControlName="name" tabindex="1" />
|
||||
@@ -211,7 +211,7 @@
|
||||
</div>
|
||||
|
||||
<div class="center sticky-bottom">
|
||||
<button class="create-customer-submit" type="submit" [disabled]="control.invalid || control.disabled">
|
||||
<button class="create-customer-submit" type="submit" [disabled]="control.disabled">
|
||||
<ui-spinner [show]="!control.enabled">
|
||||
Speichern
|
||||
</ui-spinner>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Component, ChangeDetectionStrategy, OnInit, ChangeDetectorRef, OnDestroy } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { Component, ChangeDetectionStrategy, OnInit, ChangeDetectorRef, OnDestroy, ViewChild, ElementRef } from '@angular/core';
|
||||
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
@@ -12,7 +12,7 @@ import { validateEmail } from '../validators/email-validator';
|
||||
import { DomainCheckoutService } from '@domain/checkout';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { genderLastNameValidator } from '../validators/gender-b2b-validator';
|
||||
import { setInvalidPropertyErrors, UiValidators } from '@ui/validators';
|
||||
import { UiValidators } from '@ui/validators';
|
||||
import { organisationB2bDeliveryValidator } from '../validators/organisation-delivery-b2b-validator';
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
|
||||
@@ -23,8 +23,15 @@ import { HttpErrorResponse } from '@angular/common/http';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CustomerCreateB2BComponent extends CustomerCreateComponentBase implements OnInit, OnDestroy {
|
||||
@ViewChild('formControl')
|
||||
formControlRef: ElementRef;
|
||||
|
||||
subscription: Subscription;
|
||||
|
||||
get shippingAddressGroup(): AbstractControl {
|
||||
return this.control?.get('shippingAddress');
|
||||
}
|
||||
|
||||
constructor(
|
||||
public activatedRoute: ActivatedRoute,
|
||||
public router: Router,
|
||||
@@ -43,30 +50,14 @@ export class CustomerCreateB2BComponent extends CustomerCreateComponentBase impl
|
||||
ngOnInit(): void {
|
||||
this.init();
|
||||
|
||||
this.control.get('shippingAddress').disable();
|
||||
|
||||
// this.subscription = this.control.get('shippingAddress').valueChanges.subscribe(() => {
|
||||
// console.log(
|
||||
// !!this.control.get('shippingAddress').value.lastName &&
|
||||
// !!this.control.get('shippingAddress').value.firstName &&
|
||||
// !!this.control.get('shippingAddress').value.gender
|
||||
// );
|
||||
// !!this.control.get('shippingAddress').value.lastName &&
|
||||
// !!this.control.get('shippingAddress').value.firstName &&
|
||||
// !!this.control.get('shippingAddress').value.gender
|
||||
// ? this.control.get('shippingAddress').get('organisation').get('name').setValidators(null)
|
||||
// : this.control.get('shippingAddress').get('organisation').get('name').setValidators(Validators.required);
|
||||
|
||||
// // this.control.get('shippingAddress').get('organisation').value.name ? '' : '*'"
|
||||
// this.control.updateValueAndValidity();
|
||||
// });
|
||||
|
||||
this.shippingAddressGroup.disable();
|
||||
this.subscription = this.control.get('differentShippingAddress').valueChanges.subscribe((isChecked) => {
|
||||
if (isChecked) {
|
||||
this.control.get('shippingAddress').enable();
|
||||
this.control.updateValueAndValidity();
|
||||
this.shippingAddressGroup.enable();
|
||||
this.setShippingAddressValidators(this.shippingAddressGroup);
|
||||
} else {
|
||||
this.control.get('shippingAddress').disable();
|
||||
this.shippingAddressGroup.disable();
|
||||
this.clearShippingAddressValidators(this.shippingAddressGroup);
|
||||
this.control.updateValueAndValidity();
|
||||
}
|
||||
});
|
||||
@@ -90,8 +81,13 @@ export class CustomerCreateB2BComponent extends CustomerCreateComponentBase impl
|
||||
}),
|
||||
gender: fb.control(customer?.gender),
|
||||
title: fb.control(customer?.title),
|
||||
firstName: fb.control(customer?.firstName),
|
||||
lastName: fb.control(customer?.lastName),
|
||||
firstName: fb.control(customer?.firstName),
|
||||
communicationDetails: fb.group({
|
||||
email: fb.control(customer?.communicationDetails?.email, [validateEmail]),
|
||||
phone: fb.control(customer?.communicationDetails?.phone, [UiValidators.phone]),
|
||||
mobile: fb.control(customer?.communicationDetails?.mobile, [UiValidators.phone]),
|
||||
}),
|
||||
address: fb.group({
|
||||
street: fb.control(customer?.address?.street, [Validators.required]),
|
||||
streetNumber: fb.control(customer?.address?.streetNumber, [Validators.required]),
|
||||
@@ -100,11 +96,6 @@ export class CustomerCreateB2BComponent extends CustomerCreateComponentBase impl
|
||||
info: fb.control(customer?.address?.info),
|
||||
country: fb.control(customer?.address?.country || 'DEU', [Validators.required]),
|
||||
}),
|
||||
communicationDetails: fb.group({
|
||||
email: fb.control(customer?.communicationDetails?.email, [validateEmail]),
|
||||
phone: fb.control(customer?.communicationDetails?.phone, [UiValidators.phone]),
|
||||
mobile: fb.control(customer?.communicationDetails?.mobile, [UiValidators.phone]),
|
||||
}),
|
||||
differentShippingAddress: fb.control(false),
|
||||
shippingAddress: fb.group({
|
||||
organisation: fb.group({
|
||||
@@ -117,15 +108,15 @@ export class CustomerCreateB2BComponent extends CustomerCreateComponentBase impl
|
||||
firstName: fb.control(''),
|
||||
lastName: fb.control(''),
|
||||
address: fb.group({
|
||||
street: fb.control('', [Validators.required]),
|
||||
streetNumber: fb.control('', [Validators.required]),
|
||||
zipCode: fb.control('', [Validators.required]),
|
||||
city: fb.control('', [Validators.required]),
|
||||
street: fb.control(''),
|
||||
streetNumber: fb.control(''),
|
||||
zipCode: fb.control(''),
|
||||
city: fb.control(''),
|
||||
info: fb.control(''),
|
||||
country: fb.control('DEU', [Validators.required]),
|
||||
country: fb.control('DEU'),
|
||||
}),
|
||||
communicationDetails: fb.group({
|
||||
email: fb.control('', [validateEmail]),
|
||||
email: fb.control(''),
|
||||
phone: fb.control(''),
|
||||
mobile: fb.control(''),
|
||||
}),
|
||||
@@ -136,7 +127,16 @@ export class CustomerCreateB2BComponent extends CustomerCreateComponentBase impl
|
||||
}
|
||||
|
||||
async submit(): Promise<void> {
|
||||
if (this.control.invalid || this.control.disabled) {
|
||||
if (this.control.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.control.invalid) {
|
||||
this.control.disable({ emitEvent: false });
|
||||
setTimeout(() => {
|
||||
this.enableControl();
|
||||
this.focusFirstInvalidFormControl(this.formControlRef);
|
||||
}, 200);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<page-customer-type-selector [ngModel]="cusotmers$ | async" (ngModelChange)="setType($event)"></page-customer-type-selector>
|
||||
|
||||
<div class="router-outlet-wrapper">
|
||||
<form *ngIf="control" [formGroup]="control" (ngSubmit)="submit()">
|
||||
<form *ngIf="control" #formControl [formGroup]="control" (ngSubmit)="submit()">
|
||||
<div class="control-row">
|
||||
<ui-form-control [clearable]="true" label="Anrede" requiredMark="*">
|
||||
<ui-select formControlName="gender" tabindex="1">
|
||||
@@ -193,7 +193,7 @@
|
||||
</div>
|
||||
|
||||
<div class="center sticky-bottom">
|
||||
<button class="create-customer-submit" type="submit" [disabled]="control.invalid || control.disabled">
|
||||
<button class="create-customer-submit" type="submit" [disabled]="control.disabled">
|
||||
<ui-spinner [show]="!control.enabled">
|
||||
Speichern
|
||||
</ui-spinner>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, ChangeDetectorRef } from '@angular/core';
|
||||
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, ChangeDetectorRef, ElementRef, ViewChild } from '@angular/core';
|
||||
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { ApplicationService } from '@core/application';
|
||||
@@ -11,7 +11,7 @@ import { CustomerCreateComponentBase } from './customer-create.component';
|
||||
import { validateEmail } from '../validators/email-validator';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { DomainCheckoutService } from '@domain/checkout';
|
||||
import { UiValidators, setInvalidPropertyErrors } from '@ui/validators';
|
||||
import { UiValidators } from '@ui/validators';
|
||||
import { isStringEmpty } from '@utils/common';
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
|
||||
@@ -22,6 +22,9 @@ import { HttpErrorResponse } from '@angular/common/http';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CustomerCreateBranchComponent extends CustomerCreateComponentBase implements OnInit, OnDestroy {
|
||||
@ViewChild('formControl')
|
||||
formControlRef: ElementRef;
|
||||
|
||||
shippingAddressSubscription: Subscription;
|
||||
organisationSubscription: Subscription;
|
||||
addressSubscription: Subscription;
|
||||
@@ -30,6 +33,10 @@ export class CustomerCreateBranchComponent extends CustomerCreateComponentBase i
|
||||
return this.control?.get('address');
|
||||
}
|
||||
|
||||
get shippingAddressGroup(): AbstractControl {
|
||||
return this.control?.get('shippingAddress');
|
||||
}
|
||||
|
||||
constructor(
|
||||
public activatedRoute: ActivatedRoute,
|
||||
public router: Router,
|
||||
@@ -48,14 +55,14 @@ export class CustomerCreateBranchComponent extends CustomerCreateComponentBase i
|
||||
ngOnInit() {
|
||||
this.init();
|
||||
|
||||
this.control.get('shippingAddress').disable();
|
||||
|
||||
this.shippingAddressGroup.disable();
|
||||
this.shippingAddressSubscription = this.control.get('differentShippingAddress').valueChanges.subscribe((isChecked) => {
|
||||
if (isChecked) {
|
||||
this.control.get('shippingAddress').enable();
|
||||
this.control.updateValueAndValidity();
|
||||
this.shippingAddressGroup.enable();
|
||||
this.setShippingAddressValidators(this.shippingAddressGroup);
|
||||
} else {
|
||||
this.control.get('shippingAddress').disable();
|
||||
this.shippingAddressGroup.disable();
|
||||
this.clearShippingAddressValidators(this.shippingAddressGroup);
|
||||
this.control.updateValueAndValidity();
|
||||
}
|
||||
});
|
||||
@@ -110,9 +117,14 @@ export class CustomerCreateBranchComponent extends CustomerCreateComponentBase i
|
||||
}),
|
||||
gender: fb.control(customer?.gender, [Validators.required, Validators.min(1)]),
|
||||
title: fb.control(customer?.title),
|
||||
firstName: fb.control(customer?.firstName, [Validators.required]),
|
||||
lastName: fb.control(customer?.lastName, [Validators.required]),
|
||||
firstName: fb.control(customer?.firstName, [Validators.required]),
|
||||
dateOfBirth: fb.control(customer?.dateOfBirth, [UiValidators.date]),
|
||||
communicationDetails: fb.group({
|
||||
email: fb.control(customer?.communicationDetails?.email, [validateEmail]),
|
||||
phone: fb.control(customer?.communicationDetails?.phone, [UiValidators.phone]),
|
||||
mobile: fb.control(customer?.communicationDetails?.mobile, [UiValidators.phone]),
|
||||
}),
|
||||
address: fb.group({
|
||||
street: fb.control(customer?.address?.street),
|
||||
streetNumber: fb.control(customer?.address?.streetNumber),
|
||||
@@ -121,11 +133,6 @@ export class CustomerCreateBranchComponent extends CustomerCreateComponentBase i
|
||||
country: fb.control(!this.isAddressEmpty(customer?.address) ? customer?.address?.country : ''),
|
||||
info: fb.control(customer?.address?.info),
|
||||
}),
|
||||
communicationDetails: fb.group({
|
||||
email: fb.control(customer?.communicationDetails?.email, [validateEmail]),
|
||||
phone: fb.control(customer?.communicationDetails?.phone, [UiValidators.phone]),
|
||||
mobile: fb.control(customer?.communicationDetails?.mobile, [UiValidators.phone]),
|
||||
}),
|
||||
differentShippingAddress: fb.control(false),
|
||||
shippingAddress: fb.group({
|
||||
organisation: fb.group({
|
||||
@@ -133,24 +140,33 @@ export class CustomerCreateBranchComponent extends CustomerCreateComponentBase i
|
||||
department: fb.control(''),
|
||||
vatId: fb.control(''),
|
||||
}),
|
||||
gender: fb.control(undefined, [Validators.required]),
|
||||
gender: fb.control(undefined),
|
||||
title: fb.control(''),
|
||||
firstName: fb.control('', [Validators.required]),
|
||||
lastName: fb.control('', [Validators.required]),
|
||||
firstName: fb.control(''),
|
||||
lastName: fb.control(''),
|
||||
address: fb.group({
|
||||
street: fb.control('', [Validators.required]),
|
||||
streetNumber: fb.control('', [Validators.required]),
|
||||
zipCode: fb.control('', [Validators.required]),
|
||||
city: fb.control('', [Validators.required]),
|
||||
street: fb.control(''),
|
||||
streetNumber: fb.control(''),
|
||||
zipCode: fb.control(''),
|
||||
city: fb.control(''),
|
||||
info: fb.control(''),
|
||||
country: fb.control('DEU', [Validators.required]),
|
||||
country: fb.control('DEU'),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
async submit(): Promise<void> {
|
||||
if (this.control.invalid || this.control.disabled) {
|
||||
if (this.control.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.control.invalid) {
|
||||
this.control.disable({ emitEvent: false });
|
||||
setTimeout(() => {
|
||||
this.enableControl();
|
||||
this.focusFirstInvalidFormControl(this.formControlRef);
|
||||
}, 200);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
</p>
|
||||
<page-customer-type-selector [ngModel]="cusotmers$ | async" (ngModelChange)="setType($event)"></page-customer-type-selector>
|
||||
<div class="router-outlet-wrapper">
|
||||
<form *ngIf="control" [formGroup]="control" (ngSubmit)="submit()">
|
||||
<form *ngIf="control" #formControl [formGroup]="control" (ngSubmit)="submit()">
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Anrede" [clearable]="true" requiredMark="*">
|
||||
<ui-select formControlName="gender" tabindex="1">
|
||||
@@ -198,7 +198,7 @@
|
||||
</div>
|
||||
|
||||
<div class="center sticky-bottom">
|
||||
<button class="create-customer-submit" type="submit" [disabled]="control.invalid || control.disabled">
|
||||
<button class="create-customer-submit" type="submit" [disabled]="control.disabled">
|
||||
<ui-spinner [show]="!control.enabled">
|
||||
Speichern
|
||||
</ui-spinner>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Component, ChangeDetectionStrategy, OnInit, ChangeDetectorRef, OnDestroy } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { Component, ChangeDetectionStrategy, OnInit, ChangeDetectorRef, OnDestroy, ElementRef, ViewChild } from '@angular/core';
|
||||
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
@@ -13,7 +13,6 @@ import { validateEmail } from '../validators/email-validator';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { DomainCheckoutService } from '@domain/checkout';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { setInvalidPropertyErrors } from '@ui/validators';
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
|
||||
@Component({
|
||||
@@ -23,9 +22,16 @@ import { HttpErrorResponse } from '@angular/common/http';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CustomerCreateGuestComponent extends CustomerCreateComponentBase implements OnInit, OnDestroy {
|
||||
@ViewChild('formControl')
|
||||
formControlRef: ElementRef;
|
||||
|
||||
shippingAddressSubscription: Subscription;
|
||||
organisationSubscription: Subscription;
|
||||
|
||||
get shippingAddressGroup(): AbstractControl {
|
||||
return this.control?.get('shippingAddress');
|
||||
}
|
||||
|
||||
constructor(
|
||||
public activatedRoute: ActivatedRoute,
|
||||
public router: Router,
|
||||
@@ -44,14 +50,15 @@ export class CustomerCreateGuestComponent extends CustomerCreateComponentBase im
|
||||
ngOnInit(): void {
|
||||
this.init();
|
||||
this.countries$ = this.countries$.pipe(map((countries) => countries.filter((country) => country.name === 'Deutschland')));
|
||||
this.control.get('shippingAddress').disable();
|
||||
|
||||
this.shippingAddressGroup.disable();
|
||||
this.shippingAddressSubscription = this.control.get('differentShippingAddress').valueChanges.subscribe((isChecked) => {
|
||||
if (isChecked) {
|
||||
this.control.get('shippingAddress').enable();
|
||||
this.control.updateValueAndValidity();
|
||||
this.shippingAddressGroup.enable();
|
||||
this.setShippingAddressValidators(this.shippingAddressGroup);
|
||||
} else {
|
||||
this.control.get('shippingAddress').disable();
|
||||
this.shippingAddressGroup.disable();
|
||||
this.clearShippingAddressValidators(this.shippingAddressGroup);
|
||||
this.control.updateValueAndValidity();
|
||||
}
|
||||
});
|
||||
@@ -85,9 +92,14 @@ export class CustomerCreateGuestComponent extends CustomerCreateComponentBase im
|
||||
}),
|
||||
gender: fb.control(customer?.gender, [Validators.required, Validators.min(1)]),
|
||||
title: fb.control(customer?.title),
|
||||
firstName: fb.control(customer?.firstName, [Validators.required]),
|
||||
lastName: fb.control(customer?.lastName, [Validators.required]),
|
||||
firstName: fb.control(customer?.firstName, [Validators.required]),
|
||||
dateOfBirth: fb.control(customer?.dateOfBirth, [UiValidators.date]),
|
||||
communicationDetails: fb.group({
|
||||
email: fb.control(customer?.communicationDetails?.email, [Validators.required, validateEmail]),
|
||||
phone: fb.control(customer?.communicationDetails?.phone, [UiValidators.phone]),
|
||||
mobile: fb.control(customer?.communicationDetails?.mobile, [UiValidators.phone]),
|
||||
}),
|
||||
address: fb.group({
|
||||
street: fb.control(customer?.address?.street, [Validators.required]),
|
||||
streetNumber: fb.control(customer?.address?.streetNumber, [Validators.required]),
|
||||
@@ -96,11 +108,6 @@ export class CustomerCreateGuestComponent extends CustomerCreateComponentBase im
|
||||
country: fb.control(customer?.address?.country || 'DEU', [Validators.required]),
|
||||
info: fb.control(customer?.address?.info),
|
||||
}),
|
||||
communicationDetails: fb.group({
|
||||
email: fb.control(customer?.communicationDetails?.email, [Validators.required, validateEmail]),
|
||||
phone: fb.control(customer?.communicationDetails?.phone, [UiValidators.phone]),
|
||||
mobile: fb.control(customer?.communicationDetails?.mobile, [UiValidators.phone]),
|
||||
}),
|
||||
differentShippingAddress: fb.control(false),
|
||||
shippingAddress: fb.group({
|
||||
organisation: fb.group({
|
||||
@@ -108,24 +115,33 @@ export class CustomerCreateGuestComponent extends CustomerCreateComponentBase im
|
||||
department: fb.control(''),
|
||||
vatId: fb.control(''),
|
||||
}),
|
||||
gender: fb.control(undefined, [Validators.required]),
|
||||
gender: fb.control(undefined),
|
||||
title: fb.control(''),
|
||||
firstName: fb.control('', [Validators.required]),
|
||||
lastName: fb.control('', [Validators.required]),
|
||||
firstName: fb.control(''),
|
||||
lastName: fb.control(''),
|
||||
address: fb.group({
|
||||
street: fb.control('', [Validators.required]),
|
||||
streetNumber: fb.control('', [Validators.required]),
|
||||
zipCode: fb.control('', [Validators.required]),
|
||||
city: fb.control('', [Validators.required]),
|
||||
street: fb.control(''),
|
||||
streetNumber: fb.control(''),
|
||||
zipCode: fb.control(''),
|
||||
city: fb.control(''),
|
||||
info: fb.control(''),
|
||||
country: fb.control('DEU', [Validators.required]),
|
||||
country: fb.control('DEU'),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
async submit(): Promise<void> {
|
||||
if (this.control.invalid || this.control.disabled) {
|
||||
if (this.control.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.control.invalid) {
|
||||
this.control.disable({ emitEvent: false });
|
||||
setTimeout(() => {
|
||||
this.enableControl();
|
||||
this.focusFirstInvalidFormControl(this.formControlRef);
|
||||
}, 200);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<page-customer-type-selector [ngModel]="cusotmers$ | async" (ngModelChange)="setType($event)"></page-customer-type-selector>
|
||||
|
||||
<div class="router-outlet-wrapper">
|
||||
<form *ngIf="control" [formGroup]="control" (ngSubmit)="submit()">
|
||||
<form *ngIf="control" #formControl [formGroup]="control" (ngSubmit)="submit()">
|
||||
<div class="control-row">
|
||||
<ui-form-control [clearable]="true" label="Anrede" requiredMark="*">
|
||||
<ui-select formControlName="gender" tabindex="1">
|
||||
@@ -195,7 +195,7 @@
|
||||
</div>
|
||||
|
||||
<div class="center sticky-bottom">
|
||||
<button class="create-customer-submit" type="submit" [disabled]="control.invalid || control.disabled">
|
||||
<button class="create-customer-submit" type="submit" [disabled]="control.disabled">
|
||||
<ui-spinner [show]="!control.enabled">
|
||||
Speichern
|
||||
</ui-spinner>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, ChangeDetectorRef } from '@angular/core';
|
||||
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, ChangeDetectorRef, ElementRef, ViewChild } from '@angular/core';
|
||||
import { AbstractControl, FormBuilder, FormGroup, ValidationErrors, Validators } from '@angular/forms';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { ApplicationService } from '@core/application';
|
||||
@@ -22,6 +22,9 @@ import { HttpErrorResponse } from '@angular/common/http';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CustomerCreateOnlineComponent extends CustomerCreateComponentBase implements OnInit, OnDestroy {
|
||||
@ViewChild('formControl')
|
||||
formControlRef: ElementRef;
|
||||
|
||||
upgradeCustomerId: number;
|
||||
upgradeCustomer: CustomerDTO;
|
||||
shippingAddressSubscription: Subscription;
|
||||
@@ -37,6 +40,10 @@ export class CustomerCreateOnlineComponent extends CustomerCreateComponentBase i
|
||||
return null;
|
||||
};
|
||||
|
||||
get shippingAddressGroup(): AbstractControl {
|
||||
return this.control?.get('shippingAddress');
|
||||
}
|
||||
|
||||
constructor(
|
||||
public activatedRoute: ActivatedRoute,
|
||||
public router: Router,
|
||||
@@ -79,14 +86,14 @@ export class CustomerCreateOnlineComponent extends CustomerCreateComponentBase i
|
||||
});
|
||||
}
|
||||
|
||||
this.control.get('shippingAddress').disable();
|
||||
|
||||
this.shippingAddressGroup.disable();
|
||||
this.shippingAddressSubscription = this.control.get('differentShippingAddress').valueChanges.subscribe((isChecked) => {
|
||||
if (isChecked) {
|
||||
this.control.get('shippingAddress').enable();
|
||||
this.control.updateValueAndValidity();
|
||||
this.shippingAddressGroup.enable();
|
||||
this.setShippingAddressValidators(this.shippingAddressGroup);
|
||||
} else {
|
||||
this.control.get('shippingAddress').disable();
|
||||
this.shippingAddressGroup.disable();
|
||||
this.clearShippingAddressValidators(this.shippingAddressGroup);
|
||||
this.control.updateValueAndValidity();
|
||||
}
|
||||
});
|
||||
@@ -120,17 +127,9 @@ export class CustomerCreateOnlineComponent extends CustomerCreateComponentBase i
|
||||
}),
|
||||
gender: fb.control(customer?.gender || undefined, [Validators.required, Validators.min(1)]),
|
||||
title: fb.control(customer?.title),
|
||||
firstName: fb.control(customer?.firstName, [Validators.required]),
|
||||
lastName: fb.control(customer?.lastName, [Validators.required]),
|
||||
firstName: fb.control(customer?.firstName, [Validators.required]),
|
||||
dateOfBirth: fb.control(customer?.dateOfBirth, [UiValidators.date]),
|
||||
address: fb.group({
|
||||
street: fb.control(customer?.address?.street, [Validators.required]),
|
||||
streetNumber: fb.control(customer?.address?.streetNumber, [Validators.required]),
|
||||
zipCode: fb.control(customer?.address?.zipCode, [Validators.required]),
|
||||
city: fb.control(customer?.address?.city, [Validators.required]),
|
||||
country: fb.control(customer?.address?.country || 'DEU', [Validators.required]),
|
||||
info: fb.control(customer?.address?.info),
|
||||
}),
|
||||
communicationDetails: fb.group({
|
||||
email: fb.control(
|
||||
customer?.communicationDetails?.email,
|
||||
@@ -140,6 +139,14 @@ export class CustomerCreateOnlineComponent extends CustomerCreateComponentBase i
|
||||
phone: fb.control(customer?.communicationDetails?.phone, [UiValidators.phone]),
|
||||
mobile: fb.control(customer?.communicationDetails?.mobile, [UiValidators.phone]),
|
||||
}),
|
||||
address: fb.group({
|
||||
street: fb.control(customer?.address?.street, [Validators.required]),
|
||||
streetNumber: fb.control(customer?.address?.streetNumber, [Validators.required]),
|
||||
zipCode: fb.control(customer?.address?.zipCode, [Validators.required]),
|
||||
city: fb.control(customer?.address?.city, [Validators.required]),
|
||||
country: fb.control(customer?.address?.country || 'DEU', [Validators.required]),
|
||||
info: fb.control(customer?.address?.info),
|
||||
}),
|
||||
differentShippingAddress: fb.control(false),
|
||||
shippingAddress: fb.group({
|
||||
organisation: fb.group({
|
||||
@@ -147,23 +154,32 @@ export class CustomerCreateOnlineComponent extends CustomerCreateComponentBase i
|
||||
department: fb.control(''),
|
||||
vatId: fb.control(''),
|
||||
}),
|
||||
gender: fb.control(undefined, [Validators.required]),
|
||||
gender: fb.control(undefined),
|
||||
title: fb.control(''),
|
||||
firstName: fb.control('', [Validators.required]),
|
||||
lastName: fb.control('', [Validators.required]),
|
||||
firstName: fb.control(''),
|
||||
lastName: fb.control(''),
|
||||
address: fb.group({
|
||||
street: fb.control('', [Validators.required]),
|
||||
streetNumber: fb.control('', [Validators.required]),
|
||||
zipCode: fb.control('', [Validators.required]),
|
||||
city: fb.control('', [Validators.required]),
|
||||
street: fb.control(''),
|
||||
streetNumber: fb.control(''),
|
||||
zipCode: fb.control(''),
|
||||
city: fb.control(''),
|
||||
info: fb.control(''),
|
||||
country: fb.control('DEU', [Validators.required]),
|
||||
country: fb.control('DEU'),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
}
|
||||
async submit(): Promise<void> {
|
||||
if (this.control.invalid || this.control.disabled) {
|
||||
if (this.control.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.control.invalid) {
|
||||
this.control.disable({ emitEvent: false });
|
||||
setTimeout(() => {
|
||||
this.enableControl();
|
||||
this.focusFirstInvalidFormControl(this.formControlRef);
|
||||
}, 200);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AbstractControl, AsyncValidatorFn, FormGroup, ValidationErrors } from '@angular/forms';
|
||||
import { AbstractControl, AsyncValidatorFn, FormArray, FormGroup, ValidationErrors, Validators } from '@angular/forms';
|
||||
import { ActivatedRoute, Params, Router } from '@angular/router';
|
||||
import { CrmCustomerService } from '@domain/crm';
|
||||
import { CountryDTO, CustomerDTO, InputOptionsDTO } from '@swagger/crm';
|
||||
@@ -9,10 +9,11 @@ import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { AddressSelectionModalService } from '../modals/address-selection-modal/address-selection-modal.service';
|
||||
import { DomainCheckoutService } from '@domain/checkout';
|
||||
import { ChangeDetectorRef } from '@angular/core';
|
||||
import { ChangeDetectorRef, ElementRef } from '@angular/core';
|
||||
import { isBoolean } from '@utils/common';
|
||||
import { setInvalidPropertyErrors } from '@ui/validators';
|
||||
import { camelCase } from 'lodash';
|
||||
import { validateEmail } from '../validators/email-validator';
|
||||
|
||||
export abstract class CustomerCreateComponentBase {
|
||||
emailExistsValidator: AsyncValidatorFn = async (control): Promise<ValidationErrors | null> => {
|
||||
@@ -156,6 +157,26 @@ export abstract class CustomerCreateComponentBase {
|
||||
};
|
||||
}
|
||||
|
||||
setShippingAddressValidators(group: AbstractControl) {
|
||||
group.get('gender')?.setValidators([Validators.required]);
|
||||
group.get('firstName')?.setValidators([Validators.required]);
|
||||
group.get('lastName')?.setValidators([Validators.required]);
|
||||
group.get('address')?.get('street')?.setValidators([Validators.required]);
|
||||
group.get('address')?.get('streetNumber')?.setValidators([Validators.required]);
|
||||
group.get('address')?.get('zipCode')?.setValidators([Validators.required]);
|
||||
group.get('address')?.get('city')?.setValidators([Validators.required]);
|
||||
group.get('address')?.get('country')?.setValidators([Validators.required]);
|
||||
group.get('communicationDetails')?.get('email')?.setValidators([validateEmail]);
|
||||
}
|
||||
|
||||
clearShippingAddressValidators(group: AbstractControl) {
|
||||
group.get('gender')?.clearValidators();
|
||||
group.get('firstName')?.clearValidators();
|
||||
group.get('lastName')?.clearValidators();
|
||||
group.get('address')?.clearValidators();
|
||||
group.get('communicationDetails')?.clearValidators();
|
||||
}
|
||||
|
||||
setValidationError(invalidProperties: { [key: string]: string }, formGroup: AbstractControl) {
|
||||
this.control.enable();
|
||||
this.control.reset(this.control.value);
|
||||
@@ -175,6 +196,34 @@ export abstract class CustomerCreateComponentBase {
|
||||
}
|
||||
}
|
||||
|
||||
focusFirstInvalidFormControl(formControlRef: ElementRef) {
|
||||
const invalidKeys = this.findInvalidControlsRecursive(this.control);
|
||||
const invalidControl = formControlRef.nativeElement.querySelector('[formcontrolname="' + invalidKeys?.find((_) => true) + '"]');
|
||||
invalidControl?.focus();
|
||||
}
|
||||
|
||||
findInvalidControlsRecursive(formToInvestigate: FormGroup | FormArray): string[] {
|
||||
const invalidControls: string[] = [];
|
||||
let recursiveFunc = (form: FormGroup | FormArray) => {
|
||||
Object.keys(form.controls).forEach((field) => {
|
||||
if (field === 'shippingAddress') {
|
||||
return;
|
||||
}
|
||||
|
||||
const control = form.get(field);
|
||||
if (control instanceof FormGroup) {
|
||||
recursiveFunc(control);
|
||||
} else if (control instanceof FormArray) {
|
||||
recursiveFunc(control);
|
||||
} else if (control.invalid) {
|
||||
invalidControls.push(field);
|
||||
}
|
||||
});
|
||||
};
|
||||
recursiveFunc(formToInvestigate);
|
||||
return invalidControls;
|
||||
}
|
||||
|
||||
abstract createControl(customer?: CustomerDTO): FormGroup;
|
||||
|
||||
abstract submit(): Promise<void>;
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
*ngFor="let option of value?.values"
|
||||
[ngModel]="option.selected"
|
||||
(ngModelChange)="check($event, option)"
|
||||
[disabled]="!option.enabled"
|
||||
[disabled]="!option.enabled || option.selected"
|
||||
>
|
||||
{{ option.label }}
|
||||
</ui-checkbox>
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
:host {
|
||||
@apply grid grid-flow-col flex-row text-card-sub justify-center gap-6;
|
||||
}
|
||||
|
||||
ui-checkbox.disabled.checked {
|
||||
@apply text-black;
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@
|
||||
</div>
|
||||
|
||||
<ui-radio-group
|
||||
*ngIf="(customer | address) || customer.shippingAddresses.length"
|
||||
*ngIf="(customer | address) || customer.shippingAddresses?.length"
|
||||
class="card-customer-address"
|
||||
[(ngModel)]="selectedAddress"
|
||||
>
|
||||
|
||||
@@ -32,6 +32,7 @@ import { UiSpinnerModule } from 'apps/ui/spinner/src/lib/ui-spinner.module';
|
||||
import { HistoryModule } from '@modal/history';
|
||||
import { CantSelectGuestModalModule } from '../modals/cant-select-guest/cant-select-guest-modal.module';
|
||||
import { UiScrollContainerModule } from '@ui/scroll-container';
|
||||
import { UiTooltipModule } from '@ui/tooltip';
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
@@ -45,6 +46,7 @@ import { UiScrollContainerModule } from '@ui/scroll-container';
|
||||
FormsModule,
|
||||
UiRadioModule,
|
||||
UiCommonModule,
|
||||
UiTooltipModule,
|
||||
UiScrollContainerModule,
|
||||
UiSpinnerModule,
|
||||
HistoryModule,
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
<div class="tags">
|
||||
<img class="tag preorder" src="/assets/images/tag_icon_preorder.svg" alt="Vorbesteller" *ngIf="hasPrebooked" />
|
||||
<img
|
||||
*ngIf="hasPrebooked"
|
||||
[uiOverlayTrigger]="prebookedTooltip"
|
||||
class="tag preorder"
|
||||
src="/assets/images/tag_icon_preorder.svg"
|
||||
alt="Vorbesteller"
|
||||
/>
|
||||
<ui-tooltip yPosition="above" xPosition="after" [yOffset]="-11" [xOffset]="-8" #prebookedTooltip [closeable]="true">
|
||||
Artikel wird für Sie vorgemerkt.
|
||||
</ui-tooltip>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
@@ -115,5 +124,9 @@
|
||||
</button>
|
||||
</span>
|
||||
</ng-container>
|
||||
|
||||
<div class="history-wrapper">
|
||||
<button class="cta-history" (click)="openHistory()">Historie</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -60,6 +60,14 @@ img {
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
.history-wrapper {
|
||||
@apply text-right;
|
||||
}
|
||||
|
||||
.cta-history {
|
||||
@apply text-base font-bold text-brand outline-none border-none bg-transparent mr-1;
|
||||
}
|
||||
|
||||
// .row:first-child {
|
||||
// @apply flex-grow;
|
||||
// }
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { HistoryComponent } from '@modal/history';
|
||||
import { ImageService } from '@sales/core-services';
|
||||
import { OrderDTO, OrderItemDTO, OrderItemSubsetDTO } from '@swagger/oms';
|
||||
import { UiModalService } from '@ui/modal';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
@@ -39,7 +41,7 @@ export class CustomerOrderItemCardComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
constructor(private imageService: ImageService) {}
|
||||
constructor(private imageService: ImageService, private _modal: UiModalService) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.imageUrl = this.imageService.getImageUrl(this.orderItem.product.ean, {
|
||||
@@ -47,4 +49,18 @@ export class CustomerOrderItemCardComponent implements OnInit {
|
||||
height: 100,
|
||||
});
|
||||
}
|
||||
|
||||
openHistory() {
|
||||
this._modal.open({
|
||||
content: HistoryComponent,
|
||||
title: 'Historie',
|
||||
data: {
|
||||
mode: 'order',
|
||||
item: {
|
||||
order: this.order,
|
||||
orderItemSubsetId: this.subsetItem.id,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,8 +168,8 @@ export abstract class CustomerSearch implements OnInit, OnDestroy {
|
||||
private initAutocomplete() {
|
||||
this.autocompleteResult$ = this.inputChange$.pipe(
|
||||
takeUntil(this.destroy$),
|
||||
debounceTime(300),
|
||||
distinctUntilChanged(),
|
||||
debounceTime(200),
|
||||
switchMap((queryString) => {
|
||||
if (queryString.length >= 3) {
|
||||
return this.customerSearch.complete(queryString, this.getSelecteFiltersAsDictionary()).pipe(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Component, OnInit, ChangeDetectionStrategy, ChangeDetectorRef, OnDestroy } from '@angular/core';
|
||||
import { Filter } from '@ui/filter';
|
||||
import { isEqual } from 'lodash';
|
||||
import { CustomerSearch } from '../customer-search.service';
|
||||
|
||||
@Component({
|
||||
@@ -11,16 +12,28 @@ import { CustomerSearch } from '../customer-search.service';
|
||||
export class CustomerSearchFilterComponent implements OnInit, OnDestroy {
|
||||
selectedFilters: Filter[];
|
||||
initialFilter: Filter[];
|
||||
initialSelectedFilters: Filter[];
|
||||
|
||||
filterValuesChanged = false;
|
||||
|
||||
constructor(public search: CustomerSearch, private cdr: ChangeDetectorRef) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.selectedFilters = this.search.queryFilter.filters;
|
||||
this.initialFilter = this.search.initQueryFilter.initialFilter;
|
||||
if (!isEqual(this.selectedFilters, this.initialFilter)) {
|
||||
this.initialSelectedFilters = this.selectedFilters;
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.search.setFilter(this.initialFilter);
|
||||
if (!!this.initialSelectedFilters && this.filterValuesChanged) {
|
||||
this.search.setFilter(this.selectedFilters);
|
||||
} else if (!!this.initialSelectedFilters && !this.filterValuesChanged) {
|
||||
this.search.setFilter(this.initialSelectedFilters);
|
||||
} else {
|
||||
this.search.setFilter(this.initialFilter);
|
||||
}
|
||||
}
|
||||
|
||||
applyFilters(search: boolean = true) {
|
||||
@@ -30,6 +43,7 @@ export class CustomerSearchFilterComponent implements OnInit, OnDestroy {
|
||||
if (search) {
|
||||
this.search.search({ isNewSearch: true });
|
||||
}
|
||||
this.filterValuesChanged = true;
|
||||
}
|
||||
|
||||
updateFilter(filters: Filter[]) {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { FormControl } from '@angular/forms';
|
||||
import { EnvironmentService } from '@core/environment';
|
||||
import { AutocompleteDTO } from '@swagger/crm';
|
||||
import { UiSearchboxAutocompleteComponent } from '@ui/searchbox';
|
||||
import { NativeContainerService } from 'native-container';
|
||||
import { Observable, Subject } from 'rxjs';
|
||||
import { delay, takeUntil, tap } from 'rxjs/operators';
|
||||
import { CustomerSearch } from '../../customer-search.service';
|
||||
@@ -29,7 +30,12 @@ export class CustomerSearchboxComponent implements OnInit, OnDestroy {
|
||||
@Output()
|
||||
searchStarted = new EventEmitter<void>();
|
||||
|
||||
constructor(public search: CustomerSearch, private environmentService: EnvironmentService, private cdr: ChangeDetectorRef) {}
|
||||
constructor(
|
||||
public search: CustomerSearch,
|
||||
private environmentService: EnvironmentService,
|
||||
private cdr: ChangeDetectorRef,
|
||||
private nativeContainer: NativeContainerService
|
||||
) {}
|
||||
|
||||
startSearch() {
|
||||
this.search.startSearch();
|
||||
@@ -74,7 +80,7 @@ export class CustomerSearchboxComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
async detectDevice() {
|
||||
this.isMobile = await this.environmentService.isMobile();
|
||||
this.isMobile = this.nativeContainer.isUiWebview().isNative;
|
||||
this.cdr.detectChanges();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<shared-goods-in-out-order-details (actionHandled)="actionHandled($event)">
|
||||
<shared-goods-in-out-order-details-header (editClick)="navigateToEditPage($event)"></shared-goods-in-out-order-details-header>
|
||||
<shared-goods-in-out-order-details-item [orderItem]="selectedItem$ | async"></shared-goods-in-out-order-details-item>
|
||||
<shared-goods-in-out-order-details-item
|
||||
[orderItem]="selectedItem$ | async"
|
||||
[order]="order$ | async"
|
||||
></shared-goods-in-out-order-details-item>
|
||||
<shared-goods-in-out-order-details-tags></shared-goods-in-out-order-details-tags>
|
||||
<shared-goods-in-out-order-details-covers
|
||||
[orderItems]="items$ | async"
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { DomainGoodsService, OrderItemsContext } from '@domain/oms';
|
||||
import { DomainGoodsService, DomainOmsService, OrderItemsContext } from '@domain/oms';
|
||||
import { ComponentStore, tapResponse } from '@ngrx/component-store';
|
||||
import { OrderItemListItemDTO, OrderItemProcessingStatusValue } from '@swagger/oms';
|
||||
import { combineLatest, Subject } from 'rxjs';
|
||||
import { debounceTime, first, map, switchMap, takeUntil, tap, withLatestFrom } from 'rxjs/operators';
|
||||
import { debounceTime, filter, first, map, shareReplay, switchMap, takeUntil, tap, withLatestFrom } from 'rxjs/operators';
|
||||
|
||||
export interface GoodsInDetailsComponentState {
|
||||
fetching: boolean;
|
||||
@@ -13,6 +13,7 @@ export interface GoodsInDetailsComponentState {
|
||||
processingStatus?: OrderItemProcessingStatusValue;
|
||||
selectedOrderItemId?: number;
|
||||
items?: OrderItemListItemDTO[];
|
||||
orderId?: number;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@@ -28,6 +29,8 @@ export class GoodsInDetailsComponent extends ComponentStore<GoodsInDetailsCompon
|
||||
|
||||
items$ = this.select((s) => s.items ?? []);
|
||||
|
||||
orderId$ = this.select((s) => s.orderId);
|
||||
|
||||
get items() {
|
||||
return this.get((s) => s.items);
|
||||
}
|
||||
@@ -38,6 +41,12 @@ export class GoodsInDetailsComponent extends ComponentStore<GoodsInDetailsCompon
|
||||
map(([items, processingStatus]) => items.filter((item) => item.processingStatus === processingStatus))
|
||||
);
|
||||
|
||||
order$ = this.orderId$.pipe(
|
||||
filter((orderId) => !!orderId),
|
||||
switchMap((orderId) => this._omsService.getOrder(orderId)),
|
||||
shareReplay()
|
||||
);
|
||||
|
||||
selectedItem$ = combineLatest([this.selectedOrderItemId$, this.itemsWithProcessingStatus$]).pipe(
|
||||
map(([orderItemId, items]) => items.find((item) => item.orderItemId === orderItemId) || items[0])
|
||||
);
|
||||
@@ -47,6 +56,7 @@ export class GoodsInDetailsComponent extends ComponentStore<GoodsInDetailsCompon
|
||||
constructor(
|
||||
private _activatedRoute: ActivatedRoute,
|
||||
private _domainGoodsInService: DomainGoodsService,
|
||||
private _omsService: DomainOmsService,
|
||||
private _breadcrumb: BreadcrumbService,
|
||||
private _router: Router
|
||||
) {
|
||||
@@ -117,6 +127,7 @@ export class GoodsInDetailsComponent extends ComponentStore<GoodsInDetailsCompon
|
||||
// Anzeige von Teilabholung in Wareneingang ausblenden
|
||||
actions: item.actions.filter((action) => action.command !== 'FETCHED_PARTIAL'),
|
||||
})),
|
||||
orderId: res.result[0].orderId,
|
||||
});
|
||||
this.updateBreadcrumb(
|
||||
res.result.find((item) => item.orderItemId === selectedOrderItemId && item.processingStatus === processingStatus)
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<div class="container">
|
||||
<shared-goods-in-out-order-edit (navigation)="navigateToDetailsPage()" [items]="items$ | async"></shared-goods-in-out-order-edit>
|
||||
<shared-goods-in-out-order-edit (navigation)="navigateToDetailsPage($event)" [items]="items$ | async"></shared-goods-in-out-order-edit>
|
||||
</div>
|
||||
|
||||
@@ -45,10 +45,10 @@ export class GoodsInEditComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
navigateToDetailsPage() {
|
||||
navigateToDetailsPage({ options }: { options?: { processingStatus?: number } }) {
|
||||
const orderNumber = this._activatedRoute.snapshot.params.orderNumber;
|
||||
const orderItemId = this._activatedRoute.snapshot.params.orderItemId;
|
||||
const processingStatus = this._activatedRoute.snapshot.params.processingStatus;
|
||||
const processingStatus = options?.processingStatus ? options.processingStatus : this._activatedRoute.snapshot.params.processingStatus;
|
||||
this._router.navigate([`/goods/in/details/order/${encodeURIComponent(orderNumber)}/item/${orderItemId}/${processingStatus}`]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +86,10 @@
|
||||
<div class="quantity spec">
|
||||
<strong>{{ orderItem.quantity }}x</strong>
|
||||
</div>
|
||||
<div class="order-date spec">
|
||||
<span>Bestelldatum</span>
|
||||
<strong>{{ orderItem.orderDate | date: 'dd.MM.yy' }}</strong>
|
||||
</div>
|
||||
<div class="date spec">
|
||||
<span>vsl. Lieferdatum</span>
|
||||
<strong>{{ orderItem.estimatedShippingDate | date: 'dd.MM.yy' }}</strong>
|
||||
|
||||
@@ -65,8 +65,9 @@
|
||||
}
|
||||
|
||||
.details {
|
||||
@apply grid grid-cols-2 grid-flow-row gap-1;
|
||||
grid-template-areas: 'format supplier' 'ean item-id' 'price status' 'quantity date';
|
||||
@apply grid grid-flow-row gap-1;
|
||||
grid-template-columns: 40% 60%;
|
||||
grid-template-areas: 'format supplier' 'ean item-id' 'price status' 'quantity order-date' 'empty date';
|
||||
grid-area: details;
|
||||
}
|
||||
|
||||
@@ -104,6 +105,10 @@
|
||||
grid-area: quantity;
|
||||
}
|
||||
|
||||
.order-date {
|
||||
grid-area: order-date;
|
||||
}
|
||||
|
||||
.date {
|
||||
grid-area: date;
|
||||
}
|
||||
@@ -114,6 +119,10 @@
|
||||
span:nth-child(1) {
|
||||
min-width: 150px;
|
||||
}
|
||||
|
||||
strong {
|
||||
@apply break-all;
|
||||
}
|
||||
}
|
||||
|
||||
.processing-status {
|
||||
|
||||
@@ -5,7 +5,13 @@
|
||||
|
||||
<div class="goods-in-search-filter-content-main">
|
||||
<h1 class="title">Filter</h1>
|
||||
<ui-filter [filter]="filter" [loading]="loading$ | async" (search)="applyFilter()" [hint]="message"></ui-filter>
|
||||
<ui-filter
|
||||
[filter]="filter"
|
||||
[loading]="loading$ | async"
|
||||
(search)="applyFilter()"
|
||||
[hint]="message"
|
||||
resizeInputOptionsToElement="page-goods-in-search-filter .cta-wrapper"
|
||||
></ui-filter>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import { catchError, map } from 'rxjs/operators';
|
||||
|
||||
@Injectable()
|
||||
export class GoodsInSearchMainAutocompleteProvider extends UiFilterAutocompleteProvider {
|
||||
for = 'main';
|
||||
for = 'goods-in';
|
||||
|
||||
constructor(private domainGoodsInService: DomainGoodsService) {
|
||||
super();
|
||||
|
||||
@@ -38,6 +38,7 @@ import {
|
||||
ShippingNoteActionHandler,
|
||||
SupplierTemporarilyOutOfStockActionHandler,
|
||||
ReOrderedActionHandler,
|
||||
CollectOnDeliveryNoteActionHandler,
|
||||
} from '@domain/oms';
|
||||
@NgModule({
|
||||
declarations: [GoodsInComponent],
|
||||
@@ -79,6 +80,7 @@ import {
|
||||
ReturnedByBuyerActionHandler,
|
||||
ShippingNoteActionHandler,
|
||||
SupplierTemporarilyOutOfStockActionHandler,
|
||||
CollectOnDeliveryNoteActionHandler,
|
||||
]),
|
||||
],
|
||||
exports: [GoodsInComponent],
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<shared-goods-in-out-order-details-item
|
||||
*ngFor="let item of itemsWithProcessingStatus$ | async"
|
||||
[orderItem]="item"
|
||||
[order]="order$ | async"
|
||||
></shared-goods-in-out-order-details-item>
|
||||
<shared-goods-in-out-order-details-tags></shared-goods-in-out-order-details-tags>
|
||||
</shared-goods-in-out-order-details>
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { DomainGoodsService, OrderItemsContext } from '@domain/oms';
|
||||
import { DomainGoodsService, DomainOmsService, OrderItemsContext } from '@domain/oms';
|
||||
import { ComponentStore, tapResponse } from '@ngrx/component-store';
|
||||
import { ListResponseArgsOfOrderItemListItemDTO, OrderItemListItemDTO, OrderItemProcessingStatusValue } from '@swagger/oms';
|
||||
import { Command } from 'protractor';
|
||||
import { combineLatest, Observable, Subject, Subscription } from 'rxjs';
|
||||
import { debounceTime, first, map, switchMap, takeUntil, tap, withLatestFrom } from 'rxjs/operators';
|
||||
import { combineLatest, Observable, Subject } from 'rxjs';
|
||||
import { debounceTime, filter, first, map, shareReplay, switchMap, takeUntil, tap, withLatestFrom } from 'rxjs/operators';
|
||||
|
||||
export interface GoodsOutDetailsComponentState {
|
||||
fetching: boolean;
|
||||
@@ -14,6 +13,7 @@ export interface GoodsOutDetailsComponentState {
|
||||
processingStatus?: OrderItemProcessingStatusValue;
|
||||
compartmentCode?: string;
|
||||
items?: OrderItemListItemDTO[];
|
||||
orderId?: number;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@@ -31,6 +31,8 @@ export class GoodsOutDetailsComponent extends ComponentStore<GoodsOutDetailsComp
|
||||
|
||||
items$ = this.select((s) => s.items ?? []);
|
||||
|
||||
orderId$ = this.select((s) => s.orderId);
|
||||
|
||||
get items() {
|
||||
return this.get((s) => s.items);
|
||||
}
|
||||
@@ -39,11 +41,18 @@ export class GoodsOutDetailsComponent extends ComponentStore<GoodsOutDetailsComp
|
||||
map(([items, processingStatus]) => items.filter((item) => item.processingStatus === processingStatus))
|
||||
);
|
||||
|
||||
order$ = this.orderId$.pipe(
|
||||
filter((orderId) => !!orderId),
|
||||
switchMap((orderId) => this._omsService.getOrder(orderId)),
|
||||
shareReplay()
|
||||
);
|
||||
|
||||
private _onDestroy$ = new Subject();
|
||||
|
||||
constructor(
|
||||
private _activatedRoute: ActivatedRoute,
|
||||
private _domainGoodsInService: DomainGoodsService,
|
||||
private _omsService: DomainOmsService,
|
||||
private _breadcrumb: BreadcrumbService,
|
||||
private _router: Router
|
||||
) {
|
||||
@@ -121,6 +130,7 @@ export class GoodsOutDetailsComponent extends ComponentStore<GoodsOutDetailsComp
|
||||
(res) => {
|
||||
this.patchState({
|
||||
items: res.result,
|
||||
orderId: res.result[0].orderId,
|
||||
});
|
||||
this.updateBreadcrumb(res.result.find((item) => item.processingStatus === processingStatus));
|
||||
},
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<div class="container" *ngIf="items$ | async; let items">
|
||||
<shared-goods-in-out-order-edit (navigation)="navigateToDetailsPage()" [items]="items"></shared-goods-in-out-order-edit>
|
||||
<shared-goods-in-out-order-edit (navigation)="navigateToDetailsPage($event)" [items]="items"></shared-goods-in-out-order-edit>
|
||||
</div>
|
||||
|
||||
@@ -59,10 +59,10 @@ export class GoodsOutEditComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
navigateToDetailsPage() {
|
||||
navigateToDetailsPage({ options }: { options?: { processingStatus?: number } }) {
|
||||
const orderNumber = this._activatedRoute.snapshot.params.orderNumber;
|
||||
const compartmentCode = this._activatedRoute.snapshot.params.compartmentCode;
|
||||
const processingStatus = this._activatedRoute.snapshot.params.processingStatus;
|
||||
const processingStatus = options?.processingStatus ? options.processingStatus : this._activatedRoute.snapshot.params.processingStatus;
|
||||
compartmentCode
|
||||
? this._router.navigate([`/goods/out/details/compartment/${encodeURIComponent(compartmentCode)}/${processingStatus}`])
|
||||
: this._router.navigate([`/goods/out/details/order/${encodeURIComponent(orderNumber)}/${processingStatus}`]);
|
||||
|
||||
@@ -5,7 +5,13 @@
|
||||
|
||||
<div class="goods-out-search-filter-content-main">
|
||||
<h1 class="title">Filter</h1>
|
||||
<ui-filter [filter]="filter" [loading]="loading$ | async" (search)="applyFilter()" [hint]="message"></ui-filter>
|
||||
<ui-filter
|
||||
[filter]="filter"
|
||||
[loading]="loading$ | async"
|
||||
(search)="applyFilter()"
|
||||
[hint]="message"
|
||||
resizeInputOptionsToElement="page-goods-out-search-filter .cta-wrapper"
|
||||
></ui-filter>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import { catchError, map } from 'rxjs/operators';
|
||||
|
||||
@Injectable()
|
||||
export class GoodsOutSearchMainAutocompleteProvider extends UiFilterAutocompleteProvider {
|
||||
for = 'main';
|
||||
for = 'goods-out';
|
||||
|
||||
constructor(private domainGoodsInService: DomainGoodsService) {
|
||||
super();
|
||||
|
||||
@@ -82,9 +82,9 @@ export class GoodsOutSearchMainComponent implements OnInit, OnDestroy {
|
||||
if (result.hits === 1) {
|
||||
const orderItem = result.result[0];
|
||||
this._router.navigate([this.getDetailsPath(orderItem)]);
|
||||
} else {
|
||||
this._router.navigate(['goods', 'out', 'results'], { queryParams: this._goodsOutSearchStore.filter.getQueryParams() });
|
||||
}
|
||||
|
||||
this._router.navigate(['goods', 'out', 'results'], { queryParams: this._goodsOutSearchStore.filter.getQueryParams() });
|
||||
} else {
|
||||
this.message = 'keine Suchergebnisse';
|
||||
}
|
||||
|
||||
@@ -181,7 +181,10 @@ export class GoodsOutSearchResultsComponent extends ComponentStore<GoodsOutSearc
|
||||
}
|
||||
|
||||
selectionRules(action: KeyValueDTOOfStringAndString) {
|
||||
return action.command.includes('FETCHED') && action.key === '256' && action.enabled !== false;
|
||||
return (
|
||||
(action.command.includes('FETCHED') && action.key === '256' && action.enabled !== false) ||
|
||||
action.command.includes('COLLECT_ON_DELIVERYNOTE')
|
||||
);
|
||||
}
|
||||
|
||||
async handleAction(action: KeyValueDTOOfStringAndString) {
|
||||
|
||||
@@ -37,6 +37,7 @@ import {
|
||||
ShippingNoteActionHandler,
|
||||
SupplierTemporarilyOutOfStockActionHandler,
|
||||
ReOrderedActionHandler,
|
||||
CollectOnDeliveryNoteActionHandler,
|
||||
} from '@domain/oms';
|
||||
import { CoreCommandModule } from '@core/command';
|
||||
import { ShellBreadcrumbModule } from '@shell/breadcrumb';
|
||||
@@ -83,6 +84,7 @@ import { GoodsInRoutingModule } from './good-out-routing.module';
|
||||
ReturnedByBuyerActionHandler,
|
||||
ShippingNoteActionHandler,
|
||||
SupplierTemporarilyOutOfStockActionHandler,
|
||||
CollectOnDeliveryNoteActionHandler,
|
||||
]),
|
||||
],
|
||||
exports: [GoodsOutComponent],
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<hr />
|
||||
<div class="update-comment">
|
||||
<div class="update-comment-heading">
|
||||
Update-Notiz
|
||||
Kommentar
|
||||
</div>
|
||||
|
||||
<ui-icon icon="refresh" size="19px"></ui-icon>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
[render-text]="true"
|
||||
[(page)]="page"
|
||||
(after-load-complete)="callBackFn($event)"
|
||||
[fit-to-page]="true"
|
||||
zoom-scale="page-height"
|
||||
></pdf-viewer>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
:host {
|
||||
@apply grid grid-cols-1 relative;
|
||||
@apply grid grid-cols-1 relative overflow-auto;
|
||||
max-height: calc(100vh - 4rem);
|
||||
}
|
||||
|
||||
@@ -8,16 +8,18 @@ h5 {
|
||||
}
|
||||
|
||||
pdf-viewer {
|
||||
@apply flex-grow overflow-y-auto max-h-full;
|
||||
@apply flex-grow mb-4 max-h-full overflow-auto;
|
||||
|
||||
::ng-deep .canvasWrapper {
|
||||
@apply border-2 border-gray-200 border-solid rounded-card;
|
||||
max-width: 99%;
|
||||
}
|
||||
}
|
||||
|
||||
button.cta-print {
|
||||
@apply sticky bottom-6 bg-brand text-xl px-6 py-3 text-white rounded-full border-none outline-none font-bold shadow-cta;
|
||||
@apply sticky bg-brand text-xl px-6 py-3 text-white rounded-full border-none outline-none font-bold shadow-cta;
|
||||
transform: translateX(-50%);
|
||||
left: 50%;
|
||||
bottom: 5.5rem;
|
||||
width: 175px;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<lib-offline-overlay>
|
||||
<!-- lib offline depends on these three elements to be present inside of it -->
|
||||
<app-header [ngClass]="{ loading: loading }" *ngIf="authenticated"></app-header>
|
||||
<app-content [ngClass]="{ loading: loading }"></app-content>
|
||||
<app-menu [ngClass]="{ loading: loading }" *ngIf="authenticated"></app-menu>
|
||||
<img *ngIf="loading" src="/assets/images/Icon_Loading.svg" class="app-loader" />
|
||||
<app-header [ngClass]="{ loading: loading$ | async }" *ngIf="authenticated"></app-header>
|
||||
<app-content [ngClass]="{ loading: loading$ | async }"></app-content>
|
||||
<app-menu [ngClass]="{ loading: loading$ | async }" *ngIf="authenticated"></app-menu>
|
||||
<img *ngIf="loading$ | async" src="/assets/images/Icon_Loading.svg" class="app-loader" />
|
||||
</lib-offline-overlay>
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { Component, HostBinding, Inject, OnDestroy, OnInit, Renderer2 } from '@angular/core';
|
||||
import { Router, NavigationEnd, NavigationStart } from '@angular/router';
|
||||
import { ChangeDetectorRef, Component, Inject, OnDestroy, OnInit, Renderer2 } from '@angular/core';
|
||||
import { Router, NavigationEnd } from '@angular/router';
|
||||
import { Store } from '@ngxs/store';
|
||||
import { Subject } from 'rxjs';
|
||||
import { BehaviorSubject, Subject } from 'rxjs';
|
||||
import { take, filter, takeUntil } from 'rxjs/operators';
|
||||
import { SsoService } from 'sso';
|
||||
import { AppService } from './core/services/app.service';
|
||||
import { ModuleSwitcher } from './core/models/app-switcher.enum';
|
||||
import { AppState } from './core/store/state/app.state';
|
||||
import { AppConfiguration } from './app-configuration';
|
||||
import { NativeContainerService } from 'shared/lib/barcode-scanner';
|
||||
@@ -23,7 +22,7 @@ import { DeviceDetectorService } from 'ngx-device-detector';
|
||||
})
|
||||
export class AppComponent implements OnInit, OnDestroy {
|
||||
title = 'Hugendubel InstoreApp';
|
||||
loading = true;
|
||||
loading$ = new BehaviorSubject(true);
|
||||
includeGoogleAnalytics = this.config.includeGoogleAnalytics;
|
||||
destroy$ = new Subject();
|
||||
|
||||
@@ -45,6 +44,14 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
private ngxsActions$: NgxsActions,
|
||||
private deviceDetectorService: DeviceDetectorService
|
||||
) {
|
||||
this.appService.loader$.subscribe(() => {
|
||||
this.loading$.next(false);
|
||||
});
|
||||
|
||||
this.appService.loader$.pipe(take(1)).subscribe(() => {
|
||||
this.containerNotificationMessage();
|
||||
});
|
||||
|
||||
// intialisations done only when app loads
|
||||
this.appService.appLoadInitialisations();
|
||||
|
||||
@@ -82,11 +89,6 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.appService.loader$.pipe(take(1)).subscribe(() => {
|
||||
this.loading = false;
|
||||
this.containerNotificationMessage();
|
||||
});
|
||||
|
||||
if (this.includeGoogleAnalytics) {
|
||||
this.initGoogleAnalytics();
|
||||
}
|
||||
|
||||
@@ -11,12 +11,12 @@
|
||||
</div>
|
||||
<div class="align-center">
|
||||
<div class="icons-grid pt-5">
|
||||
<div class="align-center">
|
||||
<div class="header-item align-center">
|
||||
<button class="header-icon dashboard" type="button" [routerLink]="['/dashboard']" routerLinkActive="active">
|
||||
<ui-icon icon="dashboard" size="26px"></ui-icon>
|
||||
</button>
|
||||
</div>
|
||||
<div class="align-center">
|
||||
<div class="header-item align-center">
|
||||
<button
|
||||
class="header-icon notification"
|
||||
type="button"
|
||||
@@ -28,7 +28,10 @@
|
||||
<ui-icon icon="notification" size="26px"></ui-icon>
|
||||
</button>
|
||||
</div>
|
||||
<div class="align-center">
|
||||
<div class="header-item align-center">
|
||||
<span class="current-branch" *ngIf="currentBranch$ | async; let currentBranch" [title]="currentBranch.name">
|
||||
{{ currentBranch.key | uppercase }}
|
||||
</span>
|
||||
<button class="header-icon logout" type="button" (click)="logoff()">
|
||||
<ui-icon icon="logout" size="26px"></ui-icon>
|
||||
</button>
|
||||
|
||||
@@ -14,6 +14,10 @@ button.header-icon {
|
||||
@apply text-inactive-customer;
|
||||
}
|
||||
}
|
||||
|
||||
.current-branch {
|
||||
@apply text-wild-blue-yonder;
|
||||
}
|
||||
}
|
||||
|
||||
::ng-deep .branch app-header {
|
||||
@@ -26,6 +30,10 @@ button.header-icon {
|
||||
@apply text-active-branch;
|
||||
}
|
||||
}
|
||||
|
||||
.current-branch {
|
||||
@apply text-cool-grey;
|
||||
}
|
||||
}
|
||||
|
||||
button.notification {
|
||||
@@ -37,6 +45,14 @@ button.notification {
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.header-item {
|
||||
@apply flex;
|
||||
|
||||
.current-branch {
|
||||
@apply flex items-center font-bold;
|
||||
}
|
||||
}
|
||||
|
||||
.header-wrapper {
|
||||
background-color: white;
|
||||
position: fixed;
|
||||
|
||||
@@ -12,6 +12,7 @@ import { ApplicationService } from '@core/application';
|
||||
import { NotificationsHub } from '@hub/notifications';
|
||||
import { UiModalService } from '@ui/modal';
|
||||
import { ModalNotificationsComponent } from 'apps/modal/notifications/src/public-api';
|
||||
import { DomainAvailabilityService } from '@domain/availability';
|
||||
|
||||
@Component({
|
||||
selector: 'app-header',
|
||||
@@ -28,6 +29,8 @@ export class HeaderComponent implements OnInit, OnDestroy {
|
||||
|
||||
notificationCount$ = this.notifications$.pipe(map((message) => message?.data?.length));
|
||||
|
||||
currentBranch$ = this._availabilityService.getCurrentBranch();
|
||||
|
||||
constructor(
|
||||
private store: Store,
|
||||
private router: Router,
|
||||
@@ -35,7 +38,8 @@ export class HeaderComponent implements OnInit, OnDestroy {
|
||||
private applicationService: ApplicationService,
|
||||
private cdr: ChangeDetectorRef,
|
||||
private _notificationsHub: NotificationsHub,
|
||||
private _modal: UiModalService
|
||||
private _modal: UiModalService,
|
||||
private _availabilityService: DomainAvailabilityService
|
||||
) {}
|
||||
|
||||
customer = 'Kunden';
|
||||
@@ -64,10 +68,6 @@ export class HeaderComponent implements OnInit, OnDestroy {
|
||||
this.doubleChoiceSwitch.isFirstSwitchedOn = section === 'customer';
|
||||
this.cdr.markForCheck();
|
||||
});
|
||||
|
||||
this._notificationsHub.notifications$.subscribe((data) => {
|
||||
console.log('signalr - notifications', data);
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
</div>
|
||||
<div
|
||||
class="menu-item-grid align-center active"
|
||||
(click)="routeToMenu('/customer/search', 'customersearch', { customertype: 'b2b;store;loyalty;webshop' })"
|
||||
(click)="routeToMenu('/customer/search', 'customersearch', { customertype: 'store;loyalty;webshop' })"
|
||||
>
|
||||
<div>
|
||||
<lib-icon
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
@import 'variables';
|
||||
|
||||
.grid-container {
|
||||
display: grid;
|
||||
grid-column-gap: 1vh;
|
||||
@apply grid grid-flow-col;
|
||||
grid-template-columns: 1fr auto;
|
||||
}
|
||||
|
||||
.align-left {
|
||||
@apply grid grid-flow-col;
|
||||
grid-template-columns: auto 1fr auto;
|
||||
}
|
||||
|
||||
.grid-container-fix-width-last-col {
|
||||
|
||||
@@ -73,7 +73,7 @@ export class AppService implements OnDestroy {
|
||||
});
|
||||
|
||||
this.ssoService.registerAuthentication().then((authenticated) => {
|
||||
if (authenticated) {
|
||||
if (!!authenticated) {
|
||||
this.loadState();
|
||||
// this.loadVats();
|
||||
// this.loadSuppliers();
|
||||
@@ -141,6 +141,8 @@ export class AppService implements OnDestroy {
|
||||
this.loader$.next();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.loader$.next();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
padding: 20px 20px 20px 25px;
|
||||
padding: 20px 10px 20px 25px;
|
||||
|
||||
.title {
|
||||
font-size: 15px;
|
||||
@@ -37,12 +37,15 @@
|
||||
}
|
||||
|
||||
.text {
|
||||
width: 100%;
|
||||
padding-top: 5px;
|
||||
font-size: 16px;
|
||||
color: #000000;
|
||||
text-align: left;
|
||||
line-height: 22px;
|
||||
white-space: pre-wrap;
|
||||
line-break: auto;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,9 +8,9 @@ import { isNullOrUndefined } from 'util';
|
||||
import { AppService } from '@sales/core-services';
|
||||
import { RemissionService, ShippingDocument, RemissionProcess } from '@isa/remission';
|
||||
import { SetRemissionShippingDocument } from 'apps/sales/src/app/core/store/actions/remission.actions';
|
||||
import { NativeContainerService } from 'shared/public_api';
|
||||
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
|
||||
import { UiSearchboxNextComponent } from '@ui/searchbox';
|
||||
import { NativeContainerService } from 'native-container';
|
||||
|
||||
@Component({
|
||||
selector: 'app-shipping-document-creation',
|
||||
@@ -47,9 +47,7 @@ export class ShippingDocumentCreationComponent implements OnInit {
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.showScanningButton$ = this.enableScan$.pipe(
|
||||
map((allowScan) => !!allowScan && this.appService.isIPadEnv() && this.nativeService.isUiWebview())
|
||||
);
|
||||
this.showScanningButton$ = this.enableScan$.pipe(map((allowScan) => !!allowScan && this.nativeService.isUiWebview().isNative));
|
||||
|
||||
this.showSearchbox$ = combineLatest([this.currentRemissionProcess$, this.showScanningButton$]).pipe(
|
||||
map(([process, showScanButton]) => process?.filter?.target?.name === 'Blank' && !showScanButton)
|
||||
|
||||
12
apps/sales/src/assets/bookmark_branch.svg
Normal file
12
apps/sales/src/assets/bookmark_branch.svg
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="48px" height="48px" viewBox="0 0 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Group 3 Copy 2</title>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Tablet_ISA_Filiale_Wareneingang_Detailseite_004_Flow-einbuchen_003" transform="translate(-692.000000, -377.000000)">
|
||||
<g id="Group-3-Copy-2" transform="translate(692.000000, 377.000000)">
|
||||
<path d="M12.4449999,-1.77635684e-15 L43.5550001,-1.77635684e-15 C46.0099058,-2.22731569e-15 48,1.99009422 48,4.44499986 L48,4.47368421 L48,4.47368421 L8,4.47368421 L8,4.44499986 C8,1.99009422 9.99009422,-1.32539799e-15 12.4449999,-1.77635684e-15 Z" id="Rectangle" fill="#262B31"></path>
|
||||
<path d="M4.44499986,0 L42.7806818,0 L42.7806818,0 L42.7806818,43.4807986 C42.7806818,45.9357043 40.7905876,47.9257985 38.335682,47.9257985 C37.6435186,47.9257985 36.9609195,47.7641551 36.342263,47.4537452 L23.3760316,40.9479615 C22.1211621,40.3183329 20.6425928,40.3185691 19.3879245,40.9485986 L6.43968804,47.4505326 C4.24584207,48.5521685 1.57432621,47.6667572 0.472690352,45.4729113 C0.161867564,44.8539251 -8.03354089e-16,44.1708663 0,43.4782231 L0,4.44499986 C-3.00639233e-16,1.99009422 1.99009422,1.33913727e-15 4.44499986,0 Z" id="Rectangle" fill="#596470"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
18
apps/sales/src/assets/bookmark_customer.svg
Normal file
18
apps/sales/src/assets/bookmark_customer.svg
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="67px" height="64px" viewBox="0 0 67 64" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Group 3 Copy 2</title>
|
||||
<defs>
|
||||
<filter x="-40.5%" y="-45.3%" width="181.1%" height="190.7%" filterUnits="objectBoundingBox" id="filter-1">
|
||||
<feGaussianBlur stdDeviation="5" in="SourceGraphic"></feGaussianBlur>
|
||||
</filter>
|
||||
</defs>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="01_ISA_HIMA_Abholfach_Warenausgang_Trefferliste_01_2_Radio-Buttons-active_02_nochnichtbezahlt_sichtbarerBereich" transform="translate(-680.000000, -622.000000)">
|
||||
<g id="Group-3-Copy-2" transform="translate(692.000000, 623.000000)">
|
||||
<path d="M12.4449999,-1.77635684e-15 L43.5550001,-1.77635684e-15 C46.0099058,-2.22731569e-15 48,1.99009422 48,4.44499986 L48,4.47368421 L48,4.47368421 L8,4.47368421 L8,4.44499986 C8,1.99009422 9.99009422,-1.32539799e-15 12.4449999,-1.77635684e-15 Z" id="Rectangle" fill="#172062"></path>
|
||||
<path d="M7.44499986,14 L40,14 L40,14 L40,42.6321632 C40,45.0870688 38.0099058,47.0771631 35.5550001,47.0771631 C34.8628368,47.0771631 34.1802377,46.9155196 33.5615812,46.6051098 L23.4868424,41.5501273 C22.2319729,40.9204988 20.7534035,40.920735 19.4987352,41.5507644 L9.43968804,46.6018971 C7.24584207,47.703533 4.57432621,46.8181218 3.47269035,44.6242758 C3.16186756,44.0052897 3,43.3222309 3,42.6295876 L3,18.4449999 C3,15.9900942 4.99009422,14 7.44499986,14 Z" id="Rectangle" fill-opacity="0.4" fill="#172062" filter="url(#filter-1)"></path>
|
||||
<path d="M4.44499986,0 L42.7806818,0 L42.7806818,0 L42.7806818,43.4807986 C42.7806818,45.9357043 40.7905876,47.9257985 38.335682,47.9257985 C37.6435186,47.9257985 36.9609195,47.7641551 36.342263,47.4537452 L23.3760316,40.9479615 C22.1211621,40.3183329 20.6425928,40.3185691 19.3879245,40.9485986 L6.43968804,47.4505326 C4.24584207,48.5521685 1.57432621,47.6667572 0.472690352,45.4729113 C0.161867564,44.8539251 -8.03354089e-16,44.1708663 0,43.4782231 L0,4.44499986 C-3.00639233e-16,1.99009422 1.99009422,1.33913727e-15 4.44499986,0 Z" id="Rectangle" fill="#5A728A"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 102 KiB |
@@ -1,7 +1,7 @@
|
||||
export const ProcessingStatusNameMap = new Map<number, { value: string; disabled: boolean }>([
|
||||
[1, { value: 'neu', disabled: true }],
|
||||
[2, { value: '', disabled: true }],
|
||||
[4, { value: '', disabled: true }],
|
||||
[1, { value: 'neu (angelegt)', disabled: true }],
|
||||
[2, { value: 'neu (angelegt)', disabled: true }],
|
||||
[4, { value: 'neu (übernommen)', disabled: true }],
|
||||
[8, { value: 'geparkt', disabled: true }],
|
||||
[16, { value: 'bestellt', disabled: false }],
|
||||
[32, { value: 'Vorbereitung Versand', disabled: true }],
|
||||
|
||||
@@ -69,23 +69,23 @@ export class SharedGoodsInOutOrderDetailsCoversComponent extends ComponentStore<
|
||||
this.subscriptions.add(
|
||||
this.orderItems$
|
||||
.pipe(
|
||||
map(
|
||||
(orderItems) =>
|
||||
orderItems
|
||||
?.filter((a) => a.processingStatus === 128)
|
||||
?.filter((a) => {
|
||||
if (a.readyForPickUp) {
|
||||
const readyForPickUp = new Date(a.readyForPickUp);
|
||||
return this.dateAdapter.compareDate(readyForPickUp, this.dateAdapter.today()) === 0;
|
||||
}
|
||||
return false;
|
||||
})
|
||||
?.sort((a, b) => new Date(b.readyForPickUp).getTime() - new Date(a.readyForPickUp).getTime())
|
||||
?.find((_) => true)?.compartmentCode
|
||||
map((orderItems) =>
|
||||
orderItems
|
||||
?.filter((a) => a.processingStatus === 128)
|
||||
?.filter((a) => {
|
||||
if (a.readyForPickUp) {
|
||||
const readyForPickUp = new Date(a.readyForPickUp);
|
||||
return this.dateAdapter.compareDate(readyForPickUp, this.dateAdapter.today()) === 0;
|
||||
}
|
||||
return false;
|
||||
})
|
||||
?.sort((a, b) => new Date(b.readyForPickUp).getTime() - new Date(a.readyForPickUp).getTime())
|
||||
?.find((_) => true)
|
||||
)
|
||||
)
|
||||
.subscribe((compartmentCode) => {
|
||||
this.host.latestCompartmentCode = compartmentCode;
|
||||
.subscribe((orderItem) => {
|
||||
this.host.latestCompartmentCode = orderItem?.compartmentCode;
|
||||
this.host.latestCompartmentInfo = orderItem?.compartmentInfo;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -120,21 +120,27 @@
|
||||
<div class="label">Abholfrist</div>
|
||||
<div *ngIf="!(changeDateLoader$ | async)" class="value">
|
||||
<button
|
||||
[uiOverlayTrigger]="deadlineDropdown"
|
||||
#deadlineDropdownTrigger="uiOverlayTrigger"
|
||||
[uiOverlayTrigger]="deadlineDatepicker"
|
||||
#deadlineDatepickerTrigger="uiOverlayTrigger"
|
||||
[disabled]="!!orderItem?.features?.paid || (changeDateDisabled$ | async)"
|
||||
class="cta-pickup-deadline"
|
||||
>
|
||||
<strong>
|
||||
{{ orderItem?.pickUpDeadline | date: 'dd.MM.yy' }}
|
||||
</strong>
|
||||
<ui-icon [rotate]="deadlineDropdownTrigger.opened ? '270deg' : '90deg'" icon="arrow_head"></ui-icon>
|
||||
<ui-icon [rotate]="deadlineDatepickerTrigger.opened ? '270deg' : '90deg'" icon="arrow_head"></ui-icon>
|
||||
</button>
|
||||
<ui-dropdown #deadlineDropdown yPosition="below" xPosition="after" class="deadline-select" [xOffset]="8">
|
||||
<button uiDropdownItem *ngFor="let dl of pickupDeadlines$ | async | keyvalue" (click)="updatePickupDeadline(dl.value)">
|
||||
{{ dl.key }}
|
||||
</button>
|
||||
</ui-dropdown>
|
||||
<ui-datepicker
|
||||
#deadlineDatepicker
|
||||
yPosition="below"
|
||||
xPosition="after"
|
||||
[xOffset]="8"
|
||||
[min]="minDateDatepicker"
|
||||
[disabledDaysOfWeek]="[0]"
|
||||
[selected]="orderItem?.pickUpDeadline"
|
||||
(save)="updatePickupDeadline($event)"
|
||||
>
|
||||
</ui-datepicker>
|
||||
</div>
|
||||
<ui-spinner *ngIf="changeDateLoader$ | async; let loader" class="value loader" [show]="loader"></ui-spinner>
|
||||
</ng-template>
|
||||
|
||||
@@ -56,16 +56,6 @@ export class SharedGoodsInOutOrderDetailsHeaderComponent {
|
||||
shareReplay()
|
||||
);
|
||||
|
||||
pickupDeadlines$ = this.orderItem$.pipe(
|
||||
filter((orderItem) => !!orderItem),
|
||||
map((orderItem) => {
|
||||
return new Map<string, Date>([
|
||||
['um 2 Wochen verlängern', this.dateAdapter.addCalendarDays(new Date(orderItem.pickUpDeadline), 14)],
|
||||
['um 4 Wochen verlängern', this.dateAdapter.addCalendarDays(new Date(orderItem.pickUpDeadline), 28)],
|
||||
]);
|
||||
})
|
||||
);
|
||||
|
||||
statusActions$ = this.orderItem$.pipe(map((orderItem) => orderItem?.actions?.filter((action) => action.enabled === false)));
|
||||
|
||||
showMultiselect$ = combineLatest([this.host.orderItems$, this.host.fetchPartial$, this.host.itemsSelectable$]).pipe(
|
||||
|
||||
@@ -1,19 +1,28 @@
|
||||
<ng-container *ngIf="orderItem$ | async; let orderItem">
|
||||
<div class="goods-in-out-order-details-features">
|
||||
<img *ngIf="orderItem?.features?.prebooked" src="/assets/images/tag_icon_preorder.svg" [alt]="orderItem?.features?.prebooked" />
|
||||
<ng-container *ngIf="orderItem?.features?.prebooked">
|
||||
<img [uiOverlayTrigger]="prebookedTooltip" src="/assets/images/tag_icon_preorder.svg" [alt]="orderItem?.features?.prebooked" />
|
||||
<ui-tooltip yPosition="above" xPosition="after" [yOffset]="-11" [xOffset]="-8" #prebookedTooltip [closeable]="true">
|
||||
Artikel wird für Sie vorgemerkt.
|
||||
</ui-tooltip>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="notificationsSent$ | async; let notificationsSent">
|
||||
<ng-container *ngIf="notificationsSent?.NOTIFICATION_EMAIL">
|
||||
<img [uiOverlayTrigger]="emailTooltip" src="/assets/images/email_bookmark.svg" />
|
||||
<ui-tooltip yPosition="above" xPosition="after" [yOffset]="-11" [xOffset]="-8" #emailTooltip [closeable]="true">
|
||||
Per E-Mail benachrichtigt <br />
|
||||
{{ notificationsSent?.NOTIFICATION_EMAIL | date: 'dd.MM.yyyy | HH:mm' }} Uhr
|
||||
<ng-container *ngFor="let notification of notificationsSent?.NOTIFICATION_EMAIL">
|
||||
{{ notification | date: 'dd.MM.yyyy | HH:mm' }} Uhr<br />
|
||||
</ng-container>
|
||||
</ui-tooltip>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="notificationsSent?.NOTIFICATION_SMS">
|
||||
<img [uiOverlayTrigger]="smsTooltip" src="/assets/images/sms_bookmark.svg" />
|
||||
<ui-tooltip yPosition="above" xPosition="after" [yOffset]="-11" [xOffset]="-8" #smsTooltip [closeable]="true">
|
||||
Per SMS benachrichtigt <br />
|
||||
{{ notificationsSent?.NOTIFICATION_SMS | date: 'dd.MM.yyyy | HH:mm' }} Uhr
|
||||
<ng-container *ngFor="let notification of notificationsSent?.NOTIFICATION_SMS">
|
||||
{{ notification | date: 'dd.MM.yyyy | HH:mm' }} Uhr<br />
|
||||
</ng-container>
|
||||
</ui-tooltip>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
@@ -76,6 +85,24 @@
|
||||
<div class="label">Meldenummer</div>
|
||||
<div class="value">{{ orderItem.ssc }} - {{ orderItem.sscText }}</div>
|
||||
</div>
|
||||
<div class="detail">
|
||||
<div class="label">
|
||||
<ng-container
|
||||
*ngIf="
|
||||
orderItemFeature(orderItem) === 'Versand' ||
|
||||
orderItemFeature(orderItem) === 'B2B-Versand' ||
|
||||
orderItemFeature(orderItem) === 'DIG-Versand'
|
||||
"
|
||||
>Lieferung ab</ng-container
|
||||
>
|
||||
<ng-container *ngIf="orderItemFeature(orderItem) === 'Abholung' || orderItemFeature(orderItem) === 'Rücklage'">
|
||||
Abholung ab
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="value">
|
||||
{{ orderItem?.estimatedShippingDate | date: 'dd.MM.yy' }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail">
|
||||
<div class="label">Vormerker</div>
|
||||
<div class="value">{{ orderItem.isPrebooked ? 'Ja' : 'Nein' }}</div>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { FormControl } from '@angular/forms';
|
||||
import { DomainOmsService, DomainReceiptService } from '@domain/oms';
|
||||
import { HistoryComponent } from '@modal/history';
|
||||
import { ComponentStore, tapResponse } from '@ngrx/component-store';
|
||||
import { OrderItemListItemDTO, ReceiptDTO, ReceiptType } from '@swagger/oms';
|
||||
import { OrderDTO, OrderItemListItemDTO, ReceiptDTO, ReceiptType } from '@swagger/oms';
|
||||
import { UiModalService } from '@ui/modal';
|
||||
import { isEqual } from 'lodash';
|
||||
import { combineLatest, NEVER } from 'rxjs';
|
||||
@@ -12,6 +12,7 @@ import { SharedGoodsInOutOrderDetailsStore } from '../goods-in-out-order-details
|
||||
|
||||
export interface SharedGoodsInOutOrderDetailsItemComponentState {
|
||||
orderItem?: OrderItemListItemDTO;
|
||||
order?: OrderDTO;
|
||||
quantity?: number;
|
||||
receipts?: ReceiptDTO[];
|
||||
selected?: boolean;
|
||||
@@ -47,6 +48,14 @@ export class SharedGoodsInOutOrderDetailsItemComponent extends ComponentStore<Sh
|
||||
}
|
||||
}
|
||||
|
||||
@Input()
|
||||
get order() {
|
||||
return this.get((s) => s.order);
|
||||
}
|
||||
set order(order: OrderDTO) {
|
||||
this.patchState({ order });
|
||||
}
|
||||
|
||||
@Output()
|
||||
orderItemChange = new EventEmitter<OrderItemListItemDTO>();
|
||||
|
||||
@@ -56,7 +65,7 @@ export class SharedGoodsInOutOrderDetailsItemComponent extends ComponentStore<Sh
|
||||
filter((oi) => !!oi),
|
||||
switchMap((oi) =>
|
||||
this._omsService
|
||||
.getCompletedTasks({ orderId: oi.orderId, orderItemId: oi.orderItemId, orderItemSubsetId: oi.orderItemSubsetId })
|
||||
.getCompletedTasks({ orderId: oi.orderId, orderItemId: oi.orderItemId, orderItemSubsetId: oi.orderItemSubsetId, take: 4, skip: 0 })
|
||||
.pipe(catchError(() => NEVER))
|
||||
)
|
||||
);
|
||||
@@ -203,4 +212,9 @@ export class SharedGoodsInOutOrderDetailsItemComponent extends ComponentStore<Sh
|
||||
this.selectedChange.emit(selected);
|
||||
this._cdr.markForCheck();
|
||||
}
|
||||
|
||||
orderItemFeature(orderItemListItem: OrderItemListItemDTO) {
|
||||
const orderItems = this.order?.items;
|
||||
return orderItems.find((orderItem) => orderItem.data.id === orderItemListItem.orderItemId)?.data?.features?.orderType;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<ui-spinner [show]="(changeActionLoader$ | async) === action.command">{{ action.label }}</ui-spinner>
|
||||
</button>
|
||||
<button
|
||||
[disabled]="changeActionDisabled$ | async"
|
||||
[disabled]="(changeActionDisabled$ | async) || (addToPreviousCompartmentActionDisabled$ | async)"
|
||||
*ngIf="addToPreviousCompartmentAction$ | async; let action"
|
||||
class="cta-action"
|
||||
[class.cta-action-primary]="action.selected"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user