mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-31 09:37:15 +01:00
Compare commits
42 Commits
angular-up
...
feature/39
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
967df6316b | ||
|
|
b440ddbe82 | ||
|
|
878bf44d0b | ||
|
|
d6e0d92132 | ||
|
|
da6489eb7a | ||
|
|
819827cc4c | ||
|
|
d09b5b1ce7 | ||
|
|
cc03ef4f9c | ||
|
|
b4dbd8889d | ||
|
|
483faad86a | ||
|
|
0dbc745ed0 | ||
|
|
180e93a7da | ||
|
|
5c6f416391 | ||
|
|
d97b6afac8 | ||
|
|
771816f3af | ||
|
|
0626538aea | ||
|
|
a1ad4e4a05 | ||
|
|
6df48eb555 | ||
|
|
27ab4526e2 | ||
|
|
9a24b34fbc | ||
|
|
d01e01534b | ||
|
|
5bca1f2a81 | ||
|
|
807b300885 | ||
|
|
b16ffa4352 | ||
|
|
da79d04ef4 | ||
|
|
cf619df576 | ||
|
|
8054c96315 | ||
|
|
2363f424f5 | ||
|
|
6ab1ea2e70 | ||
|
|
c9ce7d6762 | ||
|
|
6ee1b0a7f8 | ||
|
|
d881920312 | ||
|
|
516465db37 | ||
|
|
08e95cec55 | ||
|
|
9671683a93 | ||
|
|
d909d6e804 | ||
|
|
15c50779b4 | ||
|
|
1d865c47d7 | ||
|
|
5bdfec7c3f | ||
|
|
6b0beba1d9 | ||
|
|
9d886cd33f | ||
|
|
6bc265a358 |
@@ -8,7 +8,7 @@ import {
|
||||
StoreCheckoutSupplierService,
|
||||
SupplierDTO,
|
||||
} from '@swagger/checkout';
|
||||
import { combineLatest, Observable, of } from 'rxjs';
|
||||
import { BehaviorSubject, combineLatest, Observable, of } from 'rxjs';
|
||||
import {
|
||||
AvailabilityRequestDTO,
|
||||
AvailabilityService,
|
||||
@@ -21,11 +21,16 @@ import { isArray, memorize } from '@utils/common';
|
||||
import { LogisticianDTO, LogisticianService } from '@swagger/oms';
|
||||
import { ResponseArgsOfIEnumerableOfStockInfoDTO, StockDTO, StockInfoDTO, StockService } from '@swagger/remi';
|
||||
import { PriceDTO } from '@swagger/availability';
|
||||
import { AvailabilityByBranchDTO, ItemData } from './defs';
|
||||
import { AvailabilityByBranchDTO, ItemData, Ssc } from './defs';
|
||||
import { Availability } from './defs/availability';
|
||||
import { isEmpty } from 'lodash';
|
||||
|
||||
@Injectable()
|
||||
export class DomainAvailabilityService {
|
||||
// Ticket #3378 Keep Result List Items and Details Page SSC in sync
|
||||
sscs$ = new BehaviorSubject<Array<Ssc>>([]);
|
||||
sscsObs$ = this.sscs$.asObservable();
|
||||
|
||||
constructor(
|
||||
private _availabilityService: AvailabilityService,
|
||||
private _logisticanService: LogisticianService,
|
||||
@@ -479,6 +484,10 @@ export class DomainAvailabilityService {
|
||||
};
|
||||
}
|
||||
|
||||
private _priceIsEmpty(price: PriceDTO) {
|
||||
return isEmpty(price?.value) || isEmpty(price?.vat);
|
||||
}
|
||||
|
||||
private _mapToTakeAwayAvailability({
|
||||
response,
|
||||
supplier,
|
||||
@@ -499,7 +508,7 @@ export class DomainAvailabilityService {
|
||||
inStock: inStock,
|
||||
supplierSSC: quantity <= inStock ? '999' : '',
|
||||
supplierSSCText: quantity <= inStock ? 'Filialentnahme' : '',
|
||||
price: price ?? stockInfo?.retailPrice,
|
||||
price: this._priceIsEmpty(price) ? stockInfo?.retailPrice : price,
|
||||
supplier: { id: supplier?.id },
|
||||
// TODO: Change after API Update
|
||||
// LH: 2021-03-09 preis Property hat nun ein Fallback auf retailPrice
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './availability-by-branch-dto';
|
||||
export * from './availability';
|
||||
export * from './item-data';
|
||||
export * from './ssc';
|
||||
|
||||
5
apps/domain/availability/src/lib/defs/ssc.ts
Normal file
5
apps/domain/availability/src/lib/defs/ssc.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export interface Ssc {
|
||||
itemId?: number;
|
||||
ssc?: string;
|
||||
sscText?: string;
|
||||
}
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
StoreCheckoutPayerService,
|
||||
StoreCheckoutBranchService,
|
||||
ItemsResult,
|
||||
ShoppingCartItemDTO,
|
||||
} from '@swagger/checkout';
|
||||
import {
|
||||
DisplayOrderDTO,
|
||||
@@ -36,20 +37,45 @@ import {
|
||||
ResponseArgsOfValueTupleOfIEnumerableOfDisplayOrderDTOAndIEnumerableOfKeyValueDTOOfStringAndString,
|
||||
} from '@swagger/oms';
|
||||
import { isNullOrUndefined, memorize } from '@utils/common';
|
||||
import { combineLatest, Observable, of, concat, isObservable, throwError } from 'rxjs';
|
||||
import { bufferCount, catchError, filter, first, map, mergeMap, shareReplay, switchMap, tap, withLatestFrom } from 'rxjs/operators';
|
||||
import { combineLatest, Observable, of, concat, isObservable, throwError, interval, zip, EMPTY, Subscription } from 'rxjs';
|
||||
import {
|
||||
bufferCount,
|
||||
catchError,
|
||||
debounceTime,
|
||||
distinctUntilChanged,
|
||||
filter,
|
||||
first,
|
||||
map,
|
||||
mergeMap,
|
||||
share,
|
||||
shareReplay,
|
||||
switchMap,
|
||||
take,
|
||||
tap,
|
||||
withLatestFrom,
|
||||
} from 'rxjs/operators';
|
||||
|
||||
import * as DomainCheckoutSelectors from './store/domain-checkout.selectors';
|
||||
import * as DomainCheckoutActions from './store/domain-checkout.actions';
|
||||
import { DomainAvailabilityService } from '@domain/availability';
|
||||
import { DomainAvailabilityService, ItemData } from '@domain/availability';
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { CustomerDTO, EntityDTOContainerOfAttributeDTO } from '@swagger/crm';
|
||||
import { CustomerDTO } from '@swagger/crm';
|
||||
import { Config } from '@core/config';
|
||||
import parseDuration from 'parse-duration';
|
||||
import { CheckoutEntity } from './store/defs/checkout.entity';
|
||||
import { isEqual } from 'lodash';
|
||||
|
||||
@Injectable()
|
||||
export class DomainCheckoutService {
|
||||
get olaExpiration() {
|
||||
const exp = this._config.get('@domain/checkout.olaExpiration') ?? '5m';
|
||||
return parseDuration(exp);
|
||||
}
|
||||
|
||||
constructor(
|
||||
private store: Store<any>,
|
||||
private _config: Config,
|
||||
private applicationService: ApplicationService,
|
||||
private storeCheckoutService: StoreCheckoutService,
|
||||
private orderCheckoutService: OrderCheckoutService,
|
||||
@@ -119,14 +145,14 @@ export class DomainCheckoutService {
|
||||
})
|
||||
.pipe(
|
||||
map((response) => response.result),
|
||||
tap((shoppingCart) =>
|
||||
tap((shoppingCart) => {
|
||||
this.store.dispatch(
|
||||
DomainCheckoutActions.setShoppingCart({
|
||||
processId,
|
||||
shoppingCart,
|
||||
})
|
||||
)
|
||||
),
|
||||
);
|
||||
}),
|
||||
tap((shoppingCart) => this.updateProcessCount(processId, shoppingCart?.items?.length))
|
||||
)
|
||||
)
|
||||
@@ -249,11 +275,24 @@ export class DomainCheckoutService {
|
||||
shoppingCartItemId: number;
|
||||
availability: AvailabilityDTO;
|
||||
}) {
|
||||
return this._shoppingCartService.StoreCheckoutShoppingCartUpdateShoppingCartItemAvailability({
|
||||
shoppingCartId,
|
||||
shoppingCartItemId,
|
||||
availability,
|
||||
});
|
||||
return this._shoppingCartService
|
||||
.StoreCheckoutShoppingCartUpdateShoppingCartItemAvailability({
|
||||
shoppingCartId,
|
||||
shoppingCartItemId,
|
||||
availability,
|
||||
})
|
||||
.pipe(
|
||||
map((response) => response.result),
|
||||
tap((shoppingCart) => {
|
||||
this.store.dispatch(
|
||||
DomainCheckoutActions.addShoppingCartItemAvailabilityToHistoryByShoppingCartId({
|
||||
shoppingCartId,
|
||||
availability,
|
||||
shoppingCartItemId,
|
||||
})
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
updateItemInShoppingCart({
|
||||
@@ -265,7 +304,7 @@ export class DomainCheckoutService {
|
||||
shoppingCartItemId: number;
|
||||
update: UpdateShoppingCartItemDTO;
|
||||
}): Observable<ShoppingCartDTO> {
|
||||
return this.getShoppingCart({ processId }).pipe(
|
||||
return this.getShoppingCart({ processId, latest: true }).pipe(
|
||||
first(),
|
||||
mergeMap((shoppingCart) =>
|
||||
this._shoppingCartService
|
||||
@@ -276,8 +315,21 @@ export class DomainCheckoutService {
|
||||
})
|
||||
.pipe(
|
||||
map((response) => response.result),
|
||||
tap((shoppingCart) => this.store.dispatch(DomainCheckoutActions.setShoppingCart({ processId, shoppingCart }))),
|
||||
tap((shoppingCart) => this.updateProcessCount(processId, shoppingCart?.items?.length))
|
||||
tap((shoppingCart) => {
|
||||
this.store.dispatch(DomainCheckoutActions.setShoppingCart({ processId, shoppingCart }));
|
||||
|
||||
if (update.availability) {
|
||||
this.store.dispatch(
|
||||
DomainCheckoutActions.addShoppingCartItemAvailabilityToHistory({
|
||||
processId,
|
||||
availability: update.availability,
|
||||
shoppingCartItemId,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
this.updateProcessCount(processId, shoppingCart?.items?.length);
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
@@ -547,6 +599,172 @@ export class DomainCheckoutService {
|
||||
);
|
||||
}
|
||||
|
||||
async refreshAvailability({
|
||||
processId,
|
||||
shoppingCartItemId,
|
||||
}: {
|
||||
processId: number;
|
||||
shoppingCartItemId: number;
|
||||
}): Promise<AvailabilityDTO> {
|
||||
const shoppingCart = await this.getShoppingCart({ processId }).pipe(first()).toPromise();
|
||||
const item = shoppingCart?.items.find((item) => item.id === shoppingCartItemId)?.data;
|
||||
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
const itemData: ItemData = {
|
||||
ean: item.product.ean,
|
||||
itemId: Number(item.product.catalogProductNumber),
|
||||
price: item.availability.price,
|
||||
};
|
||||
|
||||
let availability: AvailabilityDTO;
|
||||
|
||||
switch (item.features.orderType) {
|
||||
case 'Abholung':
|
||||
const abholung = await this.availabilityService
|
||||
.getPickUpAvailability({
|
||||
item: itemData,
|
||||
branch: item.destination?.data?.targetBranch?.data,
|
||||
quantity: item.quantity,
|
||||
})
|
||||
.toPromise();
|
||||
availability = abholung[0];
|
||||
break;
|
||||
case 'Rücklage':
|
||||
const ruecklage = await this.availabilityService
|
||||
.getTakeAwayAvailability({
|
||||
item: itemData,
|
||||
quantity: item.quantity,
|
||||
branch: item.destination?.data?.targetBranch?.data,
|
||||
})
|
||||
.toPromise();
|
||||
availability = ruecklage;
|
||||
break;
|
||||
case 'Download':
|
||||
const download = await this.availabilityService
|
||||
.getDownloadAvailability({
|
||||
item: itemData,
|
||||
})
|
||||
.toPromise();
|
||||
|
||||
availability = download;
|
||||
break;
|
||||
|
||||
case 'Versand':
|
||||
const versand = await this.availabilityService
|
||||
.getDeliveryAvailability({
|
||||
item: itemData,
|
||||
quantity: item.quantity,
|
||||
})
|
||||
.toPromise();
|
||||
|
||||
availability = versand;
|
||||
break;
|
||||
|
||||
case 'DIG-Versand':
|
||||
const digVersand = await this.availabilityService
|
||||
.getDigDeliveryAvailability({
|
||||
item: itemData,
|
||||
quantity: item.quantity,
|
||||
})
|
||||
.toPromise();
|
||||
|
||||
availability = digVersand;
|
||||
break;
|
||||
|
||||
case 'B2B-Versand':
|
||||
const b2bVersand = await this.availabilityService
|
||||
.getB2bDeliveryAvailability({
|
||||
item: itemData,
|
||||
quantity: item.quantity,
|
||||
})
|
||||
.toPromise();
|
||||
|
||||
availability = b2bVersand;
|
||||
break;
|
||||
}
|
||||
|
||||
await this.updateItemInShoppingCart({
|
||||
processId,
|
||||
update: { availability },
|
||||
shoppingCartItemId: item.id,
|
||||
}).toPromise();
|
||||
|
||||
return availability;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the availability of all items is valid
|
||||
* @param param0 Process Id
|
||||
* @returns true if the availability of all items is valid
|
||||
*/
|
||||
validateOlaStatus({ processId, interval }: { processId: number; interval?: number }): Observable<boolean> {
|
||||
return new Observable((observer) => {
|
||||
const enity$ = this.store.select(DomainCheckoutSelectors.selectCheckoutEntityByProcessId, { processId });
|
||||
|
||||
const olaExpiration = this.olaExpiration;
|
||||
|
||||
let timeout: any;
|
||||
|
||||
let subscription: Subscription;
|
||||
|
||||
function check() {
|
||||
const now = Date.now();
|
||||
|
||||
subscription?.unsubscribe();
|
||||
subscription = enity$.pipe(take(1)).subscribe((entity) => {
|
||||
if (!entity || !entity.shoppingCart || !entity.shoppingCart.items) {
|
||||
return;
|
||||
}
|
||||
|
||||
const itemAvailabilityTimestamp = entity.itemAvailabilityTimestamp ?? {};
|
||||
const shoppingCart = entity.shoppingCart;
|
||||
|
||||
const timestamps = shoppingCart.items
|
||||
?.map((i) => i.data)
|
||||
?.filter((item) => !!item?.features?.orderType)
|
||||
?.map((item) => itemAvailabilityTimestamp[`${item.id}_${item.features.orderType}`]);
|
||||
|
||||
if (timestamps?.length > 0) {
|
||||
const oldestTimestamp = Math.min(...timestamps);
|
||||
observer.next(now - oldestTimestamp < olaExpiration);
|
||||
}
|
||||
|
||||
timeout = setTimeout(() => {
|
||||
check.call(this);
|
||||
}, interval ?? olaExpiration / 10);
|
||||
});
|
||||
}
|
||||
|
||||
check.call(this);
|
||||
|
||||
return () => {
|
||||
subscription?.unsubscribe();
|
||||
clearTimeout(timeout);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
validateAvailabilities({ processId }: { processId: number }): Observable<boolean> {
|
||||
return this.getShoppingCart({ processId }).pipe(
|
||||
map((shoppingCart) => {
|
||||
const items = shoppingCart?.items?.map((item) => item.data) || [];
|
||||
|
||||
return items.every((i) => this.availabilityService.isAvailable({ availability: i.availability }));
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
checkoutIsValid({ processId }: { processId: number }): Observable<boolean> {
|
||||
const olaStatus$ = this.validateOlaStatus({ processId, interval: 250 });
|
||||
|
||||
const availabilities$ = this.validateAvailabilities({ processId });
|
||||
|
||||
return combineLatest([olaStatus$, availabilities$]).pipe(map(([olaStatus, availabilities]) => olaStatus && availabilities));
|
||||
}
|
||||
|
||||
completeCheckout({ processId }: { processId: number }): Observable<DisplayOrderDTO[]> {
|
||||
const refreshShoppingCart$ = this.getShoppingCart({ processId, latest: true }).pipe(first());
|
||||
const refreshCheckout$ = this.getCheckout({ processId, refresh: true }).pipe(first());
|
||||
@@ -700,21 +918,23 @@ export class DomainCheckoutService {
|
||||
)
|
||||
);
|
||||
|
||||
return updateDestination$
|
||||
.pipe(tap(console.log.bind(window, 'updateDestination$')))
|
||||
return of(undefined)
|
||||
.pipe(
|
||||
mergeMap((_) => updateDestination$.pipe(tap(console.log.bind(window, 'updateDestination$')))),
|
||||
mergeMap((_) => refreshShoppingCart$.pipe(tap(console.log.bind(window, 'refreshShoppingCart$')))),
|
||||
mergeMap((_) => setSpecialComment$.pipe(tap(console.log.bind(window, 'setSpecialComment$')))),
|
||||
mergeMap((_) => refreshCheckout$.pipe(tap(console.log.bind(window, 'refreshCheckout$')))),
|
||||
mergeMap((_) => checkAvailabilities$.pipe(tap(console.log.bind(window, 'checkAvailabilities$')))),
|
||||
mergeMap((_) => updateAvailabilities$.pipe(tap(console.log.bind(window, 'updateAvailabilities$')))),
|
||||
mergeMap((_) => updateAvailabilities$.pipe(tap(console.log.bind(window, 'updateAvailabilities$'))))
|
||||
)
|
||||
.pipe(
|
||||
mergeMap((_) => setBuyer$.pipe(tap(console.log.bind(window, 'setBuyer$')))),
|
||||
mergeMap((_) => setNotificationChannels$.pipe(tap(console.log.bind(window, 'setNotificationChannels$')))),
|
||||
mergeMap((_) => setPayer$.pipe(tap(console.log.bind(window, 'setPayer$')))),
|
||||
mergeMap((_) => setPaymentType$.pipe(tap(console.log.bind(window, 'setPaymentType$')))),
|
||||
mergeMap((_) => setDestination$.pipe(tap(console.log.bind(window, 'setDestination$'))))
|
||||
)
|
||||
.pipe(mergeMap((_) => completeOrder$.pipe(tap(console.log.bind(window, 'completeOrder$')))));
|
||||
mergeMap((_) => setDestination$.pipe(tap(console.log.bind(window, 'setDestination$')))),
|
||||
mergeMap((_) => completeOrder$.pipe(tap(console.log.bind(window, 'completeOrder$'))))
|
||||
);
|
||||
}
|
||||
|
||||
completeKulturpassOrder({
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
import { BuyerDTO, CheckoutDTO, NotificationChannel, PayerDTO, ShippingAddressDTO, ShoppingCartDTO } from '@swagger/checkout';
|
||||
import {
|
||||
AvailabilityDTO,
|
||||
BuyerDTO,
|
||||
CheckoutDTO,
|
||||
NotificationChannel,
|
||||
PayerDTO,
|
||||
ShippingAddressDTO,
|
||||
ShoppingCartDTO,
|
||||
} from '@swagger/checkout';
|
||||
import { CustomerDTO } from '@swagger/crm';
|
||||
import { DisplayOrderDTO } from '@swagger/oms';
|
||||
|
||||
@@ -14,4 +22,5 @@ export interface CheckoutEntity {
|
||||
specialComment: string;
|
||||
notificationChannels: NotificationChannel;
|
||||
olaErrorIds: number[];
|
||||
itemAvailabilityTimestamp: Record<string, number>;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
ShippingAddressDTO,
|
||||
BuyerDTO,
|
||||
PayerDTO,
|
||||
AvailabilityDTO,
|
||||
} from '@swagger/checkout';
|
||||
import { CustomerDTO } from '@swagger/crm';
|
||||
import { DisplayOrderDTO, DisplayOrderItemDTO } from '@swagger/oms';
|
||||
@@ -61,3 +62,13 @@ export const setSpecialComment = createAction(`${prefix} Set Agent Comment`, pro
|
||||
export const setOlaError = createAction(`${prefix} Set Ola Error`, props<{ processId: number; olaErrorIds: number[] }>());
|
||||
|
||||
export const setCustomer = createAction(`${prefix} Set Customer`, props<{ processId: number; customer: CustomerDTO }>());
|
||||
|
||||
export const addShoppingCartItemAvailabilityToHistory = createAction(
|
||||
`${prefix} Add Shopping Cart Item Availability To History`,
|
||||
props<{ processId: number; shoppingCartItemId: number; availability: AvailabilityDTO }>()
|
||||
);
|
||||
|
||||
export const addShoppingCartItemAvailabilityToHistoryByShoppingCartId = createAction(
|
||||
`${prefix} Add Shopping Cart Item Availability To History By Shopping Cart Id`,
|
||||
props<{ shoppingCartId: number; shoppingCartItemId: number; availability: AvailabilityDTO }>()
|
||||
);
|
||||
|
||||
@@ -10,7 +10,22 @@ const _domainCheckoutReducer = createReducer(
|
||||
initialCheckoutState,
|
||||
on(DomainCheckoutActions.setShoppingCart, (s, { processId, shoppingCart }) => {
|
||||
const entity = getOrCreateCheckoutEntity({ processId, entities: s.entities });
|
||||
|
||||
const addedShoppingCartItems =
|
||||
shoppingCart?.items?.filter((item) => !entity.shoppingCart?.items?.find((i) => i.id === item.id))?.map((item) => item.data) ?? [];
|
||||
|
||||
entity.shoppingCart = shoppingCart;
|
||||
|
||||
entity.itemAvailabilityTimestamp = entity.itemAvailabilityTimestamp ? { ...entity.itemAvailabilityTimestamp } : {};
|
||||
|
||||
const now = Date.now();
|
||||
|
||||
for (let shoppingCartItem of addedShoppingCartItems) {
|
||||
if (shoppingCartItem.features?.orderType) {
|
||||
entity.itemAvailabilityTimestamp[`${shoppingCartItem.id}_${shoppingCartItem.features.orderType}`] = now;
|
||||
}
|
||||
}
|
||||
|
||||
return storeCheckoutAdapter.setOne(entity, s);
|
||||
}),
|
||||
on(DomainCheckoutActions.setCheckout, (s, { processId, checkout }) => {
|
||||
@@ -100,7 +115,40 @@ const _domainCheckoutReducer = createReducer(
|
||||
const entity = getOrCreateCheckoutEntity({ processId, entities: s.entities });
|
||||
entity.customer = customer;
|
||||
return storeCheckoutAdapter.setOne(entity, s);
|
||||
})
|
||||
}),
|
||||
on(DomainCheckoutActions.addShoppingCartItemAvailabilityToHistory, (s, { processId, shoppingCartItemId, availability }) => {
|
||||
const entity = getOrCreateCheckoutEntity({ processId, entities: s.entities });
|
||||
|
||||
const itemAvailabilityTimestamp = entity?.itemAvailabilityTimestamp ? { ...entity?.itemAvailabilityTimestamp } : {};
|
||||
|
||||
const item = entity?.shoppingCart?.items?.find((i) => i.id === shoppingCartItemId)?.data;
|
||||
|
||||
if (!item?.features?.orderType) return s;
|
||||
|
||||
itemAvailabilityTimestamp[`${item.id}_${item?.features?.orderType}`] = Date.now();
|
||||
|
||||
entity.itemAvailabilityTimestamp = itemAvailabilityTimestamp;
|
||||
|
||||
return storeCheckoutAdapter.setOne(entity, s);
|
||||
}),
|
||||
on(
|
||||
DomainCheckoutActions.addShoppingCartItemAvailabilityToHistoryByShoppingCartId,
|
||||
(s, { shoppingCartId, shoppingCartItemId, availability }) => {
|
||||
const entity = getCheckoutEntityByShoppingCartId({ shoppingCartId, entities: s.entities });
|
||||
|
||||
const itemAvailabilityTimestamp = entity?.itemAvailabilityTimestamp ? { ...entity?.itemAvailabilityTimestamp } : {};
|
||||
|
||||
const item = entity?.shoppingCart?.items?.find((i) => i.id === shoppingCartItemId)?.data;
|
||||
|
||||
if (!item?.features?.orderType) return s;
|
||||
|
||||
itemAvailabilityTimestamp[`${item.id}_${item?.features?.orderType}`] = Date.now();
|
||||
|
||||
entity.itemAvailabilityTimestamp = itemAvailabilityTimestamp;
|
||||
|
||||
return storeCheckoutAdapter.setOne(entity, s);
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
export function domainCheckoutReducer(state, action) {
|
||||
@@ -123,8 +171,20 @@ function getOrCreateCheckoutEntity({ entities, processId }: { entities: Dictiona
|
||||
notificationChannels: 0,
|
||||
olaErrorIds: [],
|
||||
customer: undefined,
|
||||
// availabilityHistory: [],
|
||||
itemAvailabilityTimestamp: {},
|
||||
};
|
||||
}
|
||||
|
||||
return { ...entity };
|
||||
}
|
||||
|
||||
function getCheckoutEntityByShoppingCartId({
|
||||
entities,
|
||||
shoppingCartId,
|
||||
}: {
|
||||
entities: Dictionary<CheckoutEntity>;
|
||||
shoppingCartId: number;
|
||||
}): CheckoutEntity {
|
||||
return Object.values(entities).find((entity) => entity.shoppingCart?.id === shoppingCartId);
|
||||
}
|
||||
|
||||
@@ -266,6 +266,11 @@
|
||||
"name": "text-decrease",
|
||||
"data": "m40-200 220-560h80l220 560h-75l-57-150H172l-57 150H40Zm156-214h208L302-685h-4L196-414Zm414-36v-60h310v60H610Z",
|
||||
"viewBox":"0 -960 960 960"
|
||||
},
|
||||
{
|
||||
"name": "calendar-today",
|
||||
"data": "M180-80q-24 0-42-18t-18-42v-620q0-24 18-42t42-18h65v-60h65v60h340v-60h65v60h65q24 0 42 18t18 42v620q0 24-18 42t-42 18H180Zm0-60h600v-430H180v430Zm0-490h600v-130H180v130Zm0 0v-130 130Z",
|
||||
"viewBox": "0 -960 960 960"
|
||||
}
|
||||
|
||||
],
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
"@core/logger": {
|
||||
"logLevel": "debug"
|
||||
},
|
||||
"@domain/checkout": {
|
||||
"olaExpiration": "5m"
|
||||
},
|
||||
"@swagger/isa": {
|
||||
"rootUrl": "https://isa-test.paragon-data.net/isa/v1"
|
||||
},
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
"@core/logger": {
|
||||
"logLevel": "debug"
|
||||
},
|
||||
"@domain/checkout": {
|
||||
"olaExpiration": "5m"
|
||||
},
|
||||
"@swagger/isa": {
|
||||
"rootUrl": "https://isa-integration.paragon-data.net/isa/v1"
|
||||
},
|
||||
|
||||
@@ -47,6 +47,9 @@
|
||||
"@swagger/wws": {
|
||||
"rootUrl": "https://isa-test.paragon-data.net/wws/v1"
|
||||
},
|
||||
"@domain/checkout": {
|
||||
"olaExpiration": "30s"
|
||||
},
|
||||
"hubs": {
|
||||
"notifications": {
|
||||
"url": "https://isa-test.paragon-data.net/isa/v1/rt",
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
"@core/logger": {
|
||||
"logLevel": "debug"
|
||||
},
|
||||
"@domain/checkout": {
|
||||
"olaExpiration": "5m"
|
||||
},
|
||||
"@swagger/isa": {
|
||||
"rootUrl": "https://isa.paragon-systems.de/isa/v1"
|
||||
},
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
"@core/logger": {
|
||||
"logLevel": "debug"
|
||||
},
|
||||
"@domain/checkout": {
|
||||
"olaExpiration": "5m"
|
||||
},
|
||||
"@swagger/isa": {
|
||||
"rootUrl": "https://isa-staging.paragon-systems.de/isa/v1"
|
||||
},
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
"@core/logger": {
|
||||
"logLevel": "debug"
|
||||
},
|
||||
"@domain/checkout": {
|
||||
"olaExpiration": "5m"
|
||||
},
|
||||
"@swagger/isa": {
|
||||
"rootUrl": "https://isa-test.paragon-data.net/isa/v1"
|
||||
},
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
:host {
|
||||
@apply text-[#0556B4];
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<a [routerLink]="route?.path" [queryParams]="route?.queryParams">
|
||||
<ng-content></ng-content>
|
||||
</a>
|
||||
@@ -0,0 +1,18 @@
|
||||
import { Component, ChangeDetectionStrategy, Input } from '@angular/core';
|
||||
import { RouterLink } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'page-article-details-text-link',
|
||||
templateUrl: 'article-details-text-link.component.html',
|
||||
styleUrls: ['article-details-text-link.component.css'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
host: { class: 'page-article-details-text-link' },
|
||||
standalone: true,
|
||||
imports: [RouterLink],
|
||||
})
|
||||
export class ArticleDetailsTextLinkComponent {
|
||||
@Input()
|
||||
route: { path: string[]; queryParams?: Record<string, string> };
|
||||
|
||||
constructor() {}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
:host {
|
||||
@apply block whitespace-pre-line;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<ng-container *ngFor="let line of lines">
|
||||
<ng-container [ngSwitch]="line | lineType">
|
||||
<page-article-details-text-link *ngSwitchCase="'reihe'" [route]="line | reiheRoute"> {{ line }} <br /> </page-article-details-text-link>
|
||||
<ng-container *ngSwitchDefault> {{ line }} <br /> </ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
@@ -0,0 +1,63 @@
|
||||
import { Component, ChangeDetectionStrategy, Input, Renderer2, ElementRef, OnChanges, SimpleChanges, HostBinding } from '@angular/core';
|
||||
import { TextDTO } from '@swagger/cat';
|
||||
import { ArticleDetailsTextLinkComponent } from './article-details-text-link.component';
|
||||
import { NgFor, NgSwitch, NgSwitchCase, NgSwitchDefault } from '@angular/common';
|
||||
import { LineTypePipe } from './line-type.pipe';
|
||||
import { ReiheRoutePipe } from './reihe-route.pipe';
|
||||
|
||||
const REIHE_REGEX = /^Reihe:\s*"(.+)\"$/m;
|
||||
|
||||
@Component({
|
||||
selector: 'page-article-details-text',
|
||||
templateUrl: 'article-details-text.component.html',
|
||||
styleUrls: ['article-details-text.component.css'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
host: { class: 'page-article-details-text' },
|
||||
standalone: true,
|
||||
imports: [ArticleDetailsTextLinkComponent, NgFor, NgSwitch, NgSwitchCase, NgSwitchDefault, LineTypePipe, ReiheRoutePipe],
|
||||
})
|
||||
export class ArticleDetailsTextComponent {
|
||||
@Input()
|
||||
text: TextDTO;
|
||||
|
||||
get lines() {
|
||||
return this.text?.value?.split('\n');
|
||||
}
|
||||
|
||||
constructor(private elementRef: ElementRef, private renderer: Renderer2) {}
|
||||
|
||||
// ngOnChanges(changes: SimpleChanges): void {
|
||||
// if (changes.text) {
|
||||
// this.renderText();
|
||||
// }
|
||||
// }
|
||||
|
||||
// renderText() {
|
||||
// console.log(this.getReihe());
|
||||
// }
|
||||
|
||||
// getReihe() {
|
||||
// REIHE_REGEX.exec(this.text?.value)?.forEach((match, groupIndex) => {
|
||||
// if (groupIndex === 0) return;
|
||||
// return match;
|
||||
// });
|
||||
// }
|
||||
|
||||
// @HostBinding('innerHTML')
|
||||
// get htmlContent() {
|
||||
// let content = this.text?.value;
|
||||
|
||||
// REIHE_REGEX.exec(content)?.forEach((match, groupIndex) => {
|
||||
// if (groupIndex === 0) return;
|
||||
|
||||
// const aElement: HTMLAnchorElement = this.renderer.createElement('a');
|
||||
// const text = this.renderer.createText(match);
|
||||
// this.renderer.appendChild(aElement, text);
|
||||
// this.renderer.setAttribute(aElement, 'href', `/search?query=${match}`);
|
||||
|
||||
// content = content.replace(match, aElement.outerHTML);
|
||||
// });
|
||||
|
||||
// return content;
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
|
||||
@Pipe({
|
||||
name: 'lineType',
|
||||
standalone: true,
|
||||
pure: true,
|
||||
})
|
||||
export class LineTypePipe implements PipeTransform {
|
||||
transform(value: string, ...args: any[]): 'text' | 'reihe' {
|
||||
const REIHE_REGEX = /^Reihe:\s*"(.+)\"$/g;
|
||||
const reihe = REIHE_REGEX.exec(value)?.[1];
|
||||
return reihe ? 'reihe' : 'text';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import { ChangeDetectorRef, OnDestroy, Pipe, PipeTransform } from '@angular/core';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { ProductCatalogNavigationService } from '@shared/services';
|
||||
import { isEqual } from 'lodash';
|
||||
import { Subscription, combineLatest, BehaviorSubject } from 'rxjs';
|
||||
import { distinctUntilChanged } from 'rxjs/operators';
|
||||
|
||||
@Pipe({
|
||||
name: 'reiheRoute',
|
||||
standalone: true,
|
||||
pure: false,
|
||||
})
|
||||
export class ReiheRoutePipe implements PipeTransform, OnDestroy {
|
||||
private subscription: Subscription;
|
||||
|
||||
value$ = new BehaviorSubject<string>('');
|
||||
|
||||
result: { path: string[]; queryParams?: Record<string, string> };
|
||||
|
||||
constructor(
|
||||
private navigation: ProductCatalogNavigationService,
|
||||
private application: ApplicationService,
|
||||
private cdr: ChangeDetectorRef
|
||||
) {
|
||||
this.subscription = combineLatest([this.application.activatedProcessId$, this.value$])
|
||||
.pipe(distinctUntilChanged(isEqual))
|
||||
.subscribe(([processId, value]) => {
|
||||
const REIHE_REGEX = /^Reihe:\s*"(.+)\"$/g;
|
||||
const reihe = REIHE_REGEX.exec(value)?.[1];
|
||||
|
||||
if (!reihe) {
|
||||
this.result = null;
|
||||
return;
|
||||
}
|
||||
|
||||
const main_qs = reihe.split('/')[0];
|
||||
|
||||
const path = this.navigation.getArticleSearchResultsPath(processId);
|
||||
|
||||
this.result = {
|
||||
path,
|
||||
queryParams: {
|
||||
main_qs,
|
||||
main_serial: 'serial',
|
||||
},
|
||||
};
|
||||
|
||||
this.cdr.detectChanges();
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.subscription?.unsubscribe();
|
||||
this.value$?.unsubscribe();
|
||||
}
|
||||
|
||||
transform(value: string, ...args: any[]) {
|
||||
this.value$.next(value);
|
||||
|
||||
return this.result;
|
||||
|
||||
// const REIHE_REGEX = /^Reihe:\s*"(.+)\"$/g;
|
||||
// const reihe = REIHE_REGEX.exec(value)?.[1];
|
||||
|
||||
// this.navigation.getArticleSearchResultsPath(this.)
|
||||
|
||||
// return reihe ? `/search?query=${reihe}` : null;
|
||||
}
|
||||
}
|
||||
@@ -104,27 +104,15 @@
|
||||
</div>
|
||||
|
||||
<div class="page-article-details__product-price-info flex flex-col mb-4">
|
||||
<div
|
||||
class="page-article-details__product-price font-bold text-xl self-end"
|
||||
*ngIf="item.catalogAvailability?.price?.value?.value; else retailPrice"
|
||||
>
|
||||
{{ item.catalogAvailability?.price?.value?.value | currency: item.catalogAvailability?.price?.value?.currency:'code' }}
|
||||
<div class="page-article-details__product-price font-bold text-xl self-end" *ngIf="price$ | async; let price">
|
||||
{{ price?.value?.value | currency: price?.value?.currency:'code' }}
|
||||
</div>
|
||||
<div *ngIf="price$ | async; let price" class="page-article-details__product-price-bound self-end">
|
||||
{{ price?.vat?.vatType | vat: (priceMaintained$ | async) }}
|
||||
</div>
|
||||
<ng-template #retailPrice>
|
||||
<div
|
||||
class="page-article-details__product-price font-bold text-xl self-end"
|
||||
*ngIf="store.takeAwayAvailability$ | async; let takeAwayAvailability"
|
||||
>
|
||||
{{ takeAwayAvailability?.retailPrice?.value?.value | currency: takeAwayAvailability?.retailPrice?.value?.currency:'code' }}
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<div class="page-article-details__product-points self-end" *ngIf="store.promotionPoints$ | async; let promotionPoints">
|
||||
{{ promotionPoints }} Lesepunkte
|
||||
</div>
|
||||
|
||||
<!-- TODO: Ticket PREISGEBUNDEN -->
|
||||
<div class="page-article-details__product-price-bound self-end"></div>
|
||||
</div>
|
||||
|
||||
<div class="page-article-details__product-origin-infos flex flex-col mb-4">
|
||||
@@ -137,21 +125,23 @@
|
||||
|
||||
<div class="page-article-details__product-stock flex justify-end items-center">
|
||||
<div class="h-5 w-16 bg-[#e6eff9] animate-[load_0.75s_linear_infinite]" *ngIf="store.fetchingTakeAwayAvailability$ | async"></div>
|
||||
<div
|
||||
<button
|
||||
class="flex flex-row py-4 pl-4"
|
||||
type="button"
|
||||
[uiOverlayTrigger]="tooltip"
|
||||
[overlayTriggerDisabled]="!(stockTooltipText$ | async)"
|
||||
(click)="showTooltip()"
|
||||
*ngIf="!(store.fetchingTakeAwayAvailability$ | async)"
|
||||
>
|
||||
<ng-container *ngIf="store.takeAwayAvailability$ | async; let takeAwayAvailability">
|
||||
<ui-icon class="mr-2 mb-1" icon="home" size="15px"></ui-icon>
|
||||
<span class="font-bold text-p3">{{ takeAwayAvailability.inStock || 0 }}x</span>
|
||||
</ng-container>
|
||||
</div>
|
||||
</button>
|
||||
<ui-tooltip #tooltip yPosition="above" xPosition="after" [yOffset]="-12" [closeable]="true">
|
||||
{{ stockTooltipText$ | async }}
|
||||
</ui-tooltip>
|
||||
</div>
|
||||
<ui-tooltip #tooltip yPosition="above" xPosition="after" [yOffset]="-8" [closeable]="true">
|
||||
{{ stockTooltipText$ | async }}
|
||||
</ui-tooltip>
|
||||
|
||||
<div class="page-article-details__product-ean-specs flex flex-col">
|
||||
<div class="page-article-details__product-ean" data-name="product-ean">{{ item.product?.ean }}</div>
|
||||
@@ -234,7 +224,7 @@
|
||||
<div class="page-article-details__ssc flex justify-end my-2 font-bold text-lg">
|
||||
<div class="w-52 h-px-20 bg-[#e6eff9] animate-[load_0.75s_linear_infinite]" *ngIf="fetchingAvailabilities$ | async"></div>
|
||||
<ng-container *ngIf="!(fetchingAvailabilities$ | async)">
|
||||
<div *ngIf="store.sscText$ | async; let sscText">
|
||||
<div class="text-right" *ngIf="store.sscText$ | async; let sscText">
|
||||
{{ sscText }}
|
||||
</div>
|
||||
</ng-container>
|
||||
@@ -325,9 +315,7 @@
|
||||
<hr class="bg-[#E6EFF9] border-t-2 my-3" />
|
||||
|
||||
<div #description class="page-article-details__product-description flex flex-col flex-grow" *ngIf="item.texts?.length > 0">
|
||||
<div class="whitespace-pre-line">
|
||||
{{ item.texts[0].value }}
|
||||
</div>
|
||||
<page-article-details-text [text]="item.texts[0]"> </page-article-details-text>
|
||||
|
||||
<button
|
||||
class="font-bold flex flex-row text-[#0556B4] items-center mt-2"
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
'image contributors contributors contributors'
|
||||
'image title title print'
|
||||
'image title title .'
|
||||
'image misc misc price'
|
||||
'image misc misc price'
|
||||
'image misc price price'
|
||||
'image misc price price'
|
||||
'image origin origin stock'
|
||||
'image origin origin stock'
|
||||
'image specs availabilities availabilities'
|
||||
|
||||
@@ -22,6 +22,7 @@ import { PurchaseOptionsModalService } from '@shared/modals/purchase-options-mod
|
||||
import { EnvironmentService } from '@core/environment';
|
||||
import { CheckoutNavigationService, ProductCatalogNavigationService } from '@shared/services';
|
||||
import { DomainCheckoutService } from '@domain/checkout';
|
||||
import { Store } from '@ngrx/store';
|
||||
|
||||
@Component({
|
||||
selector: 'page-article-details',
|
||||
@@ -100,23 +101,6 @@ export class ArticleDetailsComponent implements OnInit, OnDestroy {
|
||||
switchMap((processId) => this.applicationService.getSelectedBranch$(processId))
|
||||
);
|
||||
|
||||
stockTooltipText$ = combineLatest([this.store.branch$, this.selectedBranchId$]).pipe(
|
||||
map(([defaultBranch, selectedBranch]) => {
|
||||
if (defaultBranch?.branchType === 4) {
|
||||
if (!selectedBranch) {
|
||||
return 'Wählen Sie eine Filiale aus, um den Bestand zu sehen.';
|
||||
}
|
||||
return 'Sie sehen den Bestand einer anderen Filiale.';
|
||||
} else {
|
||||
if (selectedBranch && defaultBranch.id !== selectedBranch?.id) {
|
||||
return 'Sie sehen den Bestand einer anderen Filiale.';
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}),
|
||||
shareReplay(1)
|
||||
);
|
||||
|
||||
get isTablet$() {
|
||||
return this._environment.matchTablet$;
|
||||
}
|
||||
@@ -134,6 +118,59 @@ export class ArticleDetailsComponent implements OnInit, OnDestroy {
|
||||
return this.detailsContainer?.nativeElement;
|
||||
}
|
||||
|
||||
stockTooltipText$ = combineLatest([this.store.defaultBranch$, this.selectedBranchId$]).pipe(
|
||||
map(([defaultBranch, selectedBranch]) => {
|
||||
if (defaultBranch?.branchType !== 4 && selectedBranch && defaultBranch.id !== selectedBranch?.id) {
|
||||
return 'Sie sehen den Bestand einer anderen Filiale.';
|
||||
}
|
||||
return '';
|
||||
})
|
||||
);
|
||||
|
||||
priceMaintained$ = combineLatest([
|
||||
this.store.item$,
|
||||
this.store.takeAwayAvailability$,
|
||||
this.store.deliveryAvailability$,
|
||||
this.store.deliveryDigAvailability$,
|
||||
this.store.deliveryB2BAvailability$,
|
||||
]).pipe(
|
||||
map(([item, takeAway, delivery, deliveryDig, deliveryB2B]) =>
|
||||
[item, takeAway, delivery, deliveryDig, deliveryB2B].some((i) => i?.priceMaintained)
|
||||
)
|
||||
);
|
||||
|
||||
price$ = combineLatest([
|
||||
this.store.item$,
|
||||
this.store.takeAwayAvailability$,
|
||||
this.store.deliveryAvailability$,
|
||||
this.store.deliveryDigAvailability$,
|
||||
this.store.deliveryB2BAvailability$,
|
||||
]).pipe(
|
||||
map(([item, takeAway, delivery, deliveryDig, deliveryB2B]) => {
|
||||
if (item?.catalogAvailability?.price?.value?.value) {
|
||||
return item?.catalogAvailability?.price;
|
||||
}
|
||||
|
||||
if (takeAway?.price?.value?.value) {
|
||||
return takeAway.price;
|
||||
}
|
||||
|
||||
if (delivery?.price?.value?.value) {
|
||||
return delivery.price;
|
||||
}
|
||||
|
||||
if (deliveryDig?.price?.value?.value) {
|
||||
return deliveryDig.price;
|
||||
}
|
||||
|
||||
if (deliveryB2B?.price?.value?.value) {
|
||||
return deliveryB2B.price;
|
||||
}
|
||||
|
||||
return null;
|
||||
})
|
||||
);
|
||||
|
||||
constructor(
|
||||
public readonly applicationService: ApplicationService,
|
||||
private activatedRoute: ActivatedRoute,
|
||||
@@ -149,7 +186,8 @@ export class ArticleDetailsComponent implements OnInit, OnDestroy {
|
||||
private _checkoutNavigationService: CheckoutNavigationService,
|
||||
private _environment: EnvironmentService,
|
||||
private _router: Router,
|
||||
private _domainCheckoutService: DomainCheckoutService
|
||||
private _domainCheckoutService: DomainCheckoutService,
|
||||
private _store: Store
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
@@ -231,6 +269,14 @@ export class ArticleDetailsComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
async showTooltip() {
|
||||
const text = await this.stockTooltipText$.pipe(first()).toPromise();
|
||||
if (!text) {
|
||||
// Show Tooltip attached to branch selector dropdown
|
||||
this._store.dispatch({ type: 'OPEN_TOOLTIP_NO_BRANCH_SELECTED' });
|
||||
}
|
||||
}
|
||||
|
||||
async updateBreadcrumbDesktop(item: ItemDTO) {
|
||||
const crumbs = await this.breadcrumb
|
||||
.getBreadcrumbsByKeyAndTags$(this.applicationService.activatedProcessId, ['catalog', 'details'])
|
||||
|
||||
@@ -12,6 +12,7 @@ import { UiTooltipModule } from '@ui/tooltip';
|
||||
import { UiCommonModule } from '@ui/common';
|
||||
import { OrderDeadlinePipeModule } from '@shared/pipes/order-deadline';
|
||||
import { IconModule } from '@shared/components/icon';
|
||||
import { ArticleDetailsTextComponent } from './article-details-text/article-details-text.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@@ -26,6 +27,7 @@ import { IconModule } from '@shared/components/icon';
|
||||
IconModule,
|
||||
PipesModule,
|
||||
OrderDeadlinePipeModule,
|
||||
ArticleDetailsTextComponent,
|
||||
],
|
||||
exports: [ArticleDetailsComponent, ArticleRecommendationsComponent],
|
||||
declarations: [ArticleDetailsComponent, ArticleRecommendationsComponent],
|
||||
|
||||
@@ -61,8 +61,12 @@ export class ArticleDetailsStore extends ComponentStore<ArticleDetailsState> {
|
||||
return this.get((s) => s.branch);
|
||||
}
|
||||
|
||||
get defaultBranch$() {
|
||||
return this.domainAvailabilityService.getDefaultBranch();
|
||||
}
|
||||
|
||||
readonly branch$ = this.select((s) => s.branch).pipe(
|
||||
withLatestFrom(this.domainAvailabilityService.getDefaultBranch()),
|
||||
withLatestFrom(this.defaultBranch$),
|
||||
map(([selectedBranch, defaultBranch]) => selectedBranch ?? defaultBranch)
|
||||
);
|
||||
|
||||
@@ -267,7 +271,8 @@ export class ArticleDetailsStore extends ComponentStore<ArticleDetailsState> {
|
||||
this.deliveryB2BAvailability$,
|
||||
this.downloadAvailability$,
|
||||
]).pipe(
|
||||
map(([item, isDownload, pickupAvailability, deliveryDigAvailability, deliveryB2BAvailability, downloadAvailability]) => {
|
||||
withLatestFrom(this.domainAvailabilityService.sscs$),
|
||||
map(([[item, isDownload, pickupAvailability, deliveryDigAvailability, deliveryB2BAvailability, downloadAvailability], sscs]) => {
|
||||
let availability: AvailabilityDTO;
|
||||
|
||||
if (isDownload) {
|
||||
@@ -282,15 +287,30 @@ export class ArticleDetailsStore extends ComponentStore<ArticleDetailsState> {
|
||||
}
|
||||
}
|
||||
|
||||
let ssc = '';
|
||||
let sscText = 'Keine Lieferanten vorhanden';
|
||||
|
||||
if (item?.catalogAvailability?.supplier === 'S' && !isDownload) {
|
||||
return [item?.catalogAvailability?.ssc, item?.catalogAvailability?.sscText].filter((f) => !!f).join(' - ');
|
||||
ssc = item?.catalogAvailability?.ssc;
|
||||
sscText = item?.catalogAvailability?.sscText;
|
||||
|
||||
return [ssc, sscText].filter((f) => !!f).join(' - ');
|
||||
}
|
||||
|
||||
if (availability?.ssc || availability?.sscText) {
|
||||
return [availability?.ssc, availability?.sscText].filter((f) => !!f).join(' - ');
|
||||
ssc = availability?.ssc;
|
||||
sscText = availability?.sscText;
|
||||
|
||||
const sscExists = !!sscs?.find((ssc) => !!item?.id && ssc?.itemId === item.id);
|
||||
const sscEqualsCatalogSsc = ssc === item.catalogAvailability.ssc && sscText === item.catalogAvailability.sscText;
|
||||
|
||||
// To keep result list in sync with details page
|
||||
if (!sscExists && !sscEqualsCatalogSsc) {
|
||||
this.domainAvailabilityService.sscs$.next([...sscs, { itemId: item?.id, ssc, sscText }]);
|
||||
}
|
||||
}
|
||||
|
||||
return 'Keine Lieferanten vorhanden';
|
||||
return [ssc, sscText].filter((f) => !!f).join(' - ');
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
}
|
||||
|
||||
.cta-wrapper {
|
||||
@apply text-center whitespace-nowrap absolute bottom-8 left-0 w-full;
|
||||
@apply text-center whitespace-nowrap absolute bottom-8 left-1/2 -translate-x-1/2;
|
||||
}
|
||||
|
||||
.cta-reset-filter,
|
||||
|
||||
@@ -81,33 +81,29 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="page-search-result-item__item-stock desktop-small:text-p3 font-bold z-dropdown justify-self-start"
|
||||
<button
|
||||
class="page-search-result-item__item-stock desktop-small:text-p3 font-bold z-dropdown justify-self-start flex flex-row items-center justify-center"
|
||||
[class.justify-self-end]="!mainOutletActive"
|
||||
[uiOverlayTrigger]="tooltip"
|
||||
[overlayTriggerDisabled]="!(stockTooltipText$ | async)"
|
||||
type="button"
|
||||
(click)="$event.stopPropagation(); $event.preventDefault(); showTooltip()"
|
||||
>
|
||||
<ui-icon class="mr-[0.125rem] -mt-[0.275rem]" icon="home" size="1rem"></ui-icon>
|
||||
<ng-container *ngIf="isOrderBranch$ | async">
|
||||
<div class="flex flex-row items-center justify-between">
|
||||
<ui-icon class="-mt-[0.1875rem]" icon="home" size="1em"></ui-icon>
|
||||
<span
|
||||
*ngIf="inStock$ | async; let stock"
|
||||
[class.skeleton]="stock.inStock === undefined"
|
||||
class="min-w-[1rem] text-right inline-block"
|
||||
>{{ stock?.inStock }}</span
|
||||
>
|
||||
<span>x</span>
|
||||
</div>
|
||||
<span
|
||||
*ngIf="inStock$ | async; let stock"
|
||||
[class.skeleton]="stock.inStock === undefined"
|
||||
class="min-w-[0.75rem] text-right inline-block"
|
||||
>{{ stock?.inStock }}</span
|
||||
>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!(isOrderBranch$ | async)">
|
||||
<div class="flex flex-row items-center justify-between z-dropdown">
|
||||
<ui-icon class="block" icon="home" size="1em"></ui-icon>
|
||||
<span class="min-w-[1rem] text-center inline-block">-</span>
|
||||
<span>x</span>
|
||||
</div>
|
||||
<span class="min-w-[1rem] text-center inline-block">-</span>
|
||||
</ng-container>
|
||||
</div>
|
||||
<ui-tooltip #tooltip yPosition="above" xPosition="after" [yOffset]="-8" [closeable]="true">
|
||||
<span>x</span>
|
||||
</button>
|
||||
<ui-tooltip #tooltip yPosition="above" xPosition="after" [yOffset]="-12" [closeable]="true">
|
||||
{{ stockTooltipText$ | async }}
|
||||
</ui-tooltip>
|
||||
|
||||
@@ -115,10 +111,10 @@
|
||||
class="page-search-result-item__item-ssc desktop-small:text-p3 w-full text-right overflow-hidden text-ellipsis whitespace-nowrap"
|
||||
[class.page-search-result-item__item-ssc-main]="mainOutletActive"
|
||||
>
|
||||
<div class="hidden" [class.page-search-result-item__item-ssc-tooltip]="mainOutletActive">
|
||||
{{ item?.catalogAvailability?.ssc }} - {{ item?.catalogAvailability?.sscText }}
|
||||
</div>
|
||||
<strong>{{ item?.catalogAvailability?.ssc }}</strong> - {{ item?.catalogAvailability?.sscText }}
|
||||
<ng-container *ngIf="ssc$ | async; let ssc">
|
||||
<div class="hidden" [class.page-search-result-item__item-ssc-tooltip]="mainOutletActive">{{ ssc?.ssc }} - {{ ssc?.sscText }}</div>
|
||||
<strong>{{ ssc?.ssc }}</strong> - {{ ssc?.sscText }}
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
@@ -8,9 +8,10 @@ import { ItemDTO } from '@swagger/cat';
|
||||
import { DateAdapter } from '@ui/common';
|
||||
import { isEqual } from 'lodash';
|
||||
import { combineLatest } from 'rxjs';
|
||||
import { debounceTime, switchMap, map, shareReplay, filter } from 'rxjs/operators';
|
||||
import { debounceTime, switchMap, map, filter, first } from 'rxjs/operators';
|
||||
import { ArticleSearchService } from '../article-search.store';
|
||||
import { ProductCatalogNavigationService } from '@shared/services';
|
||||
import { Store } from '@ngrx/store';
|
||||
|
||||
export interface SearchResultItemComponentState {
|
||||
item?: ItemDTO;
|
||||
@@ -104,15 +105,8 @@ export class SearchResultItemComponent extends ComponentStore<SearchResultItemCo
|
||||
|
||||
stockTooltipText$ = combineLatest([this.defaultBranch$, this.selectedBranchId$]).pipe(
|
||||
map(([defaultBranch, selectedBranch]) => {
|
||||
if (defaultBranch?.branchType === 4) {
|
||||
if (!selectedBranch) {
|
||||
return 'Wählen Sie eine Filiale aus, um den Bestand zu sehen.';
|
||||
}
|
||||
if (defaultBranch?.branchType !== 4 && selectedBranch && defaultBranch.id !== selectedBranch?.id) {
|
||||
return 'Sie sehen den Bestand einer anderen Filiale.';
|
||||
} else {
|
||||
if (selectedBranch && defaultBranch.id !== selectedBranch?.id) {
|
||||
return 'Sie sehen den Bestand einer anderen Filiale.';
|
||||
}
|
||||
}
|
||||
return '';
|
||||
})
|
||||
@@ -126,6 +120,17 @@ export class SearchResultItemComponent extends ComponentStore<SearchResultItemCo
|
||||
)
|
||||
);
|
||||
|
||||
ssc$ = this._availability.sscsObs$.pipe(
|
||||
debounceTime(100),
|
||||
map((sscs) => {
|
||||
const updatedSsc = sscs?.find((ssc) => this.item?.id === ssc?.itemId);
|
||||
return {
|
||||
ssc: updatedSsc?.ssc ?? this.item?.catalogAvailability?.ssc,
|
||||
sscText: updatedSsc?.sscText ?? this.item?.catalogAvailability?.sscText,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
constructor(
|
||||
private _dateAdapter: DateAdapter,
|
||||
private _datePipe: DatePipe,
|
||||
@@ -135,7 +140,8 @@ export class SearchResultItemComponent extends ComponentStore<SearchResultItemCo
|
||||
private _availability: DomainAvailabilityService,
|
||||
private _environment: EnvironmentService,
|
||||
private _navigationService: ProductCatalogNavigationService,
|
||||
private _elRef: ElementRef<HTMLElement>
|
||||
private _elRef: ElementRef<HTMLElement>,
|
||||
private _store: Store
|
||||
) {
|
||||
super({
|
||||
selected: false,
|
||||
@@ -157,6 +163,14 @@ export class SearchResultItemComponent extends ComponentStore<SearchResultItemCo
|
||||
// }
|
||||
}
|
||||
|
||||
async showTooltip() {
|
||||
const text = await this.stockTooltipText$.pipe(first()).toPromise();
|
||||
if (!text) {
|
||||
// Show Tooltip attached to branch selector dropdown
|
||||
this._store.dispatch({ type: 'OPEN_TOOLTIP_NO_BRANCH_SELECTED' });
|
||||
}
|
||||
}
|
||||
|
||||
@HostBinding('style') get class() {
|
||||
return this.mainOutletActive ? { height: '6.125rem' } : '';
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<shared-breadcrumb class="mb-5 desktop-small:mb-9" [key]="activatedProcessId$ | async" [tags]="['catalog']">
|
||||
<shared-branch-selector
|
||||
[uiOverlayTrigger]="tooltip"
|
||||
[overlayTriggerDisabled]="stockTooltipDisabled"
|
||||
[filterCurrentBranch]="!!auth.hasRole('Store')"
|
||||
[orderBy]="auth.hasRole('Store') ? 'distance' : 'name'"
|
||||
[branchType]="1"
|
||||
@@ -7,6 +9,9 @@
|
||||
(valueChange)="patchProcessData($event)"
|
||||
>
|
||||
</shared-branch-selector>
|
||||
<ui-tooltip #tooltip yPosition="below" xPosition="after" [xOffset]="-263" [yOffset]="4" [closeable]="true">
|
||||
{{ stockTooltipText$ | async }}
|
||||
</ui-tooltip>
|
||||
</shared-breadcrumb>
|
||||
|
||||
<ng-container *ngIf="routerEvents$ | async">
|
||||
|
||||
@@ -6,9 +6,12 @@ import { EnvironmentService } from '@core/environment';
|
||||
import { BranchSelectorComponent } from '@shared/components/branch-selector';
|
||||
import { BreadcrumbComponent } from '@shared/components/breadcrumb';
|
||||
import { BranchDTO } from '@swagger/checkout';
|
||||
import { UiOverlayTriggerDirective } from '@ui/common';
|
||||
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
|
||||
import { fromEvent, Observable, Subject } from 'rxjs';
|
||||
import { combineLatest, fromEvent, Observable, Subject } from 'rxjs';
|
||||
import { first, map, shareReplay, switchMap, takeUntil, withLatestFrom } from 'rxjs/operators';
|
||||
import { ActionsSubject } from '@ngrx/store';
|
||||
import { DomainAvailabilityService } from '@domain/availability';
|
||||
|
||||
@Component({
|
||||
selector: 'page-catalog',
|
||||
@@ -23,6 +26,8 @@ export class PageCatalogComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
activatedProcessId$: Observable<string>;
|
||||
selectedBranch$: Observable<BranchDTO>;
|
||||
|
||||
@ViewChild(UiOverlayTriggerDirective) branchInputNoBranchSelectedTrigger: UiOverlayTriggerDirective;
|
||||
|
||||
get branchSelectorWidth() {
|
||||
return `${this.breadcrumbRef?.nativeElement?.clientWidth}px`;
|
||||
}
|
||||
@@ -45,23 +50,52 @@ export class PageCatalogComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
|
||||
showRightOutlet$ = this.routerEvents$.pipe(map((_) => !!this._activatedRoute?.children?.find((child) => child?.outlet === 'right')));
|
||||
|
||||
defaultBranch$ = this._availability.getDefaultBranch();
|
||||
|
||||
stockTooltipText$: Observable<string>;
|
||||
|
||||
stockTooltipDisabled$: Observable<boolean>;
|
||||
|
||||
get stockTooltipDisabled() {
|
||||
return this.branchInputNoBranchSelectedTrigger?.opened ? false : true;
|
||||
}
|
||||
|
||||
constructor(
|
||||
public application: ApplicationService,
|
||||
private _availability: DomainAvailabilityService,
|
||||
private _uiModal: UiModalService,
|
||||
public auth: AuthService,
|
||||
private _environmentService: EnvironmentService,
|
||||
private _renderer: Renderer2,
|
||||
private _activatedRoute: ActivatedRoute,
|
||||
private _router: Router
|
||||
private _router: Router,
|
||||
private _actions: ActionsSubject
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.activatedProcessId$ = this.application.activatedProcessId$.pipe(map((processId) => String(processId)));
|
||||
|
||||
this.selectedBranch$ = this.activatedProcessId$.pipe(switchMap((processId) => this.application.getSelectedBranch$(Number(processId))));
|
||||
|
||||
this.stockTooltipText$ = combineLatest([this.defaultBranch$, this.selectedBranch$]).pipe(
|
||||
map(([defaultBranch, selectedBranch]) => {
|
||||
if (defaultBranch?.branchType === 4 && !selectedBranch) {
|
||||
return 'Bitte wählen Sie eine Filiale aus, um den Bestand zu sehen.';
|
||||
} else if (defaultBranch?.branchType !== 4 && !selectedBranch) {
|
||||
return 'Bitte wählen Sie eine Filiale aus, um den Bestand einer anderen Filiale zu sehen';
|
||||
}
|
||||
return '';
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
this._actions.pipe(takeUntil(this._onDestroy$), withLatestFrom(this.stockTooltipText$)).subscribe(([action, text]) => {
|
||||
if (action.type === 'OPEN_TOOLTIP_NO_BRANCH_SELECTED' && !!text) {
|
||||
this.branchInputNoBranchSelectedTrigger.open();
|
||||
}
|
||||
});
|
||||
|
||||
fromEvent(this.branchSelectorRef.nativeElement, 'focusin')
|
||||
.pipe(takeUntil(this._onDestroy$), withLatestFrom(this.isTablet$))
|
||||
.subscribe(([_, isTablet]) => {
|
||||
|
||||
@@ -6,9 +6,20 @@ import { ArticleDetailsModule } from './article-details/article-details.module';
|
||||
import { ArticleSearchModule } from './article-search/article-search.module';
|
||||
import { PageCatalogRoutingModule } from './page-catalog-routing.module';
|
||||
import { PageCatalogComponent } from './page-catalog.component';
|
||||
import { UiCommonModule } from '@ui/common';
|
||||
import { UiTooltipModule } from '@ui/tooltip';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, PageCatalogRoutingModule, ArticleSearchModule, ArticleDetailsModule, BreadcrumbModule, BranchSelectorComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
PageCatalogRoutingModule,
|
||||
ArticleSearchModule,
|
||||
ArticleDetailsModule,
|
||||
BreadcrumbModule,
|
||||
BranchSelectorComponent,
|
||||
UiCommonModule,
|
||||
UiTooltipModule,
|
||||
],
|
||||
exports: [],
|
||||
declarations: [PageCatalogComponent],
|
||||
})
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
import { TrimPipe } from './trim.pipe';
|
||||
import { VatPipe } from './vat.pipe';
|
||||
|
||||
@NgModule({
|
||||
imports: [],
|
||||
exports: [TrimPipe],
|
||||
declarations: [TrimPipe],
|
||||
exports: [TrimPipe, VatPipe],
|
||||
declarations: [TrimPipe, VatPipe],
|
||||
providers: [],
|
||||
})
|
||||
export class PipesModule {}
|
||||
|
||||
18
apps/page/catalog/src/lib/shared/pipes/vat.pipe.ts
Normal file
18
apps/page/catalog/src/lib/shared/pipes/vat.pipe.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import { VATType } from '@swagger/cat';
|
||||
|
||||
@Pipe({
|
||||
name: 'vat',
|
||||
})
|
||||
export class VatPipe implements PipeTransform {
|
||||
transform(vatType: VATType, priceMaintained?: boolean, ...args: any[]): any {
|
||||
const vatString = vatType === 1 ? '0%' : vatType === 2 ? '19%' : vatType === 8 ? '7%' : undefined;
|
||||
if (!vatString) {
|
||||
return;
|
||||
}
|
||||
if (priceMaintained) {
|
||||
return `inkl. ${vatString} MwSt; Preisgebunden`;
|
||||
}
|
||||
return `inkl. ${vatString} MwSt`;
|
||||
}
|
||||
}
|
||||
@@ -126,7 +126,8 @@
|
||||
[disabled]="
|
||||
showOrderButtonSpinner ||
|
||||
((primaryCtaLabel$ | async) === 'Bestellen' && !(checkNotificationChannelControl$ | async)) ||
|
||||
notificationsControl?.invalid
|
||||
notificationsControl?.invalid ||
|
||||
((primaryCtaLabel$ | async) === 'Bestellen' && ((checkingOla$ | async) || (checkoutIsInValid$ | async)))
|
||||
"
|
||||
>
|
||||
<ui-spinner [show]="showOrderButtonSpinner">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, ChangeDetectionStrategy, ChangeDetectorRef, OnInit, OnDestroy } from '@angular/core';
|
||||
import { Component, ChangeDetectionStrategy, ChangeDetectorRef, OnInit, OnDestroy, ViewChildren, QueryList } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { DomainAvailabilityService } from '@domain/availability';
|
||||
@@ -6,8 +6,8 @@ import { DomainCheckoutService } from '@domain/checkout';
|
||||
import { AvailabilityDTO, DestinationDTO, ShoppingCartItemDTO } from '@swagger/checkout';
|
||||
import { UiMessageModalComponent, UiModalService } from '@ui/modal';
|
||||
import { PrintModalData, PrintModalComponent } from '@modal/printer';
|
||||
import { first, map, shareReplay, switchMap, takeUntil } from 'rxjs/operators';
|
||||
import { Subject, NEVER, combineLatest, BehaviorSubject } from 'rxjs';
|
||||
import { catchError, debounceTime, delay, first, map, shareReplay, switchMap, take, takeUntil, tap } from 'rxjs/operators';
|
||||
import { Subject, NEVER, combineLatest, BehaviorSubject, interval, of, merge } from 'rxjs';
|
||||
import { DomainCatalogService } from '@domain/catalog';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { DomainPrinterService } from '@domain/printer';
|
||||
@@ -17,6 +17,8 @@ import { PurchaseOptionsModalService } from '@shared/modals/purchase-options-mod
|
||||
import { CheckoutNavigationService, ProductCatalogNavigationService } from '@shared/services';
|
||||
import { EnvironmentService } from '@core/environment';
|
||||
import { CheckoutReviewStore } from './checkout-review.store';
|
||||
import { ToasterService } from '@shared/shell';
|
||||
import { ShoppingCartItemComponent } from './shopping-cart-item/shopping-cart-item.component';
|
||||
|
||||
@Component({
|
||||
selector: 'page-checkout-review',
|
||||
@@ -25,8 +27,12 @@ import { CheckoutReviewStore } from './checkout-review.store';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CheckoutReviewComponent implements OnInit, OnDestroy {
|
||||
checkingOla$ = new BehaviorSubject<boolean>(false);
|
||||
|
||||
payer$ = this._store.payer$;
|
||||
|
||||
buyer$ = this._store.buyer$;
|
||||
|
||||
shoppingCart$ = this._store.shoppingCart$;
|
||||
|
||||
fetching$ = this._store.fetching$;
|
||||
@@ -120,12 +126,12 @@ export class CheckoutReviewComponent implements OnInit, OnDestroy {
|
||||
showQuantityControlSpinnerItemId: number;
|
||||
quantityError$ = new BehaviorSubject<{ [key: string]: string }>({});
|
||||
|
||||
primaryCtaLabel$ = combineLatest([this.payer$, this.shoppingCartItemsWithoutOrderType$]).pipe(
|
||||
map(([payer, shoppingCartItemsWithoutOrderType]) => {
|
||||
primaryCtaLabel$ = combineLatest([this.payer$, this.buyer$, this.shoppingCartItemsWithoutOrderType$]).pipe(
|
||||
map(([payer, buyer, shoppingCartItemsWithoutOrderType]) => {
|
||||
if (shoppingCartItemsWithoutOrderType?.length > 0) {
|
||||
return 'Kaufoptionen';
|
||||
}
|
||||
if (!payer) {
|
||||
if (!(payer || buyer)) {
|
||||
return 'Weiter';
|
||||
}
|
||||
return 'Bestellen';
|
||||
@@ -147,6 +153,11 @@ export class CheckoutReviewComponent implements OnInit, OnDestroy {
|
||||
loadingOnQuantityChangeById$ = new Subject<number>();
|
||||
showOrderButtonSpinner: boolean;
|
||||
|
||||
checkoutIsInValid$ = this.applicationService.activatedProcessId$.pipe(
|
||||
switchMap((processId) => this.domainCheckoutService.checkoutIsValid({ processId })),
|
||||
map((valid) => !valid)
|
||||
);
|
||||
|
||||
get productSearchBasePath() {
|
||||
return this._productNavigationService.getArticleSearchBasePath(this.applicationService.activatedProcessId);
|
||||
}
|
||||
@@ -157,6 +168,9 @@ export class CheckoutReviewComponent implements OnInit, OnDestroy {
|
||||
|
||||
private _onDestroy$ = new Subject<void>();
|
||||
|
||||
@ViewChildren(ShoppingCartItemComponent)
|
||||
private _shoppingCartItems: QueryList<ShoppingCartItemComponent>;
|
||||
|
||||
constructor(
|
||||
private domainCheckoutService: DomainCheckoutService,
|
||||
public applicationService: ApplicationService,
|
||||
@@ -171,7 +185,8 @@ export class CheckoutReviewComponent implements OnInit, OnDestroy {
|
||||
private _productNavigationService: ProductCatalogNavigationService,
|
||||
private _navigationService: CheckoutNavigationService,
|
||||
private _environmentService: EnvironmentService,
|
||||
private _store: CheckoutReviewStore
|
||||
private _store: CheckoutReviewStore,
|
||||
private _toaster: ToasterService
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
@@ -181,6 +196,12 @@ export class CheckoutReviewComponent implements OnInit, OnDestroy {
|
||||
|
||||
await this.removeBreadcrumbs();
|
||||
await this.updateBreadcrumb();
|
||||
|
||||
this.registerOlaCechk();
|
||||
|
||||
window['Checkout'] = {
|
||||
refreshAvailabilities: this.refreshAvailabilities.bind(this),
|
||||
};
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
@@ -189,6 +210,32 @@ export class CheckoutReviewComponent implements OnInit, OnDestroy {
|
||||
this._onDestroy$.complete();
|
||||
}
|
||||
|
||||
registerOlaCechk() {
|
||||
this.applicationService.activatedProcessId$
|
||||
.pipe(
|
||||
takeUntil(this._onDestroy$),
|
||||
switchMap((processId) =>
|
||||
this.domainCheckoutService.validateOlaStatus({
|
||||
processId,
|
||||
})
|
||||
)
|
||||
)
|
||||
.subscribe((result) => {
|
||||
if (!result) {
|
||||
this.refreshAvailabilities();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async refreshAvailabilities() {
|
||||
this.checkingOla$.next(true);
|
||||
for (let itemComp of this._shoppingCartItems.toArray()) {
|
||||
await itemComp.refreshAvailability();
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
}
|
||||
this.checkingOla$.next(false);
|
||||
}
|
||||
|
||||
async updateBreadcrumb() {
|
||||
await this.breadcrumb.addOrUpdateBreadcrumbIfNotExists({
|
||||
key: this.applicationService.activatedProcessId,
|
||||
@@ -474,6 +521,8 @@ export class CheckoutReviewComponent implements OnInit, OnDestroy {
|
||||
title: 'Hinweis',
|
||||
data: { message: message.trim() },
|
||||
});
|
||||
} else if (error) {
|
||||
this.uiModal.error('Fehler beim abschließen der Bestellung', error);
|
||||
}
|
||||
|
||||
if (error.status === 409) {
|
||||
|
||||
@@ -20,6 +20,7 @@ import { CheckoutReviewDetailsComponent } from './details/checkout-review-detail
|
||||
import { CheckoutReviewStore } from './checkout-review.store';
|
||||
import { IconModule } from '@shared/components/icon';
|
||||
import { TextFieldModule } from '@angular/cdk/text-field';
|
||||
import { LoaderComponent } from '@shared/components/loader';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@@ -39,6 +40,7 @@ import { TextFieldModule } from '@angular/cdk/text-field';
|
||||
UiCheckboxModule,
|
||||
SharedNotificationChannelControlModule,
|
||||
TextFieldModule,
|
||||
LoaderComponent,
|
||||
],
|
||||
exports: [CheckoutReviewComponent, CheckoutReviewDetailsComponent],
|
||||
declarations: [CheckoutReviewComponent, SpecialCommentComponent, ShoppingCartItemComponent, CheckoutReviewDetailsComponent],
|
||||
|
||||
@@ -67,6 +67,7 @@
|
||||
<page-special-comment
|
||||
class="mb-6 mt-4"
|
||||
[hasPayer]="!!(payer$ | async)"
|
||||
[hasBuyer]="!!(buyer$ | async)"
|
||||
[ngModel]="specialComment$ | async"
|
||||
(ngModelChange)="setAgentComment($event)"
|
||||
>
|
||||
|
||||
@@ -40,16 +40,26 @@
|
||||
{{ item?.product?.volume }} <span *ngIf="item?.product?.volume && item?.product?.publicationDate">|</span>
|
||||
{{ item?.product?.publicationDate | date }}
|
||||
</div>
|
||||
|
||||
<div class="item-date" *ngIf="orderType === 'Abholung'">Abholung ab {{ item?.availability?.estimatedShippingDate | date }}</div>
|
||||
<div class="item-date" *ngIf="orderType === 'Versand' || orderType === 'B2B-Versand' || orderType === 'DIG-Versand'">
|
||||
<ng-container *ngIf="item?.availability?.estimatedDelivery; else estimatedShippingDate">
|
||||
Zustellung zwischen {{ (item?.availability?.estimatedDelivery?.start | date: 'EEE, dd.MM.')?.replace('.', '') }}
|
||||
und
|
||||
{{ (item?.availability?.estimatedDelivery?.stop | date: 'EEE, dd.MM.')?.replace('.', '') }}
|
||||
</ng-container>
|
||||
<ng-template #estimatedShippingDate> Versand {{ item?.availability?.estimatedShippingDate | date }} </ng-template>
|
||||
<div *ngIf="notAvailable$ | async">
|
||||
<span class="text-brand item-date">Nicht verfügbar</span>
|
||||
</div>
|
||||
<ui-spinner class="ava-loader" [show]="refreshingAvailabilit$ | async">
|
||||
<div class="item-date" [class.ssc-changed]="sscChanged$ | async" *ngIf="orderType === 'Abholung'">
|
||||
Abholung ab {{ item?.availability?.estimatedShippingDate | date }}
|
||||
</div>
|
||||
<div
|
||||
class="item-date"
|
||||
[class.ssc-changed]="sscChanged$ | async"
|
||||
*ngIf="orderType === 'Versand' || orderType === 'B2B-Versand' || orderType === 'DIG-Versand'"
|
||||
>
|
||||
<ng-container *ngIf="item?.availability?.estimatedDelivery; else estimatedShippingDate">
|
||||
Zustellung zwischen {{ (item?.availability?.estimatedDelivery?.start | date: 'EEE, dd.MM.')?.replace('.', '') }}
|
||||
und
|
||||
{{ (item?.availability?.estimatedDelivery?.stop | date: 'EEE, dd.MM.')?.replace('.', '') }}
|
||||
</ng-container>
|
||||
<ng-template #estimatedShippingDate> Versand {{ item?.availability?.estimatedShippingDate | date }} </ng-template>
|
||||
</div>
|
||||
</ui-spinner>
|
||||
|
||||
<div class="item-availability-message" *ngIf="olaError$ | async">
|
||||
Artikel nicht verfügbar
|
||||
|
||||
@@ -101,8 +101,16 @@ button {
|
||||
}
|
||||
}
|
||||
|
||||
.ssc-changed {
|
||||
@apply text-dark-goldenrod;
|
||||
}
|
||||
|
||||
::ng-deep page-shopping-cart-item ui-quantity-dropdown {
|
||||
.current-quantity {
|
||||
font-weight: normal !important;
|
||||
}
|
||||
}
|
||||
|
||||
::ng-deep page-shopping-cart-item .ava-loader ui-icon {
|
||||
left: 0 !important;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, NgZone, OnInit, Output, inject } from '@angular/core';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { EnvironmentService } from '@core/environment';
|
||||
import { DomainAvailabilityService } from '@domain/availability';
|
||||
@@ -6,6 +6,7 @@ import { DomainCheckoutService } from '@domain/checkout';
|
||||
import { ComponentStore } from '@ngrx/component-store';
|
||||
import { ProductCatalogNavigationService } from '@shared/services';
|
||||
import { ItemType, ShoppingCartItemDTO } from '@swagger/checkout';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { combineLatest } from 'rxjs';
|
||||
import { filter, first, map, shareReplay, switchMap } from 'rxjs/operators';
|
||||
|
||||
@@ -14,6 +15,9 @@ export interface ShoppingCartItemComponentState {
|
||||
orderType: string;
|
||||
loadingOnItemChangeById?: number;
|
||||
loadingOnQuantityChangeById?: number;
|
||||
refreshingAvailability: boolean;
|
||||
sscChanged: boolean;
|
||||
sscTextChanged: boolean;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@@ -23,6 +27,8 @@ export interface ShoppingCartItemComponentState {
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ShoppingCartItemComponent extends ComponentStore<ShoppingCartItemComponentState> implements OnInit {
|
||||
private _zone = inject(NgZone);
|
||||
|
||||
@Output() changeItem = new EventEmitter<{ shoppingCartItem: ShoppingCartItemDTO }>();
|
||||
@Output() changeDummyItem = new EventEmitter<{ shoppingCartItem: ShoppingCartItemDTO }>();
|
||||
@Output() changeQuantity = new EventEmitter<{ shoppingCartItem: ShoppingCartItemDTO; quantity: number }>();
|
||||
@@ -127,14 +133,35 @@ export class ShoppingCartItemComponent extends ComponentStore<ShoppingCartItemCo
|
||||
return this._environment.matchTablet();
|
||||
}
|
||||
|
||||
refreshingAvailabilit$ = this.select((s) => s.refreshingAvailability);
|
||||
|
||||
sscChanged$ = this.select((s) => s.sscChanged || s.sscTextChanged);
|
||||
|
||||
notAvailable$ = this.item$.pipe(
|
||||
map((item) => {
|
||||
const availability = item?.availability;
|
||||
|
||||
if (availability.availabilityType === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (availability.inStock && item.quantity > availability.inStock) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !this.availabilityService.isAvailable({ availability });
|
||||
})
|
||||
);
|
||||
|
||||
constructor(
|
||||
private availabilityService: DomainAvailabilityService,
|
||||
private checkoutService: DomainCheckoutService,
|
||||
public application: ApplicationService,
|
||||
private _productNavigationService: ProductCatalogNavigationService,
|
||||
private _environment: EnvironmentService
|
||||
private _environment: EnvironmentService,
|
||||
private _cdr: ChangeDetectorRef
|
||||
) {
|
||||
super({ item: undefined, orderType: '' });
|
||||
super({ item: undefined, orderType: '', refreshingAvailability: false, sscChanged: false, sscTextChanged: false });
|
||||
}
|
||||
|
||||
ngOnInit() {}
|
||||
@@ -147,4 +174,48 @@ export class ShoppingCartItemComponent extends ComponentStore<ShoppingCartItemCo
|
||||
onChangeQuantity(quantity: number) {
|
||||
this.changeQuantity.emit({ shoppingCartItem: this.item, quantity });
|
||||
}
|
||||
|
||||
async refreshAvailability() {
|
||||
const currentAvailability = cloneDeep(this.item.availability);
|
||||
|
||||
try {
|
||||
this.patchRefreshingAvailability(true);
|
||||
this._cdr.markForCheck();
|
||||
const availability = await this.checkoutService.refreshAvailability({
|
||||
processId: this.application.activatedProcessId,
|
||||
shoppingCartItemId: this.item.id,
|
||||
});
|
||||
|
||||
if (currentAvailability.ssc !== availability.ssc) {
|
||||
this.sscChanged();
|
||||
}
|
||||
if (currentAvailability.sscText !== availability.sscText) {
|
||||
this.ssctextChanged();
|
||||
}
|
||||
} catch (error) {}
|
||||
|
||||
this.patchRefreshingAvailability(false);
|
||||
this._cdr.markForCheck();
|
||||
}
|
||||
|
||||
patchRefreshingAvailability(value: boolean) {
|
||||
this._zone.run(() => {
|
||||
this.patchState({ refreshingAvailability: value });
|
||||
this._cdr.markForCheck();
|
||||
});
|
||||
}
|
||||
|
||||
ssctextChanged() {
|
||||
this._zone.run(() => {
|
||||
this.patchState({ sscTextChanged: true });
|
||||
this._cdr.markForCheck();
|
||||
});
|
||||
}
|
||||
|
||||
sscChanged() {
|
||||
this._zone.run(() => {
|
||||
this.patchState({ sscChanged: true });
|
||||
this._cdr.markForCheck();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,5 +33,5 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="!hasPayer" class="text-p3">Zur Info: Sie haben dem Warenkorb noch keinen Kunden hinzugefügt.</div>
|
||||
<div *ngIf="!(hasPayer || hasBuyer)" class="text-p3">Zur Info: Sie haben dem Warenkorb noch keinen Kunden hinzugefügt.</div>
|
||||
</div>
|
||||
|
||||
@@ -30,6 +30,9 @@ export class SpecialCommentComponent implements ControlValueAccessor {
|
||||
@Input()
|
||||
hasPayer: boolean;
|
||||
|
||||
@Input()
|
||||
hasBuyer: boolean;
|
||||
|
||||
@Output()
|
||||
isDirtyChange = new EventEmitter<boolean>();
|
||||
|
||||
|
||||
@@ -165,7 +165,7 @@
|
||||
[disabled]="showLoader$ | async"
|
||||
>
|
||||
<shared-loader [loading]="showLoader$ | async" spinnerSize="32">
|
||||
Weiter zur Artielsuche
|
||||
Weiter zur Artikelsuche
|
||||
</shared-loader>
|
||||
</button>
|
||||
|
||||
|
||||
@@ -11,5 +11,6 @@
|
||||
[hint]="hint$ | async"
|
||||
(scan)="search($event)"
|
||||
[scanner]="true"
|
||||
(hintCleared)="clearHint()"
|
||||
></ui-searchbox>
|
||||
</div>
|
||||
|
||||
@@ -59,8 +59,12 @@ export class FinishShippingDocumentComponent implements OnInit, OnDestroy {
|
||||
this._onDestroy$.complete();
|
||||
}
|
||||
|
||||
search(query: string) {
|
||||
clearHint() {
|
||||
this.hint$.next('');
|
||||
}
|
||||
|
||||
search(query: string) {
|
||||
query = query?.trim();
|
||||
if (!query) {
|
||||
this.hint$.next('Ungültige Eingabe');
|
||||
return;
|
||||
|
||||
@@ -1,70 +1,80 @@
|
||||
<div class="options-wrapper">
|
||||
<div
|
||||
*ngIf="uiStartOption"
|
||||
class="option"
|
||||
<div class="grid grid-flow-col items-center justify-start gap-4" [formGroup]="formGroup">
|
||||
<shared-form-control
|
||||
[attr.data-label]="uiStartOption?.label"
|
||||
[attr.data-value]="uiStartOption?.value"
|
||||
[attr.data-key]="uiStartOption?.key"
|
||||
[attr.data-selected]="uiStartOption?.selected"
|
||||
>
|
||||
<div class="option-wrapper">
|
||||
<span> {{ uiStartOption?.label }}: </span>
|
||||
<button
|
||||
class="cta-picker"
|
||||
[class.open]="dpStartTrigger?.opened"
|
||||
[uiOverlayTrigger]="dpStart"
|
||||
#dpStartTrigger="uiOverlayTrigger"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
{{ uiStartOption?.value | date: 'dd.MM.yy' }}
|
||||
</span>
|
||||
<ui-icon icon="arrow_head" size="1em"></ui-icon>
|
||||
</button>
|
||||
</div>
|
||||
<ui-datepicker
|
||||
class="dp-left"
|
||||
#dpStart
|
||||
yPosition="below"
|
||||
xPosition="after"
|
||||
[ngModel]="uiStartOption?.value"
|
||||
saveLabel="Übernehmen"
|
||||
(save)="uiStartOption?.setValue($event)"
|
||||
>
|
||||
</ui-datepicker>
|
||||
</div>
|
||||
<div
|
||||
*ngIf="uiStopOption"
|
||||
class="option"
|
||||
<shared-input-control>
|
||||
<input
|
||||
placeholder="TT.MM.JJJJ"
|
||||
sharedInputControlInput
|
||||
sharedDateInput
|
||||
type="text"
|
||||
formControlName="start"
|
||||
(blur)="setStratValue($event.target['value'])"
|
||||
/>
|
||||
<shared-input-control-suffix>
|
||||
<button
|
||||
type="button"
|
||||
class="grid items-center justify-center h-10 w-14 my-2 border-l solid border-[#AEB7C1] text-[#596470]"
|
||||
[uiOverlayTrigger]="dpStart"
|
||||
#dpStartTrigger="uiOverlayTrigger"
|
||||
>
|
||||
<shared-icon icon="calendar-today"></shared-icon>
|
||||
</button>
|
||||
</shared-input-control-suffix>
|
||||
</shared-input-control>
|
||||
</shared-form-control>
|
||||
<div class="font-bold -mt-4">bis</div>
|
||||
<shared-form-control
|
||||
[attr.data-label]="uiStopOption?.label"
|
||||
[attr.data-value]="uiStopOption?.value"
|
||||
[attr.data-key]="uiStopOption?.key"
|
||||
[attr.data-selected]="uiStopOption?.selected"
|
||||
>
|
||||
<div class="option-wrapper">
|
||||
<span> {{ uiStopOption?.label }}: </span>
|
||||
<button
|
||||
class="cta-picker"
|
||||
[class.open]="dpStopTrigger?.opened"
|
||||
[uiOverlayTrigger]="dpStop"
|
||||
#dpStopTrigger="uiOverlayTrigger"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
{{ uiStopOptionValue | date: 'dd.MM.yy' }}
|
||||
</span>
|
||||
<ui-icon icon="arrow_head" size="1em"></ui-icon>
|
||||
</button>
|
||||
</div>
|
||||
<ui-datepicker
|
||||
class="dp-right"
|
||||
yPosition="below"
|
||||
xPosition="after"
|
||||
#dpStop
|
||||
[ngModel]="uiStopOptionValue"
|
||||
(save)="setStopValue($event)"
|
||||
saveLabel="Übernehmen"
|
||||
>
|
||||
</ui-datepicker>
|
||||
</div>
|
||||
<shared-input-control>
|
||||
<input
|
||||
placeholder="TT.MM.JJJJ"
|
||||
sharedInputControlInput
|
||||
sharedDateInput
|
||||
type="text"
|
||||
formControlName="stop"
|
||||
(blur)="setStopValue($event.target['value'])"
|
||||
/>
|
||||
<shared-input-control-suffix>
|
||||
<button
|
||||
type="button"
|
||||
class="grid items-center justify-center h-10 w-14 my-2 border-l solid border-[#AEB7C1] text-[#596470]"
|
||||
[uiOverlayTrigger]="dpStop"
|
||||
#dpStartTrigger="uiOverlayTrigger"
|
||||
>
|
||||
<shared-icon icon="calendar-today"></shared-icon>
|
||||
</button>
|
||||
</shared-input-control-suffix>
|
||||
</shared-input-control>
|
||||
</shared-form-control>
|
||||
|
||||
<ui-datepicker
|
||||
class="dp-left"
|
||||
#dpStart
|
||||
yPosition="below"
|
||||
xPosition="after"
|
||||
formControlName="start"
|
||||
[max]="maxDate"
|
||||
saveLabel="Übernehmen"
|
||||
(save)="setStratValue($event)"
|
||||
>
|
||||
</ui-datepicker>
|
||||
<ui-datepicker
|
||||
class="dp-right"
|
||||
yPosition="below"
|
||||
xPosition="after"
|
||||
#dpStop
|
||||
[min]="minDate"
|
||||
formControlName="stop"
|
||||
(save)="setStopValue($event)"
|
||||
saveLabel="Übernehmen"
|
||||
>
|
||||
</ui-datepicker>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { Component, ChangeDetectionStrategy, Input, ChangeDetectorRef } from '@angular/core';
|
||||
import { Component, ChangeDetectionStrategy, Input, ChangeDetectorRef, ViewChild } from '@angular/core';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { IOption, Option } from '../../../tree';
|
||||
import { FormControl, FormGroup } from '@angular/forms';
|
||||
import { DateValidator } from '@shared/forms';
|
||||
import { UiDatepickerComponent } from '@ui/datepicker';
|
||||
|
||||
@Component({
|
||||
selector: 'shared-input-option-date-range',
|
||||
@@ -9,11 +12,29 @@ import { IOption, Option } from '../../../tree';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class FilterInputOptionDateRangeComponent {
|
||||
@ViewChild('dpStart', { static: true }) startDatepicker: UiDatepickerComponent;
|
||||
|
||||
@ViewChild('dpStop', { static: true }) stopDatepicker: UiDatepickerComponent;
|
||||
|
||||
private _options: Option[];
|
||||
|
||||
formGroup = new FormGroup({
|
||||
start: new FormControl<Date>(undefined, DateValidator),
|
||||
stop: new FormControl<Date>(undefined, DateValidator),
|
||||
});
|
||||
|
||||
get startControl(): FormControl<Date> {
|
||||
return this.formGroup.get('start') as FormControl<Date>;
|
||||
}
|
||||
|
||||
get stopControl(): FormControl<Date> {
|
||||
return this.formGroup.get('stop') as FormControl<Date>;
|
||||
}
|
||||
|
||||
@Input()
|
||||
set options(value: IOption[]) {
|
||||
this._options = value?.map((option) => (option instanceof Option ? option : Option.create(option)));
|
||||
|
||||
this.subscribeChanges();
|
||||
}
|
||||
|
||||
@@ -29,28 +50,56 @@ export class FilterInputOptionDateRangeComponent {
|
||||
return this.uiOptions?.find((o) => o.key === 'stop');
|
||||
}
|
||||
|
||||
get uiStopOptionValue() {
|
||||
const stopDate = new Date(this.uiStopOption?.value);
|
||||
stopDate?.setDate(stopDate?.getDate() - 1); // to update the view correctly after setStopValue() gets called !
|
||||
return stopDate?.toJSON();
|
||||
optionChangeSubscription: Subscription;
|
||||
|
||||
get startDate() {
|
||||
return this.uiStartOption?.value ? new Date(this.uiStartOption?.value) : undefined;
|
||||
}
|
||||
|
||||
optionChangeSubscription: Subscription;
|
||||
get stopDate() {
|
||||
return this.uiStopOption?.value ? new Date(this.uiStopOption?.value) : undefined;
|
||||
}
|
||||
|
||||
get minDate() {
|
||||
return this.startDate ?? new Date(0);
|
||||
}
|
||||
|
||||
get maxDate() {
|
||||
return this.stopDate ?? new Date('9999-12-31');
|
||||
}
|
||||
|
||||
constructor(private cdr: ChangeDetectorRef) {}
|
||||
|
||||
subscribeChanges() {
|
||||
this.unsubscribeChanges();
|
||||
if (this.uiStartOption) {
|
||||
this.formGroup.patchValue({ start: (this.uiStartOption.value as any) as Date });
|
||||
|
||||
this.optionChangeSubscription.add(
|
||||
this.uiStartOption.changes.subscribe(() => {
|
||||
this.uiStartOption.changes.subscribe(({ target, keys }) => {
|
||||
if (keys.includes('value')) {
|
||||
if (new Date(target.value) !== this.formGroup.get('start').value) {
|
||||
this.formGroup.patchValue({ start: target.value as any });
|
||||
this.startDatepicker?.setDisplayed(new Date(target.value));
|
||||
}
|
||||
}
|
||||
|
||||
this.cdr.markForCheck();
|
||||
})
|
||||
);
|
||||
}
|
||||
if (this.uiStopOption) {
|
||||
this.formGroup.patchValue({ stop: (this.uiStopOption.value as any) as Date });
|
||||
|
||||
this.optionChangeSubscription.add(
|
||||
this.uiStopOption.changes.subscribe(() => {
|
||||
this.uiStopOption.changes.subscribe(({ target, keys }) => {
|
||||
if (keys.includes('value')) {
|
||||
if (new Date(target.value) !== this.formGroup.get('start').value) {
|
||||
this.formGroup.patchValue({ stop: target.value as any });
|
||||
this.stopDatepicker?.setDisplayed(new Date(target.value));
|
||||
}
|
||||
}
|
||||
|
||||
this.cdr.markForCheck();
|
||||
})
|
||||
);
|
||||
@@ -62,9 +111,55 @@ export class FilterInputOptionDateRangeComponent {
|
||||
this.optionChangeSubscription = new Subscription();
|
||||
}
|
||||
|
||||
setStratValue(date: Date) {
|
||||
if (!date) {
|
||||
this.uiStartOption?.setValue(undefined);
|
||||
this.formGroup.patchValue({ start: undefined });
|
||||
this.startDatepicker?.setDisplayed(undefined);
|
||||
this.startDatepicker?.setSelected(undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
const startDate = new Date(date);
|
||||
startDate.setHours(0, 0, 0, 0);
|
||||
|
||||
if (this.startDate === date) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.uiStartOption?.setValue(startDate);
|
||||
this.formGroup.patchValue({ start: startDate });
|
||||
this.startDatepicker?.setDisplayed(startDate ?? new Date());
|
||||
this.startDatepicker?.setSelected(startDate);
|
||||
|
||||
if (this.stopDate && startDate > this.stopDate) {
|
||||
this.setStopValue(startDate);
|
||||
}
|
||||
}
|
||||
|
||||
setStopValue(date: Date) {
|
||||
const stopDate = date;
|
||||
stopDate?.setDate(stopDate?.getDate() + 1); // to include the selected stop date !
|
||||
if (!date) {
|
||||
this.uiStopOption?.setValue(undefined);
|
||||
this.formGroup.patchValue({ stop: undefined });
|
||||
this.stopDatepicker?.setDisplayed(undefined);
|
||||
this.stopDatepicker?.setSelected(undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
const stopDate = new Date(date);
|
||||
stopDate.setHours(23, 59, 59, 999);
|
||||
|
||||
if (this.stopDate === date) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.uiStopOption?.setValue(stopDate);
|
||||
this.formGroup.patchValue({ stop: stopDate });
|
||||
this.stopDatepicker?.setDisplayed(stopDate ?? new Date());
|
||||
this.stopDatepicker?.setSelected(stopDate);
|
||||
|
||||
if (this.startDate && stopDate < this.startDate) {
|
||||
this.setStratValue(stopDate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,12 +3,28 @@ import { CommonModule } from '@angular/common';
|
||||
|
||||
import { FilterInputOptionDateRangeComponent } from './filter-input-option-date-range.component';
|
||||
import { UiDatepickerModule } from '@ui/datepicker';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { UiIconModule } from '@ui/icon';
|
||||
import { UiCommonModule } from '@ui/common';
|
||||
import { FormControlComponent } from '@shared/components/form-control';
|
||||
import { IconComponent } from '@shared/components/icon';
|
||||
import { InputControlModule } from '@shared/components/input-control';
|
||||
import { UiDateInputDirective } from '@ui/input';
|
||||
import { DateInputDirective } from '@shared/forms';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, UiCommonModule, UiDatepickerModule, FormsModule, UiIconModule],
|
||||
imports: [
|
||||
CommonModule,
|
||||
InputControlModule,
|
||||
FormControlComponent,
|
||||
UiCommonModule,
|
||||
UiDatepickerModule,
|
||||
ReactiveFormsModule,
|
||||
FormsModule,
|
||||
IconComponent,
|
||||
UiDateInputDirective,
|
||||
DateInputDirective,
|
||||
],
|
||||
exports: [FilterInputOptionDateRangeComponent],
|
||||
declarations: [FilterInputOptionDateRangeComponent],
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<label class="shared-fomr-control-label">{{ displayLabel }}</label>
|
||||
<ng-content select="shared-select, input"></ng-content>
|
||||
<ng-content select="shared-select, input, shared-input-control"></ng-content>
|
||||
<div class="shared-fomr-control-error">
|
||||
{{ control?.errors | firstError: label }}
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
.shared-input-control {
|
||||
@apply relative leading-[21px] text-p2 font-bold;
|
||||
@apply relative leading-[1.3125rem] text-p2;
|
||||
}
|
||||
|
||||
.shared-input-control:has(input.ng-invalid.ng-dirty) {
|
||||
@@ -7,7 +7,11 @@
|
||||
}
|
||||
|
||||
.shared-input-control-wrapper {
|
||||
@apply flex flex-row items-center grow border border-solid border-[#AEB7C1] rounded-[5px] p-4;
|
||||
@apply grid grid-flow-col items-stretch grow border border-solid border-[#AEB7C1] rounded h-14;
|
||||
}
|
||||
|
||||
.shared-input-control-wrapper input {
|
||||
@apply bg-transparent px-4;
|
||||
}
|
||||
|
||||
.shared-input-control-wrapper:has(input.ng-invalid.ng-dirty) {
|
||||
@@ -31,14 +35,6 @@
|
||||
@apply inline-block grow-0;
|
||||
}
|
||||
|
||||
.shared-input-control-prefix {
|
||||
@apply -ml-2 mr-2;
|
||||
}
|
||||
|
||||
.shared-input-control-suffix {
|
||||
@apply -mr-2 ml-2;
|
||||
}
|
||||
|
||||
.shared-input-control-error {
|
||||
@apply text-left mt-[2px];
|
||||
@apply text-left mt-[.125rem];
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { OnDestroy, TemplateRef } from '@angular/core';
|
||||
import { OnDestroy } from '@angular/core';
|
||||
import { QueryList } from '@angular/core';
|
||||
import { ContentChildren } from '@angular/core';
|
||||
import { Component, ChangeDetectionStrategy, ViewEncapsulation, AfterContentInit, ContentChild, ViewChild } from '@angular/core';
|
||||
@@ -98,7 +98,7 @@ export class InputControlComponent implements AfterContentInit, OnDestroy {
|
||||
console.error(new Error(`No input[sharedInput] found in \`<shared-input-control>\` component`));
|
||||
}
|
||||
|
||||
const statusChangesSub = this.inputDirective.control.statusChanges.subscribe(() => {
|
||||
const statusChangesSub = this.inputDirective.control.statusChanges.subscribe((s) => {
|
||||
this.renderError();
|
||||
this.renderIndicator();
|
||||
});
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import { AfterContentInit, ChangeDetectionStrategy, Component, ElementRef, Renderer2 } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'shared-scale-content, [sharedScaleContent]',
|
||||
template: '<ng-content></ng-content>',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
styles: [
|
||||
`
|
||||
:host {
|
||||
overflow-y: hidden;
|
||||
}
|
||||
`,
|
||||
],
|
||||
})
|
||||
export class ScaleContentComponent implements AfterContentInit {
|
||||
// TODO: Bessere Lösung finden? Falls keine bessere Lösung gefunden wird, dann muss die Komponente auslagen
|
||||
|
||||
fontSizeInEm = 1;
|
||||
|
||||
adjustmentSteps = 0.05;
|
||||
|
||||
constructor(private _elementRef: ElementRef<HTMLElement>, private _renderer: Renderer2) {}
|
||||
|
||||
ngAfterContentInit(): void {
|
||||
this.adjustFontSize();
|
||||
}
|
||||
|
||||
adjustFontSize() {
|
||||
const element = this._elementRef.nativeElement;
|
||||
|
||||
const clientRect = element?.getClientRects();
|
||||
const scrollHeight = element?.scrollHeight;
|
||||
|
||||
const domRect = clientRect && clientRect[0];
|
||||
|
||||
if (domRect && Math.ceil(domRect?.height) < scrollHeight) {
|
||||
this.fontSizeInEm -= this.adjustmentSteps;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
this._renderer.setStyle(element, 'font-size', `${this.fontSizeInEm}em`);
|
||||
|
||||
setTimeout(() => this.adjustFontSize(), 1);
|
||||
}
|
||||
}
|
||||
1
apps/shared/components/scale-content/src/public-api.ts
Normal file
1
apps/shared/components/scale-content/src/public-api.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './lib/scale-content.component';
|
||||
6
apps/shared/forms/ng-package.json
Normal file
6
apps/shared/forms/ng-package.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
|
||||
"lib": {
|
||||
"entryFile": "src/public-api.ts"
|
||||
}
|
||||
}
|
||||
86
apps/shared/forms/src/lib/directives/date-input.directive.ts
Normal file
86
apps/shared/forms/src/lib/directives/date-input.directive.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import { ChangeDetectorRef, Directive, HostBinding, HostListener, Input, forwardRef } from '@angular/core';
|
||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import moment from 'moment';
|
||||
import { DE_DATE_REGEX, ISO_DATE_REGEX } from '../regex';
|
||||
|
||||
@Directive({
|
||||
selector: 'input[type="text"][sharedDateInput]',
|
||||
standalone: true,
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => DateInputDirective),
|
||||
multi: true,
|
||||
},
|
||||
],
|
||||
})
|
||||
export class DateInputDirective implements ControlValueAccessor {
|
||||
private _onChange = (_: any) => {};
|
||||
|
||||
private _onTouched = () => {};
|
||||
|
||||
@Input()
|
||||
value: any;
|
||||
|
||||
@HostBinding('value')
|
||||
displayValue: string;
|
||||
|
||||
@HostBinding('disabled')
|
||||
disabled: boolean;
|
||||
|
||||
constructor(private _cdr: ChangeDetectorRef) {}
|
||||
|
||||
writeValue(obj: any): void {
|
||||
this.value = obj;
|
||||
this.setDisplayValue(obj);
|
||||
}
|
||||
|
||||
registerOnChange(fn: any): void {
|
||||
this._onChange = fn;
|
||||
}
|
||||
|
||||
registerOnTouched(fn: any): void {
|
||||
this._onTouched = fn;
|
||||
}
|
||||
|
||||
setDisabledState?(isDisabled: boolean): void {
|
||||
this.disabled = isDisabled;
|
||||
}
|
||||
|
||||
setDisplayValue(value: any) {
|
||||
let date: Date;
|
||||
|
||||
if (value instanceof Date) {
|
||||
date = value;
|
||||
} else if (DE_DATE_REGEX.test(value)) {
|
||||
const mom = moment(value, 'L');
|
||||
date = mom.toDate();
|
||||
} else if (ISO_DATE_REGEX.test(value)) {
|
||||
date = new Date(value);
|
||||
}
|
||||
|
||||
if (date) {
|
||||
this.displayValue = moment(date).format('L');
|
||||
} else {
|
||||
this.displayValue = value ?? '';
|
||||
}
|
||||
|
||||
this._cdr.markForCheck();
|
||||
}
|
||||
|
||||
@HostListener('input', ['$event.target.value'])
|
||||
onInput(value: string) {
|
||||
let date: Date;
|
||||
if (DE_DATE_REGEX.test(value)) {
|
||||
const mom = moment(value, 'L');
|
||||
date = mom.toDate();
|
||||
}
|
||||
|
||||
this.value = date ?? value;
|
||||
|
||||
this.displayValue = value;
|
||||
|
||||
this._onTouched();
|
||||
this._onChange(this.value);
|
||||
}
|
||||
}
|
||||
1
apps/shared/forms/src/lib/directives/index.ts
Normal file
1
apps/shared/forms/src/lib/directives/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './date-input.directive';
|
||||
3
apps/shared/forms/src/lib/regex.ts
Normal file
3
apps/shared/forms/src/lib/regex.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export const DE_DATE_REGEX = /^(0?[1-9]|[12][0-9]|3[01])\.(0?[1-9]|1[0-2])\.\d{4}$/g;
|
||||
|
||||
export const ISO_DATE_REGEX = /^[0-9]{4}-((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01])|(0[469]|11)-(0[1-9]|[12][0-9]|30)|(02)-(0[1-9]|[12][0-9]))T(0[0-9]|1[0-9]|2[0-3]):(0[0-9]|[1-5][0-9]):(0[0-9]|[1-5][0-9])\.[0-9]{3}Z$/g;
|
||||
25
apps/shared/forms/src/lib/validators/date.validator.ts
Normal file
25
apps/shared/forms/src/lib/validators/date.validator.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { AbstractControl } from '@angular/forms';
|
||||
import { DE_DATE_REGEX, ISO_DATE_REGEX } from '../regex';
|
||||
import moment from 'moment';
|
||||
|
||||
export function DateValidator(control: AbstractControl) {
|
||||
if (control.value) {
|
||||
let date: Date = null;
|
||||
|
||||
if (control.value instanceof Date) {
|
||||
date = control.value;
|
||||
} else if (typeof control.value === 'string') {
|
||||
if (DE_DATE_REGEX.test(control.value)) {
|
||||
date = moment(control.value, 'L').toDate();
|
||||
} else if (ISO_DATE_REGEX.test(control.value)) {
|
||||
date = new Date(control.value);
|
||||
}
|
||||
}
|
||||
|
||||
if (date === null) {
|
||||
return { date: 'Datum ist ungültig' };
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
1
apps/shared/forms/src/lib/validators/index.ts
Normal file
1
apps/shared/forms/src/lib/validators/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './date.validator';
|
||||
2
apps/shared/forms/src/public-api.ts
Normal file
2
apps/shared/forms/src/public-api.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './lib/directives';
|
||||
export * from './lib/validators';
|
||||
@@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
|
||||
import { ComponentStore, OnStoreInit, tapResponse } from '@ngrx/component-store';
|
||||
import { AddToShoppingCartDTO, AvailabilityDTO, BranchDTO, CheckoutDTO, ShoppingCartDTO, ShoppingCartItemDTO } from '@swagger/checkout';
|
||||
import { DomainCheckoutService } from '@domain/checkout';
|
||||
import { mergeMap, switchMap, tap, withLatestFrom } from 'rxjs/operators';
|
||||
import { catchError, mergeMap, switchMap, tap, withLatestFrom } from 'rxjs/operators';
|
||||
import {
|
||||
BranchService,
|
||||
DisplayOrderDTO,
|
||||
@@ -12,12 +12,13 @@ import {
|
||||
ResponseArgsOfIEnumerableOfBranchDTO,
|
||||
ResponseArgsOfValueTupleOfIEnumerableOfDisplayOrderDTOAndIEnumerableOfKeyValueDTOOfStringAndString,
|
||||
} from '@swagger/oms';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Observable, of, zip } from 'rxjs';
|
||||
import { AuthService } from '@core/auth';
|
||||
import { UiModalService } from '@ui/modal';
|
||||
import { getCatalogProductNumber } from './catalog-product-number';
|
||||
import { ItemDTO } from '@swagger/cat';
|
||||
import { DomainAvailabilityService } from '@domain/availability';
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
|
||||
export interface KulturpassOrderModalState {
|
||||
orderItemListItem?: OrderItemListItemDTO;
|
||||
@@ -200,9 +201,18 @@ export class KulturpassOrderModalStore extends ComponentStore<KulturpassOrderMod
|
||||
|
||||
addItemToShoppingCart = this.effect((item$: Observable<ItemDTO>) =>
|
||||
item$.pipe(
|
||||
mergeMap((item) =>
|
||||
this._availabilityService
|
||||
.getTakeAwayAvailability({
|
||||
mergeMap((item) => {
|
||||
const takeAwayAvailability$ = this._availabilityService.getTakeAwayAvailability({
|
||||
item: {
|
||||
ean: item.product.ean,
|
||||
itemId: item.id,
|
||||
price: item.catalogAvailability.price,
|
||||
},
|
||||
quantity: this.itemQuantityByCatalogProductNumber(getCatalogProductNumber(item)) + 1,
|
||||
});
|
||||
|
||||
const deliveryAvailability$ = this._availabilityService
|
||||
.getDeliveryAvailability({
|
||||
item: {
|
||||
ean: item.product.ean,
|
||||
itemId: item.id,
|
||||
@@ -210,12 +220,45 @@ export class KulturpassOrderModalStore extends ComponentStore<KulturpassOrderMod
|
||||
},
|
||||
quantity: this.itemQuantityByCatalogProductNumber(getCatalogProductNumber(item)) + 1,
|
||||
})
|
||||
.pipe(tapResponse(this.handleAddItemToShoppingCartResponse(item), this.handleAddItemToShoppingCartError))
|
||||
)
|
||||
.pipe(
|
||||
catchError((err) => {
|
||||
return of(undefined);
|
||||
})
|
||||
);
|
||||
|
||||
return zip(takeAwayAvailability$, deliveryAvailability$).pipe(
|
||||
tapResponse(this.handleAddItemToShoppingCartResponse2(item), this.handleAddItemToShoppingCartError)
|
||||
);
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
handleAddItemToShoppingCartResponse = (item: ItemDTO) => (availability: AvailabilityDTO) => {
|
||||
handleAddItemToShoppingCartResponse2 = (item: ItemDTO) => ([takeAwayAvailability, deliveryAvailability]: [
|
||||
AvailabilityDTO,
|
||||
AvailabilityDTO
|
||||
]) => {
|
||||
const isPriceMaintained = deliveryAvailability['priceMaintained'] ?? false;
|
||||
const offlinePrice = takeAwayAvailability.price?.value?.value ?? -1;
|
||||
const onlinePrice = deliveryAvailability?.price?.value?.value ?? -1;
|
||||
|
||||
const availability = takeAwayAvailability;
|
||||
|
||||
/**
|
||||
* Onlinepreis ist niedliger als der Offlinepreis
|
||||
* wenn der Artikel nicht Preisgebunden ist, wird der Onlinepreis genommen
|
||||
* wenn der Artikel Preisgebunden ist, wird der Ladenpreis verwendet
|
||||
*/
|
||||
|
||||
/**
|
||||
* Offlinepreis ist niedliger als der Onlinepreis
|
||||
* wenn der Artikel nicht Preisgebunden ist, wird der Ladenpreis genommen
|
||||
* wenn der Artikel Preisgebunden ist, wird der Ladenpreis verwendet
|
||||
*/
|
||||
|
||||
if (onlinePrice < offlinePrice && !isPriceMaintained) {
|
||||
availability.price = deliveryAvailability.price;
|
||||
}
|
||||
|
||||
this.setAvailability({
|
||||
catalogProductNumber: getCatalogProductNumber(item),
|
||||
availability: availability,
|
||||
@@ -240,6 +283,31 @@ export class KulturpassOrderModalStore extends ComponentStore<KulturpassOrderMod
|
||||
this.addItem(addToShoppingCartDTO);
|
||||
};
|
||||
|
||||
// handleAddItemToShoppingCartResponse = (item: ItemDTO) => (availability: AvailabilityDTO) => {
|
||||
// this.setAvailability({
|
||||
// catalogProductNumber: getCatalogProductNumber(item),
|
||||
// availability: availability,
|
||||
// });
|
||||
|
||||
// const addToShoppingCartDTO: AddToShoppingCartDTO = {
|
||||
// quantity: 1,
|
||||
// availability: availability,
|
||||
// destination: {
|
||||
// data: {
|
||||
// target: 1,
|
||||
// targetBranch: { id: this.branch.id },
|
||||
// },
|
||||
// },
|
||||
// promotion: {
|
||||
// points: 0,
|
||||
// },
|
||||
// itemType: item.type,
|
||||
// product: { catalogProductNumber: getCatalogProductNumber(item), ...item.product },
|
||||
// };
|
||||
|
||||
// this.addItem(addToShoppingCartDTO);
|
||||
// };
|
||||
|
||||
handleAddItemToShoppingCartError = (err: any) => {
|
||||
this._modal.error('Fehler beim Hinzufügen des Artikels', err);
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<div class="shared-purchase-options-list-item__contributors font-bold">
|
||||
{{ product?.contributors }}
|
||||
</div>
|
||||
<div class="shared-purchase-options-list-item__name font-bold h-12" scaleContent>
|
||||
<div class="shared-purchase-options-list-item__name font-bold h-12" sharedScaleContent>
|
||||
{{ product?.name }}
|
||||
</div>
|
||||
<div class="shared-purchase-options-list-item__format flex flex-row items-center">
|
||||
@@ -82,67 +82,40 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="shared-purchase-options-list-item__price text-right ml-4 flex flex-col items-end">
|
||||
<div
|
||||
class="shared-purchase-options-list-item__price-value font-bold text-xl flex flex-row items-center"
|
||||
*ngIf="!(canEditPrice$ | async)"
|
||||
>
|
||||
<div class="shared-purchase-options-list-item__price-value font-bold text-xl flex flex-row items-center">
|
||||
<ui-svg-icon class="mr-3" [uiOverlayTrigger]="tooltip" icon="mat-info" *ngIf="priceMaintained$ | async"></ui-svg-icon>
|
||||
<ui-tooltip #tooltip yPosition="above" xPosition="after" [yOffset]="-8" [xOffset]="5" [closeable]="true">
|
||||
Günstigerer Preis aus Hugendubel Katalog wird übernommen
|
||||
</ui-tooltip>
|
||||
<ng-container *ngIf="!(setManualPrice$ | async); else setManualPrice">
|
||||
{{ priceValue$ | async | currency: 'EUR':'code' }}
|
||||
</ng-container>
|
||||
<ng-template #setManualPrice>
|
||||
<div class="relative flex flex-row items-start">
|
||||
<ui-select
|
||||
class="w-[6.5rem] min-h-[3.4375rem] p-4 rounded border border-solid border-[#AEB7C1] mr-4"
|
||||
tabindex="-1"
|
||||
[formControl]="manualVatFormControl"
|
||||
[defaultLabel]="'MwSt'"
|
||||
>
|
||||
<ui-select-option *ngFor="let vat of vats$ | async" [label]="vat.name + '%'" [value]="vat.vatType"></ui-select-option>
|
||||
</ui-select>
|
||||
|
||||
<shared-input-control [class.ml-8]="manualPriceFormControl?.invalid && manualPriceFormControl?.dirty">
|
||||
<shared-input-control-indicator>
|
||||
<shared-icon *ngIf="manualPriceFormControl?.invalid && manualPriceFormControl?.dirty" icon="mat-info"></shared-icon>
|
||||
</shared-input-control-indicator>
|
||||
<input
|
||||
triggerOn="init"
|
||||
#quantityInput
|
||||
sharedInputControlInput
|
||||
type="string"
|
||||
class="w-24"
|
||||
[formControl]="manualPriceFormControl"
|
||||
placeholder="00,00"
|
||||
(sharedOnInit)="quantityInput.focus()"
|
||||
sharedNumberValue
|
||||
/>
|
||||
<shared-input-control-suffix>EUR</shared-input-control-suffix>
|
||||
<shared-input-control-error error="required">Preis ist ungültig</shared-input-control-error>
|
||||
<shared-input-control-error error="pattern">Preis ist ungültig</shared-input-control-error>
|
||||
<shared-input-control-error error="max">Preis ist ungültig</shared-input-control-error>
|
||||
</shared-input-control>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
<div class="shared-purchase-options-list-item__price-value font-bold text-xl" *ngIf="canEditPrice$ | async">
|
||||
<div class="relative flex flex-col">
|
||||
<shared-input-control>
|
||||
<div class="relative flex flex-row justify-end items-start">
|
||||
<ui-select
|
||||
*ngIf="canEditVat$ | async"
|
||||
class="w-[6.5rem] min-h-[3.4375rem] p-4 rounded-card border border-solid border-[#AEB7C1] mr-4"
|
||||
tabindex="-1"
|
||||
[formControl]="manualVatFormControl"
|
||||
[defaultLabel]="'MwSt'"
|
||||
>
|
||||
<ui-select-option *ngFor="let vat of vats$ | async" [label]="vat.name + '%'" [value]="vat.vatType"></ui-select-option>
|
||||
</ui-select>
|
||||
<shared-input-control
|
||||
[class.ml-6]="priceFormControl?.invalid && priceFormControl?.dirty"
|
||||
*ngIf="canEditPrice$ | async; else priceTmpl"
|
||||
>
|
||||
<shared-input-control-indicator>
|
||||
<shared-icon *ngIf="priceFormControl?.invalid && priceFormControl?.dirty" icon="mat-info"></shared-icon>
|
||||
</shared-input-control-indicator>
|
||||
<input
|
||||
[uiOverlayTrigger]="tooltip"
|
||||
triggerOn="init"
|
||||
[uiOverlayTrigger]="giftCardTooltip"
|
||||
triggerOn="none"
|
||||
#quantityInput
|
||||
#priceOverlayTrigger="uiOverlayTrigger"
|
||||
sharedInputControlInput
|
||||
type="string"
|
||||
class="w-24"
|
||||
[formControl]="priceFormControl"
|
||||
placeholder="00,00"
|
||||
(sharedOnInit)="quantityInput.focus()"
|
||||
(sharedOnInit)="onPriceInputInit(quantityInput, priceOverlayTrigger)"
|
||||
sharedNumberValue
|
||||
/>
|
||||
<shared-input-control-suffix>EUR</shared-input-control-suffix>
|
||||
@@ -152,11 +125,14 @@
|
||||
<shared-input-control-error error="max">Preis ist ungültig</shared-input-control-error>
|
||||
</shared-input-control>
|
||||
|
||||
<ui-tooltip [warning]="true" xPosition="after" yPosition="below" [xOffset]="-55" [yOffset]="18" [closeable]="true" #tooltip>
|
||||
<ui-tooltip [warning]="true" xPosition="after" yPosition="below" [xOffset]="-55" [yOffset]="18" [closeable]="true" #giftCardTooltip>
|
||||
Tragen Sie hier den <br />
|
||||
Gutscheinbetrag ein.
|
||||
</ui-tooltip>
|
||||
</div>
|
||||
<ng-template #priceTmpl>
|
||||
{{ priceValue$ | async | currency: 'EUR':'code' }}
|
||||
</ng-template>
|
||||
</div>
|
||||
<ui-quantity-dropdown class="mt-2" [formControl]="quantityFormControl" [range]="maxSelectableQuantity$ | async"> </ui-quantity-dropdown>
|
||||
<div class="pt-7">
|
||||
|
||||
@@ -1,78 +1,22 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import {
|
||||
Component,
|
||||
ChangeDetectionStrategy,
|
||||
Input,
|
||||
OnInit,
|
||||
OnDestroy,
|
||||
OnChanges,
|
||||
SimpleChanges,
|
||||
AfterContentInit,
|
||||
ElementRef,
|
||||
Renderer2,
|
||||
} from '@angular/core';
|
||||
import { Component, ChangeDetectionStrategy, Input, OnInit, OnDestroy, OnChanges, SimpleChanges } from '@angular/core';
|
||||
import { FormControl, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||
import { ProductImageModule } from '@cdn/product-image';
|
||||
import { InputControlModule } from '@shared/components/input-control';
|
||||
import { ElementLifecycleModule } from '@shared/directives/element-lifecycle';
|
||||
import { UiCommonModule } from '@ui/common';
|
||||
import { UiCommonModule, UiOverlayTriggerDirective } from '@ui/common';
|
||||
import { UiQuantityDropdownModule } from '@ui/quantity-dropdown';
|
||||
import { UiSpinnerModule } from '@ui/spinner';
|
||||
import { UiTooltipModule } from '@ui/tooltip';
|
||||
import { combineLatest, ReplaySubject, Subscription } from 'rxjs';
|
||||
import { IconComponent } from '@shared/components/icon';
|
||||
import { map, take, shareReplay, startWith, switchMap, withLatestFrom, last } from 'rxjs/operators';
|
||||
import { map, take, shareReplay, startWith, switchMap, withLatestFrom } from 'rxjs/operators';
|
||||
import { GIFT_CARD_MAX_PRICE, PRICE_PATTERN } from '../constants';
|
||||
import { Item, PurchaseOptionsStore } from '../store';
|
||||
import { OrderDeadlinePipeModule } from '@shared/pipes/order-deadline';
|
||||
import { UiSelectModule } from '@ui/select';
|
||||
import { KeyValueDTOOfStringAndString } from '@swagger/cat';
|
||||
|
||||
@Component({
|
||||
selector: 'scale-content, [scaleContent]',
|
||||
template: '<ng-content></ng-content>',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
styles: [
|
||||
`
|
||||
:host {
|
||||
overflow-y: hidden;
|
||||
}
|
||||
`,
|
||||
],
|
||||
})
|
||||
export class ScaleContentComponent implements AfterContentInit {
|
||||
// TODO: Bessere Lösung finden? Falls keine bessere Lösung gefunden wird, dann muss die Komponente auslagen
|
||||
|
||||
fontSizeInEm = 1;
|
||||
|
||||
adjustmentSteps = 0.05;
|
||||
|
||||
constructor(private _elementRef: ElementRef<HTMLElement>, private _renderer: Renderer2) {}
|
||||
|
||||
ngAfterContentInit(): void {
|
||||
this.adjustFontSize();
|
||||
}
|
||||
|
||||
adjustFontSize() {
|
||||
const element = this._elementRef.nativeElement;
|
||||
|
||||
const clientRect = element?.getClientRects();
|
||||
const scrollHeight = element?.scrollHeight;
|
||||
|
||||
const domRect = clientRect && clientRect[0];
|
||||
|
||||
if (domRect && Math.ceil(domRect?.height) < scrollHeight) {
|
||||
this.fontSizeInEm -= this.adjustmentSteps;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
this._renderer.setStyle(element, 'font-size', `${this.fontSizeInEm}em`);
|
||||
|
||||
setTimeout(() => this.adjustFontSize(), 1);
|
||||
}
|
||||
}
|
||||
import { ScaleContentComponent } from '@shared/components/scale-content';
|
||||
import { IconComponent } from '@shared/components/icon';
|
||||
|
||||
@Component({
|
||||
selector: 'shared-purchase-options-list-item',
|
||||
@@ -115,14 +59,22 @@ export class PurchaseOptionsListItemComponent implements OnInit, OnDestroy, OnCh
|
||||
|
||||
quantityFormControl = new FormControl<number>(null);
|
||||
|
||||
priceFormControl = new FormControl<string>(null, [
|
||||
private readonly _giftCardValidators = [
|
||||
Validators.required,
|
||||
Validators.min(1),
|
||||
Validators.max(GIFT_CARD_MAX_PRICE),
|
||||
Validators.pattern(PRICE_PATTERN),
|
||||
]);
|
||||
];
|
||||
|
||||
private readonly _defaultValidators = [
|
||||
Validators.required,
|
||||
Validators.min(0.01),
|
||||
Validators.max(999.99),
|
||||
Validators.pattern(PRICE_PATTERN),
|
||||
];
|
||||
|
||||
priceFormControl = new FormControl<string>(null);
|
||||
|
||||
manualPriceFormControl = new FormControl<string>(null, [Validators.required, Validators.max(999.99), Validators.pattern(PRICE_PATTERN)]);
|
||||
manualVatFormControl = new FormControl<string>('', [Validators.required]);
|
||||
|
||||
selectedFormControl = new FormControl<boolean>(false);
|
||||
@@ -160,10 +112,20 @@ export class PurchaseOptionsListItemComponent implements OnInit, OnDestroy, OnCh
|
||||
|
||||
priceVat$ = this.price$.pipe(map((price) => price?.vat?.vatType));
|
||||
|
||||
canEditPrice$ = this.item$.pipe(switchMap((item) => this._store.getCanEditPrice$(item.id)));
|
||||
|
||||
canAddResult$ = this.item$.pipe(switchMap((item) => this._store.getCanAddResultForItemAndCurrentPurchaseOption$(item.id)));
|
||||
|
||||
canEditPrice$ = this.item$.pipe(
|
||||
switchMap((item) => combineLatest([this.canAddResult$, this._store.getCanEditPrice$(item.id)])),
|
||||
map(([canAddResult, canEditPrice]) => canAddResult?.canAdd && canEditPrice)
|
||||
);
|
||||
|
||||
canEditVat$ = this.item$.pipe(
|
||||
switchMap((item) => combineLatest([this.canAddResult$, this._store.getCanEditVat$(item.id)])),
|
||||
map(([canAddResult, canEditVat]) => canAddResult?.canAdd && canEditVat)
|
||||
);
|
||||
|
||||
isGiftCard$ = this.item$.pipe(switchMap((item) => this._store.getIsGiftCard$(item.id)));
|
||||
|
||||
maxSelectableQuantity$ = combineLatest([this._store.purchaseOption$, this.availability$]).pipe(
|
||||
map(([purchaseOption, availability]) => {
|
||||
if (purchaseOption === 'in-store') {
|
||||
@@ -205,6 +167,14 @@ export class PurchaseOptionsListItemComponent implements OnInit, OnDestroy, OnCh
|
||||
|
||||
constructor(private _store: PurchaseOptionsStore) {}
|
||||
|
||||
onPriceInputInit(target: HTMLElement, overlayTrigger: UiOverlayTriggerDirective) {
|
||||
if (this._store.getIsGiftCard(this.item.id)) {
|
||||
overlayTrigger.open();
|
||||
}
|
||||
|
||||
target?.focus();
|
||||
}
|
||||
|
||||
// Wichtig für das korrekte Setzen des Preises an das Item für den Endpoint request
|
||||
parsePrice(value: string) {
|
||||
if (PRICE_PATTERN.test(value)) {
|
||||
@@ -225,10 +195,11 @@ export class PurchaseOptionsListItemComponent implements OnInit, OnDestroy, OnCh
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.initPriceValidatorSubscription();
|
||||
this.initQuantitySubscription();
|
||||
this.initPriceSubscription();
|
||||
this.initVatSubscription();
|
||||
this.initSelectedSubscription();
|
||||
this.initManualPriceSubscriptions();
|
||||
}
|
||||
|
||||
ngOnChanges({ item }: SimpleChanges) {
|
||||
@@ -242,14 +213,16 @@ export class PurchaseOptionsListItemComponent implements OnInit, OnDestroy, OnCh
|
||||
this._subscriptions.unsubscribe();
|
||||
}
|
||||
|
||||
// Ticket #4074 analog zu Ticket #2244
|
||||
// Logik gilt ausschließlich für Archivartikel und über die Kaufoptionen. Nicht über den Warenkorb
|
||||
async initManualPriceSubscriptions() {
|
||||
const isManualPrice = await this.setManualPrice$.pipe(last()).toPromise();
|
||||
if (!!isManualPrice) {
|
||||
this.initManualPriceSubscription();
|
||||
this.initManualVatSubscription();
|
||||
}
|
||||
initPriceValidatorSubscription() {
|
||||
const sub = this.item$.pipe(switchMap((item) => this._store.getIsGiftCard$(item.id))).subscribe((isGiftCard) => {
|
||||
if (isGiftCard) {
|
||||
this.priceFormControl.setValidators(this._giftCardValidators);
|
||||
} else {
|
||||
this.priceFormControl.setValidators(this._defaultValidators);
|
||||
}
|
||||
});
|
||||
|
||||
this._subscriptions.add(sub);
|
||||
}
|
||||
|
||||
initQuantitySubscription() {
|
||||
@@ -279,7 +252,6 @@ export class PurchaseOptionsListItemComponent implements OnInit, OnDestroy, OnCh
|
||||
if (priceStr === '') return;
|
||||
|
||||
if (this.parsePrice(this.priceFormControl.value) !== price?.value?.value) {
|
||||
debugger;
|
||||
this.priceFormControl.setValue(priceStr);
|
||||
}
|
||||
});
|
||||
@@ -305,34 +277,7 @@ export class PurchaseOptionsListItemComponent implements OnInit, OnDestroy, OnCh
|
||||
this._subscriptions.add(valueChangesSub);
|
||||
}
|
||||
|
||||
initManualPriceSubscription() {
|
||||
const sub = this.price$.subscribe((price) => {
|
||||
const priceStr = this.stringifyPrice(price?.value?.value);
|
||||
if (priceStr === '') return;
|
||||
|
||||
if (this.parsePrice(this.manualPriceFormControl.value) !== price?.value?.value) {
|
||||
this.manualPriceFormControl.setValue(priceStr);
|
||||
}
|
||||
});
|
||||
|
||||
const valueChangesSub = this.manualPriceFormControl.valueChanges.subscribe((value) => {
|
||||
const price = this._store.getPrice(this.item.id);
|
||||
const parsedPrice = this.parsePrice(value);
|
||||
|
||||
if (!parsedPrice) {
|
||||
this._store.setPrice(this.item.id, null, true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (price[this.item.id] !== parsedPrice) {
|
||||
this._store.setPrice(this.item.id, this.parsePrice(value), true);
|
||||
}
|
||||
});
|
||||
this._subscriptions.add(sub);
|
||||
this._subscriptions.add(valueChangesSub);
|
||||
}
|
||||
|
||||
initManualVatSubscription() {
|
||||
initVatSubscription() {
|
||||
const valueChangesSub = this.manualVatFormControl.valueChanges.pipe(withLatestFrom(this.vats$)).subscribe(([formVatType, vats]) => {
|
||||
const price = this._store.getPrice(this.item.id);
|
||||
|
||||
|
||||
@@ -25,18 +25,13 @@
|
||||
</div>
|
||||
<div class="text-center -mx-4 border-t border-gray-200 p-4 border-solid">
|
||||
<ng-container *ngIf="type === 'add'">
|
||||
<button
|
||||
type="button"
|
||||
class="isa-cta-button"
|
||||
[disabled]="!(canContinue$ | async) || saving || !(hasPrice$ | async)"
|
||||
(click)="save('continue-shopping')"
|
||||
>
|
||||
<button type="button" class="isa-cta-button" [disabled]="!(canContinue$ | async) || saving" (click)="save('continue-shopping')">
|
||||
Weiter einkaufen
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="ml-4 isa-cta-button isa-button-primary"
|
||||
[disabled]="!(canContinue$ | async) || saving || !(hasPrice$ | async)"
|
||||
[disabled]="!(canContinue$ | async) || saving"
|
||||
(click)="save('continue')"
|
||||
>
|
||||
Fortfahren
|
||||
@@ -46,7 +41,7 @@
|
||||
<button
|
||||
type="button"
|
||||
class="ml-4 isa-cta-button isa-button-primary"
|
||||
[disabled]="!(canContinue$ | async) || saving || !(hasPrice$ | async)"
|
||||
[disabled]="!(canContinue$ | async) || saving"
|
||||
(click)="save('continue')"
|
||||
>
|
||||
Fortfahren
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
PickupPurchaseOptionTileComponent,
|
||||
} from './purchase-options-tile';
|
||||
import { isGiftCard, Item, PurchaseOption, PurchaseOptionsStore } from './store';
|
||||
import { delay, map, shareReplay, skip, switchMap, takeUntil } from 'rxjs/operators';
|
||||
import { delay, map, shareReplay, skip, switchMap, takeUntil, tap } from 'rxjs/operators';
|
||||
import { KeyValueDTOOfStringAndString } from '@swagger/cat';
|
||||
|
||||
@Component({
|
||||
@@ -83,7 +83,7 @@ export class PurchaseOptionsModalComponent implements OnInit, OnDestroy {
|
||||
|
||||
hasDownload$ = this.purchasingOptions$.pipe(map((purchasingOptions) => purchasingOptions.includes('download')));
|
||||
|
||||
canContinue$ = this.store.canContinue$.pipe(shareReplay(1));
|
||||
canContinue$ = this.store.canContinue$;
|
||||
|
||||
private _onDestroy$ = new Subject<void>();
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
export * from './purchase-options.helpers';
|
||||
export * from './purchase-options.selectors';
|
||||
export * from './purchase-options.service';
|
||||
export * from './purchase-options.state';
|
||||
export * from './purchase-options.store';
|
||||
|
||||
@@ -60,6 +60,14 @@ export function isGiftCard(item: Item, type: ActionType): boolean {
|
||||
}
|
||||
}
|
||||
|
||||
export function isArchive(item: Item, type: ActionType): boolean {
|
||||
if (isItemDTO(item, type)) {
|
||||
return item?.features?.some((f) => f.key === 'ARC');
|
||||
} else {
|
||||
return !!item?.features?.['ARC'];
|
||||
}
|
||||
}
|
||||
|
||||
export function mapToItemPayload({
|
||||
item,
|
||||
quantity,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { PriceDTO, PriceValueDTO } from '@swagger/checkout';
|
||||
import { DEFAULT_PRICE_DTO, DEFAULT_PRICE_VALUE, GIFT_CARD_MAX_PRICE, GIFT_CARD_TYPE, PURCHASE_OPTIONS } from '../constants';
|
||||
import { PriceDTO } from '@swagger/checkout';
|
||||
import { DEFAULT_PRICE_DTO, GIFT_CARD_MAX_PRICE, PURCHASE_OPTIONS } from '../constants';
|
||||
import { isGiftCard, isItemDTO } from './purchase-options.helpers';
|
||||
import { PurchaseOptionsState } from './purchase-options.state';
|
||||
import { ActionType, Availability, Branch, CanAdd, FetchingAvailability, Item, PurchaseOption } from './purchase-options.types';
|
||||
@@ -198,31 +198,66 @@ export function getAvailabilitiesForItem(
|
||||
};
|
||||
}
|
||||
|
||||
export function isArchive(item: Item, type: ActionType): boolean {
|
||||
if (isItemDTO(item, type)) {
|
||||
return item?.features?.some((f) => f.key === 'ARC');
|
||||
} else {
|
||||
return !!item?.features['ARC'];
|
||||
}
|
||||
}
|
||||
|
||||
export function getCanEditPrice(itemId: number): (state: PurchaseOptionsState) => boolean {
|
||||
return (state) => {
|
||||
const item = getItems(state).find((item) => item.id === itemId);
|
||||
|
||||
if (isGiftCard(item, getType(state))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const purchaseOption = getPurchaseOption(state);
|
||||
|
||||
if (isArchive(item, getType(state)) && !getAvailabilityPriceForPurchaseOption(itemId, purchaseOption)(state)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
export function getCanEditVat(itemId: number): (state: PurchaseOptionsState) => boolean {
|
||||
return (state) => {
|
||||
const item = getItems(state).find((item) => item.id === itemId);
|
||||
const purchaseOption = getPurchaseOption(state);
|
||||
|
||||
if (isArchive(item, getType(state)) && !getAvailabilityPriceForPurchaseOption(itemId, purchaseOption)(state)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
export function getIsGiftCard(itemId: number): (state: PurchaseOptionsState) => boolean {
|
||||
return (state) => {
|
||||
const item = getItems(state).find((item) => item.id === itemId);
|
||||
return isGiftCard(item, getType(state));
|
||||
};
|
||||
}
|
||||
|
||||
export function getPriceForPurchaseOption(
|
||||
export function getAvailabilityPriceForPurchaseOption(
|
||||
itemId: number,
|
||||
purchaseOption: PurchaseOption
|
||||
): (state: PurchaseOptionsState) => PriceDTO & { fromCatalogue?: boolean } {
|
||||
): (state: PurchaseOptionsState) => (PriceDTO & { fromCatalogue?: boolean }) | undefined {
|
||||
return (state) => {
|
||||
if (getCanEditPrice(itemId)(state)) {
|
||||
const price = getPrices(state)[itemId];
|
||||
|
||||
if (price) {
|
||||
return price;
|
||||
}
|
||||
}
|
||||
|
||||
const item = getItems(state).find((item) => item.id === itemId);
|
||||
|
||||
const type = getType(state);
|
||||
|
||||
let availabilities = getAvailabilitiesForItem(itemId)(state);
|
||||
|
||||
let availability = availabilities.find((availability) => availability.purchaseOption === purchaseOption);
|
||||
@@ -259,13 +294,29 @@ export function getPriceForPurchaseOption(
|
||||
}
|
||||
|
||||
if (isItemDTO(item, type)) {
|
||||
return item?.catalogAvailability?.price ?? DEFAULT_PRICE_DTO;
|
||||
return item?.catalogAvailability?.price;
|
||||
} else {
|
||||
return item?.unitPrice ?? DEFAULT_PRICE_DTO;
|
||||
return item?.unitPrice;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function getPriceForPurchaseOption(
|
||||
itemId: number,
|
||||
purchaseOption: PurchaseOption
|
||||
): (state: PurchaseOptionsState) => PriceDTO & { fromCatalogue?: boolean } {
|
||||
return (state) => {
|
||||
if (getCanEditPrice(itemId)(state)) {
|
||||
const price = getPrices(state)[itemId];
|
||||
if (price) {
|
||||
return price;
|
||||
}
|
||||
}
|
||||
|
||||
return getAvailabilityPriceForPurchaseOption(itemId, purchaseOption)(state) ?? DEFAULT_PRICE_DTO;
|
||||
};
|
||||
}
|
||||
|
||||
export function getQuantityForItem(itemId: number): (state: PurchaseOptionsState) => number {
|
||||
return (state) => {
|
||||
const item = getItems(state).find((item) => item.id === itemId);
|
||||
@@ -352,12 +403,21 @@ export function canContinue(state: PurchaseOptionsState): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
const actionType = getType(state);
|
||||
|
||||
for (let item of items) {
|
||||
if (isGiftCard(item, getType(state))) {
|
||||
if (isGiftCard(item, actionType)) {
|
||||
const price = getPriceForPurchaseOption(item.id, purchaseOption)(state);
|
||||
if (!(price?.value?.value > 0 && price?.value?.value <= GIFT_CARD_MAX_PRICE)) {
|
||||
return false;
|
||||
}
|
||||
} else if (isArchive(item, actionType) && !getAvailabilityPriceForPurchaseOption(item.id, purchaseOption)(state)) {
|
||||
const price = getPriceForPurchaseOption(item.id, purchaseOption)(state);
|
||||
const hasPrice = price?.value?.value > 0;
|
||||
const hasVat = price?.vat?.vatType > 0;
|
||||
if (!(hasPrice && hasVat)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -612,8 +612,8 @@ export class PurchaseOptionsStore extends ComponentStore<PurchaseOptionsState> {
|
||||
});
|
||||
}
|
||||
|
||||
isGiftcard(itemId: number) {
|
||||
return this._service;
|
||||
getIsGiftCard(itemId: number) {
|
||||
return this.get(Selectors.getIsGiftCard(itemId));
|
||||
}
|
||||
|
||||
getPrice(itemId: number) {
|
||||
@@ -640,9 +640,22 @@ export class PurchaseOptionsStore extends ComponentStore<PurchaseOptionsState> {
|
||||
return this.select(Selectors.getCanEditPrice(itemId));
|
||||
}
|
||||
|
||||
getCanEditVat(itemId: number) {
|
||||
return this.get(Selectors.getCanEditVat(itemId));
|
||||
}
|
||||
|
||||
getCanEditVat$(itemId: number) {
|
||||
return this.select(Selectors.getCanEditVat(itemId));
|
||||
}
|
||||
|
||||
getIsGiftCard$(itemId: number) {
|
||||
return this.select(Selectors.getIsGiftCard(itemId));
|
||||
}
|
||||
|
||||
setPrice(itemId: number, value: number, manually: boolean = false) {
|
||||
const prices = this.prices;
|
||||
let price = prices[itemId];
|
||||
|
||||
if (price?.value?.value !== value) {
|
||||
if (!price) {
|
||||
price = {
|
||||
|
||||
@@ -68,15 +68,19 @@ export class ShellProcessBarComponent implements OnInit {
|
||||
setTimeout(() => this.scrollToEnd(), 25);
|
||||
}
|
||||
|
||||
static REGEX_PROCESS_NAME = /^Vorgang \d+$/;
|
||||
|
||||
async createCartProcess() {
|
||||
const processes = await this._app.getProcesses$('customer').pipe(first()).toPromise();
|
||||
|
||||
const count = processes.filter((x) => x.type === 'cart' && x.name.startsWith('Vorgang ')).length;
|
||||
const processIds = processes.filter((x) => ShellProcessBarComponent.REGEX_PROCESS_NAME.test(x.name)).map((x) => +x.name.split(' ')[1]);
|
||||
|
||||
const maxId = processIds.length > 0 ? Math.max(...processIds) : 0;
|
||||
|
||||
const process: ApplicationProcess = {
|
||||
id: Date.now(),
|
||||
type: 'cart',
|
||||
name: `Vorgang ${count + 1}`,
|
||||
name: `Vorgang ${maxId + 1}`,
|
||||
section: 'customer',
|
||||
closeable: true,
|
||||
};
|
||||
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
Directive,
|
||||
ElementRef,
|
||||
EmbeddedViewRef,
|
||||
HostBinding,
|
||||
HostListener,
|
||||
Input,
|
||||
OnChanges,
|
||||
@@ -13,10 +12,12 @@ import {
|
||||
OnInit,
|
||||
SimpleChanges,
|
||||
ViewContainerRef,
|
||||
Inject,
|
||||
} from '@angular/core';
|
||||
import { asapScheduler, Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { asapScheduler, Subject, fromEvent, Subscription } from 'rxjs';
|
||||
import { take, takeUntil } from 'rxjs/operators';
|
||||
import { UiOverlayTrigger } from './overlay-trigger';
|
||||
import { DOCUMENT } from '@angular/common';
|
||||
|
||||
@Directive({ selector: '[uiOverlayTrigger]', exportAs: 'uiOverlayTrigger' })
|
||||
export class UiOverlayTriggerDirective implements OnInit, OnDestroy, OnChanges {
|
||||
@@ -24,7 +25,7 @@ export class UiOverlayTriggerDirective implements OnInit, OnDestroy, OnChanges {
|
||||
component: UiOverlayTrigger;
|
||||
|
||||
@Input()
|
||||
triggerOn: 'click' | 'hover' | 'init' = 'click';
|
||||
triggerOn: 'click' | 'hover' | 'init' | 'none' = 'click';
|
||||
|
||||
@Input()
|
||||
overlayTriggerDisabled: boolean;
|
||||
@@ -32,6 +33,7 @@ export class UiOverlayTriggerDirective implements OnInit, OnDestroy, OnChanges {
|
||||
private overlayRef: OverlayRef;
|
||||
private viewRef: EmbeddedViewRef<any>;
|
||||
private _onDestroy$ = new Subject<void>();
|
||||
private _clickListenerSub: Subscription;
|
||||
|
||||
get opened() {
|
||||
return !!this.viewRef;
|
||||
@@ -42,7 +44,8 @@ export class UiOverlayTriggerDirective implements OnInit, OnDestroy, OnChanges {
|
||||
private viewContainerRef: ViewContainerRef,
|
||||
private elementRef: ElementRef,
|
||||
private overlay: Overlay,
|
||||
private cdr: ChangeDetectorRef
|
||||
private cdr: ChangeDetectorRef,
|
||||
@Inject(DOCUMENT) private _document: Document
|
||||
) {}
|
||||
|
||||
ngOnChanges({ position }: SimpleChanges): void {
|
||||
@@ -107,6 +110,9 @@ export class UiOverlayTriggerDirective implements OnInit, OnDestroy, OnChanges {
|
||||
this.updatePositionStrategy();
|
||||
|
||||
this.viewRef = this.overlayRef.attach(dropdownPortal);
|
||||
|
||||
this.registerCloseOnClickListener();
|
||||
|
||||
this.component.close = () => this.close();
|
||||
|
||||
this.cdr.markForCheck();
|
||||
@@ -116,6 +122,7 @@ export class UiOverlayTriggerDirective implements OnInit, OnDestroy, OnChanges {
|
||||
this.viewRef?.destroy();
|
||||
this.overlayRef.detach();
|
||||
delete this.viewRef;
|
||||
this._clickListenerSub.unsubscribe();
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
|
||||
@@ -130,6 +137,18 @@ export class UiOverlayTriggerDirective implements OnInit, OnDestroy, OnChanges {
|
||||
.subscribe(() => this.close());
|
||||
}
|
||||
|
||||
registerCloseOnClickListener() {
|
||||
asapScheduler.schedule(() => {
|
||||
this._clickListenerSub = fromEvent(this._document.body, 'click')
|
||||
.pipe(take(1))
|
||||
.subscribe((event) => {
|
||||
if (this.viewRef && !this.overlayRef?.hostElement?.contains(event.target as HTMLElement)) {
|
||||
this.close();
|
||||
}
|
||||
});
|
||||
}, 1);
|
||||
}
|
||||
|
||||
updatePositionStrategy() {
|
||||
this.overlayRef.updatePositionStrategy(this.getPositionStrategy());
|
||||
}
|
||||
@@ -169,11 +188,4 @@ export class UiOverlayTriggerDirective implements OnInit, OnDestroy, OnChanges {
|
||||
updatePosition() {
|
||||
this.overlayRef?.updatePositionStrategy(this.getPositionStrategy());
|
||||
}
|
||||
|
||||
@HostListener('document:click', ['$event'])
|
||||
documentClick(event: MouseEvent) {
|
||||
if (this.viewRef && !this.overlayRef?.hostElement?.contains(event.target as HTMLElement)) {
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,11 +8,7 @@
|
||||
<div class="hr"></div>
|
||||
<ui-datepicker-body></ui-datepicker-body>
|
||||
<div class="text-center mb-px-10">
|
||||
<button
|
||||
*ngIf="!content"
|
||||
class="rounded-full font-bold text-white bg-brand py-px-15 px-px-25"
|
||||
(click)="save.emit(selectedDate); close()"
|
||||
>
|
||||
<button *ngIf="!content" class="rounded-full font-bold text-white bg-brand py-px-15 px-px-25" (click)="onSave()">
|
||||
<ng-container>{{ saveLabel }}</ng-container>
|
||||
</button>
|
||||
<ng-content></ng-content>
|
||||
|
||||
@@ -9,12 +9,14 @@ import {
|
||||
ViewChild,
|
||||
TemplateRef,
|
||||
ContentChild,
|
||||
ChangeDetectorRef,
|
||||
} from '@angular/core';
|
||||
import { Datepicker } from './datepicker';
|
||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { DateAdapter, UiOverlayTrigger } from '@ui/common';
|
||||
import { DatepickerPositionX, DatepickerPositionY } from './datepicker-positions';
|
||||
import { isDate } from 'lodash';
|
||||
|
||||
@Component({
|
||||
selector: 'ui-datepicker',
|
||||
@@ -59,7 +61,7 @@ export class UiDatepickerComponent extends Datepicker implements UiOverlayTrigge
|
||||
|
||||
onTouched = () => {};
|
||||
|
||||
constructor(dateAdapter: DateAdapter) {
|
||||
constructor(dateAdapter: DateAdapter, private _cdr: ChangeDetectorRef) {
|
||||
super(dateAdapter);
|
||||
const sub = this.selectedChange.subscribe((date) => {
|
||||
this.onChange(date);
|
||||
@@ -75,8 +77,18 @@ export class UiDatepickerComponent extends Datepicker implements UiOverlayTrigge
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
writeValue(obj: Date): void {
|
||||
this.setSelected(obj, { emit: false });
|
||||
writeValue(obj: Date | string): void {
|
||||
let date = undefined;
|
||||
|
||||
if (obj) {
|
||||
date = new Date(obj);
|
||||
}
|
||||
|
||||
this.setSelected(date, { emit: false });
|
||||
|
||||
this.setDisplayed(date ?? new Date(), { emit: false });
|
||||
|
||||
this._cdr.markForCheck();
|
||||
}
|
||||
|
||||
registerOnChange(fn: any): void {
|
||||
@@ -88,4 +100,11 @@ export class UiDatepickerComponent extends Datepicker implements UiOverlayTrigge
|
||||
}
|
||||
|
||||
setDisabledState?(isDisabled: boolean): void {}
|
||||
|
||||
onSave() {
|
||||
this.save.emit(this.selectedDate);
|
||||
this.onChange(this.selectedDate);
|
||||
this.onTouched();
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { EventEmitter, Input, Output, Directive } from '@angular/core';
|
||||
import { DateAdapter } from '@ui/common';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
interface SetPropertyOptions {
|
||||
emit: boolean;
|
||||
@@ -26,6 +27,7 @@ export abstract class Datepicker {
|
||||
return this.displayedSubject.value;
|
||||
}
|
||||
set displayed(val: Date) {
|
||||
console.log('setDisplayed', val);
|
||||
this.setDisplayed(val, { emit: false });
|
||||
}
|
||||
@Output()
|
||||
@@ -65,7 +67,7 @@ export abstract class Datepicker {
|
||||
return this.selectedSubject.asObservable();
|
||||
}
|
||||
get displayed$() {
|
||||
return this.displayedSubject.asObservable();
|
||||
return this.displayedSubject.asObservable()?.pipe(map((date) => date ?? this.dateAdapter.today()));
|
||||
}
|
||||
get min$() {
|
||||
return this.minSubject.asObservable();
|
||||
|
||||
@@ -82,6 +82,9 @@ export class UiSearchboxNextComponent extends UiFormControlDirective<any>
|
||||
@Input()
|
||||
hint: string = '';
|
||||
|
||||
@Output()
|
||||
hintCleared = new EventEmitter<void>();
|
||||
|
||||
@Input()
|
||||
autocompleteValueSelector: (item: any) => string = (item: any) => item;
|
||||
|
||||
@@ -196,6 +199,7 @@ export class UiSearchboxNextComponent extends UiFormControlDirective<any>
|
||||
clearHint() {
|
||||
this.hint = '';
|
||||
this.focused.emit(true);
|
||||
this.hintCleared.emit();
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
|
||||
|
||||
231
package-lock.json
generated
231
package-lock.json
generated
@@ -34,8 +34,9 @@
|
||||
"lodash": "^4.17.21",
|
||||
"moment": "^2.29.4",
|
||||
"ng2-pdf-viewer": "^9.1.3",
|
||||
"parse-duration": "^1.1.0",
|
||||
"rxjs": "^6.6.7",
|
||||
"scandit-sdk": "^5.12.1",
|
||||
"scandit-sdk": "^5.13.2",
|
||||
"socket.io": "^4.5.4",
|
||||
"tslib": "^2.0.0",
|
||||
"uglify-js": "^3.4.9",
|
||||
@@ -93,19 +94,6 @@
|
||||
"npm": "8.x"
|
||||
}
|
||||
},
|
||||
"../../@paragondata/ngx-ui/dist/paragondata/ngx-ui": {
|
||||
"name": "@paragondata/ngx-ui",
|
||||
"version": "0.0.0-watch+1683643023778",
|
||||
"extraneous": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/cdk": ">= 13.0.0 < 16.0.0",
|
||||
"@angular/common": ">= 13.0.0 < 16.0.0",
|
||||
"@angular/core": ">= 13.0.0 < 16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@ampproject/remapping": {
|
||||
"version": "2.2.0",
|
||||
"license": "Apache-2.0",
|
||||
@@ -2186,16 +2174,22 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime-corejs2": {
|
||||
"version": "7.20.7",
|
||||
"license": "MIT",
|
||||
"version": "7.22.11",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.22.11.tgz",
|
||||
"integrity": "sha512-6z+Y7otDbBpPbg+eXnYVnrR7J3bq5Xp31QiGf7bJzbBOYUXLDGXnCdmxzH3xGxczTvaSXV/oeXFV4PNq3f64Sg==",
|
||||
"dependencies": {
|
||||
"core-js": "^2.6.12",
|
||||
"regenerator-runtime": "^0.13.11"
|
||||
"regenerator-runtime": "^0.14.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime-corejs2/node_modules/regenerator-runtime": {
|
||||
"version": "0.14.0",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz",
|
||||
"integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA=="
|
||||
},
|
||||
"node_modules/@babel/template": {
|
||||
"version": "7.18.10",
|
||||
"license": "MIT",
|
||||
@@ -2273,9 +2267,8 @@
|
||||
},
|
||||
"node_modules/@cspotcode/source-map-support": {
|
||||
"version": "0.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
||||
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
@@ -2287,9 +2280,8 @@
|
||||
},
|
||||
"node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.9",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
|
||||
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
@@ -2724,8 +2716,6 @@
|
||||
},
|
||||
"node_modules/@paragondata/ngx-ui": {
|
||||
"version": "12.0.0-beta.91",
|
||||
"resolved": "https://npm.pkg.github.com/download/@paragondata/ngx-ui/12.0.0-beta.91/18864a278241d4874fc1ab1e41b8d43330721ffb",
|
||||
"integrity": "sha512-Ran2ZOw7tNhSJwaBJPDUwGwxfC/bpaVrUum+Hn7Q/RfIuC3PUmFSJkBaZdAL4eyOASIWD6ZrmNxb2qcVCo7ctA==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
@@ -2832,7 +2822,6 @@
|
||||
},
|
||||
"node_modules/@stylelint/postcss-markdown": {
|
||||
"version": "0.36.2",
|
||||
"deprecated": "Use the original unforked package instead: postcss-markdown",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -2936,33 +2925,29 @@
|
||||
},
|
||||
"node_modules/@tsconfig/node10": {
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
|
||||
"integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@tsconfig/node12": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
|
||||
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@tsconfig/node14": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
|
||||
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@tsconfig/node16": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz",
|
||||
"integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
@@ -3116,10 +3101,8 @@
|
||||
},
|
||||
"node_modules/@types/moment": {
|
||||
"version": "2.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/moment/-/moment-2.13.0.tgz",
|
||||
"integrity": "sha512-DyuyYGpV6r+4Z1bUznLi/Y7HpGn4iQ4IVcGn8zrr1P4KotKLdH0sbK1TFR6RGyX6B+G8u83wCzL+bpawKU/hdQ==",
|
||||
"deprecated": "This is a stub types definition for Moment (https://github.com/moment/moment). Moment provides its own type definitions, so you don't need @types/moment installed!",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"moment": "*"
|
||||
}
|
||||
@@ -3449,9 +3432,8 @@
|
||||
},
|
||||
"node_modules/adm-zip": {
|
||||
"version": "0.5.10",
|
||||
"resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz",
|
||||
"integrity": "sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
}
|
||||
@@ -4779,7 +4761,6 @@
|
||||
},
|
||||
"node_modules/core-js": {
|
||||
"version": "2.6.12",
|
||||
"deprecated": "core-js@<3.4 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Please, upgrade your dependencies to the actual version of core-js.",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
@@ -4828,9 +4809,8 @@
|
||||
},
|
||||
"node_modules/create-require": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
||||
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
@@ -6903,7 +6883,6 @@
|
||||
},
|
||||
"node_modules/har-validator": {
|
||||
"version": "5.1.5",
|
||||
"deprecated": "this library is no longer supported",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -7211,9 +7190,8 @@
|
||||
},
|
||||
"node_modules/http-cache-semantics": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
|
||||
"integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/http-deceiver": {
|
||||
"version": "1.2.7",
|
||||
@@ -9050,8 +9028,6 @@
|
||||
},
|
||||
"node_modules/karma/node_modules/ua-parser-js": {
|
||||
"version": "0.7.33",
|
||||
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.33.tgz",
|
||||
"integrity": "sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -9063,6 +9039,7 @@
|
||||
"url": "https://paypal.me/faisalman"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
@@ -11302,6 +11279,10 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/parse-duration": {
|
||||
"version": "1.1.0",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/parse-entities": {
|
||||
"version": "2.0.0",
|
||||
"dev": true,
|
||||
@@ -12747,6 +12728,7 @@
|
||||
},
|
||||
"node_modules/regenerator-runtime": {
|
||||
"version": "0.13.11",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/regenerator-transform": {
|
||||
@@ -13011,7 +12993,6 @@
|
||||
},
|
||||
"node_modules/request": {
|
||||
"version": "2.88.2",
|
||||
"deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
@@ -13062,7 +13043,6 @@
|
||||
},
|
||||
"node_modules/request/node_modules/uuid": {
|
||||
"version": "3.4.0",
|
||||
"deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
@@ -13452,10 +13432,11 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/scandit-sdk": {
|
||||
"version": "5.12.2",
|
||||
"license": "SEE LICENSE IN LICENSE",
|
||||
"version": "5.13.3",
|
||||
"resolved": "https://registry.npmjs.org/scandit-sdk/-/scandit-sdk-5.13.3.tgz",
|
||||
"integrity": "sha512-lETa3+ZmOXWytmAzb+PtenvU878UvOvsCVP4RhmF/HkyzjhobS/OOsluc0gaWh7U7d+gJIrnlheY8pTehARtqQ==",
|
||||
"dependencies": {
|
||||
"@babel/runtime-corejs2": "^7.20.7",
|
||||
"@babel/runtime-corejs2": "^7.20.13",
|
||||
"@juggle/resize-observer": "^3.4.0",
|
||||
"csstype": "^3.1.1",
|
||||
"eventemitter3": "^5.0.0",
|
||||
@@ -13463,15 +13444,16 @@
|
||||
"js-cookie": "^3.0.1",
|
||||
"objectFitPolyfill": "^2.3.5",
|
||||
"tslib": "^2.4.1",
|
||||
"ua-parser-js": "^1.0.32"
|
||||
"ua-parser-js": "^1.0.33"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.18"
|
||||
}
|
||||
},
|
||||
"node_modules/scandit-sdk/node_modules/eventemitter3": {
|
||||
"version": "5.0.0",
|
||||
"license": "MIT"
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
|
||||
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="
|
||||
},
|
||||
"node_modules/schema-utils": {
|
||||
"version": "4.0.0",
|
||||
@@ -15195,7 +15177,6 @@
|
||||
},
|
||||
"node_modules/tslint": {
|
||||
"version": "6.1.3",
|
||||
"deprecated": "TSLint has been deprecated in favor of ESLint. Please see https://github.com/palantir/tslint/issues/4534 for more information.",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
@@ -15398,8 +15379,6 @@
|
||||
},
|
||||
"node_modules/ua-parser-js": {
|
||||
"version": "1.0.33",
|
||||
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.33.tgz",
|
||||
"integrity": "sha512-RqshF7TPTE0XLYAqmjlu5cLLuGdKrNu9O1KLA/qp39QtbZwuzwv1dT46DZSopoUMsYgXpB3Cv8a03FI8b74oFQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@@ -15410,6 +15389,7 @@
|
||||
"url": "https://paypal.me/faisalman"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
@@ -15637,9 +15617,8 @@
|
||||
},
|
||||
"node_modules/v8-compile-cache-lib": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
|
||||
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
@@ -15749,8 +15728,7 @@
|
||||
},
|
||||
"node_modules/web-animations-js": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/web-animations-js/-/web-animations-js-2.3.2.tgz",
|
||||
"integrity": "sha512-TOMFWtQdxzjWp8qx4DAraTWTsdhxVSiWa6NkPFSaPtZ1diKUxTn4yTix73A1euG1WbSOMMPcY51cnjTIHrGtDA=="
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/web-streams-polyfill": {
|
||||
"version": "3.2.1",
|
||||
@@ -15773,9 +15751,8 @@
|
||||
},
|
||||
"node_modules/webdriver-manager": {
|
||||
"version": "12.1.9",
|
||||
"resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.1.9.tgz",
|
||||
"integrity": "sha512-Yl113uKm8z4m/KMUVWHq1Sjtla2uxEBtx2Ue3AmIlnlPAKloDn/Lvmy6pqWCUersVISpdMeVpAaGbNnvMuT2LQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"adm-zip": "^0.5.2",
|
||||
"chalk": "^1.1.1",
|
||||
@@ -15798,24 +15775,27 @@
|
||||
},
|
||||
"node_modules/webdriver-manager/node_modules/ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||
"integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/webdriver-manager/node_modules/ansi-styles": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
|
||||
"integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/webdriver-manager/node_modules/brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
@@ -15823,8 +15803,9 @@
|
||||
},
|
||||
"node_modules/webdriver-manager/node_modules/chalk": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
|
||||
"integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^2.2.1",
|
||||
"escape-string-regexp": "^1.0.2",
|
||||
@@ -15838,8 +15819,9 @@
|
||||
},
|
||||
"node_modules/webdriver-manager/node_modules/glob": {
|
||||
"version": "7.2.3",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
@@ -15857,13 +15839,15 @@
|
||||
},
|
||||
"node_modules/webdriver-manager/node_modules/ini": {
|
||||
"version": "1.3.8",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
|
||||
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/webdriver-manager/node_modules/minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
},
|
||||
@@ -15873,8 +15857,9 @@
|
||||
},
|
||||
"node_modules/webdriver-manager/node_modules/rimraf": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
|
||||
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"glob": "^7.1.3"
|
||||
},
|
||||
@@ -15883,17 +15868,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/webdriver-manager/node_modules/semver": {
|
||||
"version": "5.7.1",
|
||||
"version": "5.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
|
||||
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver"
|
||||
}
|
||||
},
|
||||
"node_modules/webdriver-manager/node_modules/strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
},
|
||||
@@ -15903,8 +15890,9 @@
|
||||
},
|
||||
"node_modules/webdriver-manager/node_modules/supports-color": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
|
||||
"integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
@@ -17702,10 +17690,19 @@
|
||||
}
|
||||
},
|
||||
"@babel/runtime-corejs2": {
|
||||
"version": "7.20.7",
|
||||
"version": "7.22.11",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.22.11.tgz",
|
||||
"integrity": "sha512-6z+Y7otDbBpPbg+eXnYVnrR7J3bq5Xp31QiGf7bJzbBOYUXLDGXnCdmxzH3xGxczTvaSXV/oeXFV4PNq3f64Sg==",
|
||||
"requires": {
|
||||
"core-js": "^2.6.12",
|
||||
"regenerator-runtime": "^0.13.11"
|
||||
"regenerator-runtime": "^0.14.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"regenerator-runtime": {
|
||||
"version": "0.14.0",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz",
|
||||
"integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@babel/template": {
|
||||
@@ -17763,8 +17760,6 @@
|
||||
},
|
||||
"@cspotcode/source-map-support": {
|
||||
"version": "0.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
||||
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
@@ -17774,8 +17769,6 @@
|
||||
"dependencies": {
|
||||
"@jridgewell/trace-mapping": {
|
||||
"version": "0.3.9",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
|
||||
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
@@ -18048,8 +18041,6 @@
|
||||
},
|
||||
"@paragondata/ngx-ui": {
|
||||
"version": "12.0.0-beta.91",
|
||||
"resolved": "https://npm.pkg.github.com/download/@paragondata/ngx-ui/12.0.0-beta.91/18864a278241d4874fc1ab1e41b8d43330721ffb",
|
||||
"integrity": "sha512-Ran2ZOw7tNhSJwaBJPDUwGwxfC/bpaVrUum+Hn7Q/RfIuC3PUmFSJkBaZdAL4eyOASIWD6ZrmNxb2qcVCo7ctA==",
|
||||
"requires": {
|
||||
"tslib": "^2.3.0"
|
||||
}
|
||||
@@ -18168,32 +18159,24 @@
|
||||
},
|
||||
"@tsconfig/node10": {
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
|
||||
"integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"@tsconfig/node12": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
|
||||
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"@tsconfig/node14": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
|
||||
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"@tsconfig/node16": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz",
|
||||
"integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"peer": true
|
||||
@@ -18327,8 +18310,6 @@
|
||||
},
|
||||
"@types/moment": {
|
||||
"version": "2.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/moment/-/moment-2.13.0.tgz",
|
||||
"integrity": "sha512-DyuyYGpV6r+4Z1bUznLi/Y7HpGn4iQ4IVcGn8zrr1P4KotKLdH0sbK1TFR6RGyX6B+G8u83wCzL+bpawKU/hdQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"moment": "*"
|
||||
@@ -18597,8 +18578,6 @@
|
||||
},
|
||||
"adm-zip": {
|
||||
"version": "0.5.10",
|
||||
"resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz",
|
||||
"integrity": "sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==",
|
||||
"dev": true
|
||||
},
|
||||
"agent-base": {
|
||||
@@ -19468,8 +19447,6 @@
|
||||
},
|
||||
"create-require": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
||||
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"peer": true
|
||||
@@ -21032,8 +21009,6 @@
|
||||
},
|
||||
"http-cache-semantics": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
|
||||
"integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==",
|
||||
"dev": true
|
||||
},
|
||||
"http-deceiver": {
|
||||
@@ -22036,8 +22011,6 @@
|
||||
},
|
||||
"ua-parser-js": {
|
||||
"version": "0.7.33",
|
||||
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.33.tgz",
|
||||
"integrity": "sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw==",
|
||||
"dev": true
|
||||
},
|
||||
"yargs": {
|
||||
@@ -23611,6 +23584,9 @@
|
||||
"callsites": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"parse-duration": {
|
||||
"version": "1.1.0"
|
||||
},
|
||||
"parse-entities": {
|
||||
"version": "2.0.0",
|
||||
"dev": true,
|
||||
@@ -24529,7 +24505,8 @@
|
||||
}
|
||||
},
|
||||
"regenerator-runtime": {
|
||||
"version": "0.13.11"
|
||||
"version": "0.13.11",
|
||||
"dev": true
|
||||
},
|
||||
"regenerator-transform": {
|
||||
"version": "0.15.1",
|
||||
@@ -24968,9 +24945,11 @@
|
||||
"dev": true
|
||||
},
|
||||
"scandit-sdk": {
|
||||
"version": "5.12.2",
|
||||
"version": "5.13.3",
|
||||
"resolved": "https://registry.npmjs.org/scandit-sdk/-/scandit-sdk-5.13.3.tgz",
|
||||
"integrity": "sha512-lETa3+ZmOXWytmAzb+PtenvU878UvOvsCVP4RhmF/HkyzjhobS/OOsluc0gaWh7U7d+gJIrnlheY8pTehARtqQ==",
|
||||
"requires": {
|
||||
"@babel/runtime-corejs2": "^7.20.7",
|
||||
"@babel/runtime-corejs2": "^7.20.13",
|
||||
"@juggle/resize-observer": "^3.4.0",
|
||||
"csstype": "^3.1.1",
|
||||
"eventemitter3": "^5.0.0",
|
||||
@@ -24978,11 +24957,13 @@
|
||||
"js-cookie": "^3.0.1",
|
||||
"objectFitPolyfill": "^2.3.5",
|
||||
"tslib": "^2.4.1",
|
||||
"ua-parser-js": "^1.0.32"
|
||||
"ua-parser-js": "^1.0.33"
|
||||
},
|
||||
"dependencies": {
|
||||
"eventemitter3": {
|
||||
"version": "5.0.0"
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
|
||||
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -26240,9 +26221,7 @@
|
||||
"version": "4.8.4"
|
||||
},
|
||||
"ua-parser-js": {
|
||||
"version": "1.0.33",
|
||||
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.33.tgz",
|
||||
"integrity": "sha512-RqshF7TPTE0XLYAqmjlu5cLLuGdKrNu9O1KLA/qp39QtbZwuzwv1dT46DZSopoUMsYgXpB3Cv8a03FI8b74oFQ=="
|
||||
"version": "1.0.33"
|
||||
},
|
||||
"uglify-js": {
|
||||
"version": "3.17.4"
|
||||
@@ -26373,8 +26352,6 @@
|
||||
},
|
||||
"v8-compile-cache-lib": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
|
||||
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"peer": true
|
||||
@@ -26451,9 +26428,7 @@
|
||||
}
|
||||
},
|
||||
"web-animations-js": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/web-animations-js/-/web-animations-js-2.3.2.tgz",
|
||||
"integrity": "sha512-TOMFWtQdxzjWp8qx4DAraTWTsdhxVSiWa6NkPFSaPtZ1diKUxTn4yTix73A1euG1WbSOMMPcY51cnjTIHrGtDA=="
|
||||
"version": "2.3.2"
|
||||
},
|
||||
"web-streams-polyfill": {
|
||||
"version": "3.2.1"
|
||||
@@ -26468,8 +26443,6 @@
|
||||
},
|
||||
"webdriver-manager": {
|
||||
"version": "12.1.9",
|
||||
"resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.1.9.tgz",
|
||||
"integrity": "sha512-Yl113uKm8z4m/KMUVWHq1Sjtla2uxEBtx2Ue3AmIlnlPAKloDn/Lvmy6pqWCUersVISpdMeVpAaGbNnvMuT2LQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"adm-zip": "^0.5.2",
|
||||
@@ -26487,14 +26460,20 @@
|
||||
"dependencies": {
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||
"integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
|
||||
"dev": true
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
|
||||
"integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==",
|
||||
"dev": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
@@ -26503,6 +26482,8 @@
|
||||
},
|
||||
"chalk": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
|
||||
"integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^2.2.1",
|
||||
@@ -26514,6 +26495,8 @@
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.2.3",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
@@ -26526,10 +26509,14 @@
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
|
||||
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
|
||||
"dev": true
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
@@ -26537,17 +26524,23 @@
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
|
||||
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"glob": "^7.1.3"
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.7.1",
|
||||
"version": "5.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
|
||||
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
|
||||
"dev": true
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
@@ -26555,6 +26548,8 @@
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
|
||||
"integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,6 +88,7 @@
|
||||
"lodash": "^4.17.21",
|
||||
"moment": "^2.29.4",
|
||||
"ng2-pdf-viewer": "^9.1.3",
|
||||
"parse-duration": "^1.1.0",
|
||||
"rxjs": "^6.6.7",
|
||||
"scandit-sdk": "^5.13.2",
|
||||
"socket.io": "^4.5.4",
|
||||
|
||||
Reference in New Issue
Block a user