Compare commits

...

9 Commits

Author SHA1 Message Date
Lorenz Hilpert
f23d3c5adc Font Size Test 2024-06-11 13:09:08 +02:00
Lorenz Hilpert
c68706b54f Merged PR 1786: #4760 Fehler bei Abholpreisberechnung in Filiale Darmstadt Ernst-Ludwig-Straße
#4760 Fehler bei Abholpreisberechnung in Filiale Darmstadt Ernst-Ludwig-Straße
2024-06-10 12:22:36 +00:00
Lorenz Hilpert
b271ce9711 Merged PR 1785: Angular 17 Update + Cleanup Packages 2024-06-06 13:31:02 +00:00
Nino Righi
94888213b1 Merged PR 1784: #4758 Loading Indicator if Date gets Patched on OrderItemSubset
#4758 Loading Indicator if Date gets Patched on OrderItemSubset
2024-06-05 17:04:41 +00:00
Nino Righi
1041d92486 Merged PR 1783: #4750 Code improvements, Check if Icon gets loaded
#4750 Code improvements, Check if Icon gets loaded
2024-06-04 16:02:53 +00:00
Nino Righi
43d8d220c9 Merged PR 1782: #4750 Check if Icon File exists
#4750 Check if Icon File exists
2024-05-28 16:03:15 +00:00
Nino Righi
e0993d9c46 Merged PR 1781: #4599 Fix Routing After Click on Continue Shopping CTA - On Tablet Size or Sm...
#4599 Fix Routing After Click on Continue Shopping CTA - On Tablet Size or Smaller
2024-05-28 15:57:54 +00:00
Nino Righi
82656d9b27 Merged PR 1780: #4314 Navigation on Product Cover Click
#4314 Navigation on Product Cover Click
2024-05-28 15:57:31 +00:00
Lorenz Hilpert
df36d0934d Merged PR 1779: #4752 Performance in der WA verbessert -> Details Seite
#4752 Performance in der WA verbessert -> Details Seite
2024-05-28 13:48:00 +00:00
72 changed files with 6944 additions and 10188 deletions

1
.husky/pre-commit Normal file
View File

@@ -0,0 +1 @@
npm run pretty-quick

View File

@@ -1,4 +0,0 @@
- Neue Icon Module (z.B. mit SVG sprites)
- Breadcrumb Navigation (Neu)
- Remissions Produkt Liste (Refactoring / Neu)
- Angular Version (Upgrade)

View File

@@ -959,10 +959,10 @@
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"browserTarget": "isa-app:build:production"
"buildTarget": "isa-app:build:production"
},
"development": {
"browserTarget": "isa-app:build:development"
"buildTarget": "isa-app:build:development"
}
},
"defaultConfiguration": "development"
@@ -970,7 +970,7 @@
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "isa-app:build"
"buildTarget": "isa-app:build"
}
},
"test": {
@@ -1470,39 +1470,6 @@
}
}
}
},
"shell": {
"projectType": "library",
"root": "apps/shell",
"sourceRoot": "apps/shell/src",
"prefix": "shell",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:ng-packagr",
"options": {
"project": "apps/shell/ng-package.json"
},
"configurations": {
"production": {
"tsConfig": "apps/shell/tsconfig.lib.prod.json"
},
"development": {
"tsConfig": "apps/shell/tsconfig.lib.json"
}
},
"defaultConfiguration": "production"
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"tsConfig": "apps/shell/tsconfig.spec.json",
"polyfills": [
"zone.js",
"zone.js/testing"
]
}
}
}
}
},
"cli": {

View File

@@ -0,0 +1,31 @@
import { Directive, HostListener, Input } from '@angular/core';
import { ProductCatalogNavigationService } from '@shared/services';
@Directive({
selector: '[productImageNavigation]',
standalone: true,
})
export class NavigateOnClickDirective {
@Input('productImageNavigation') ean: string;
constructor(private readonly _productCatalogNavigation: ProductCatalogNavigationService) {}
@HostListener('click', ['$event'])
async onClick(event: MouseEvent) {
event.preventDefault();
event.stopPropagation();
if (this.ean) {
await this._navigateToProductSearchDetails();
}
}
private async _navigateToProductSearchDetails() {
await this._productCatalogNavigation
.getArticleDetailsPathByEan({
processId: Date.now(),
ean: this.ean,
extras: { queryParams: { main_qs: this.ean } },
})
.navigate();
}
}

View File

@@ -5,4 +5,5 @@
export * from './lib/product-image.service';
export * from './lib/product-image.module';
export * from './lib/product-image.pipe';
export * from './lib/product-image-navigation.directive';
export * from './lib/tokens';

View File

@@ -36,7 +36,7 @@ export class DomainAvailabilityService {
private _logisticanService: LogisticianService,
private _stockService: StockService,
private _supplierService: StoreCheckoutSupplierService,
private _branchService: StoreCheckoutBranchService
private _branchService: StoreCheckoutBranchService,
) {}
@memorize({ ttl: 10000 })
@@ -48,7 +48,7 @@ export class DomainAvailabilityService {
getSuppliers(): Observable<SupplierDTO[]> {
return this._supplierService.StoreCheckoutSupplierGetSuppliers({}).pipe(
map((response) => response.result),
shareReplay(1)
shareReplay(1),
);
}
@@ -56,7 +56,7 @@ export class DomainAvailabilityService {
getTakeAwaySupplier(): Observable<SupplierDTO> {
return this._supplierService.StoreCheckoutSupplierGetSuppliers({}).pipe(
map(({ result }) => result?.find((supplier) => supplier?.supplierNumber === 'F')),
shareReplay(1)
shareReplay(1),
);
}
@@ -64,7 +64,7 @@ export class DomainAvailabilityService {
getBranches(): Observable<BranchDTO[]> {
return this._branchService.StoreCheckoutBranchGetBranches({}).pipe(
map((response) => response.result),
shareReplay(1)
shareReplay(1),
);
}
@@ -73,7 +73,7 @@ export class DomainAvailabilityService {
return this._stockService.StockGetStocksByBranch({ branchId }).pipe(
map((response) => response.result),
map((result) => result?.find((_) => true)),
shareReplay(1)
shareReplay(1),
);
}
@@ -81,7 +81,7 @@ export class DomainAvailabilityService {
getDefaultStock(): Observable<StockDTO> {
return this._stockService.StockCurrentStock().pipe(
map((response) => response.result),
shareReplay(1)
shareReplay(1),
);
}
@@ -105,7 +105,7 @@ export class DomainAvailabilityService {
status: response.result.status,
version: response.result.version,
})),
shareReplay(1)
shareReplay(1),
);
}
@@ -113,7 +113,7 @@ export class DomainAvailabilityService {
getLogisticians(): Observable<LogisticianDTO> {
return this._logisticanService.LogisticianGetLogisticians({}).pipe(
map((response) => response.result?.find((l) => l.logisticianNumber === '2470')),
shareReplay(1)
shareReplay(1),
);
}
@@ -146,7 +146,7 @@ export class DomainAvailabilityService {
});
return availabilities;
}),
shareReplay(1)
shareReplay(1),
);
}
@@ -167,13 +167,13 @@ export class DomainAvailabilityService {
this._stockService.StockInStock({ articleIds: [item.itemId], stockId: s.id }),
this.getTakeAwaySupplier(),
this.getDefaultBranch(),
])
]),
),
map(([response, supplier, defaultBranch]) => {
const price = item?.price;
return this._mapToTakeAwayAvailability({ response, supplier, branchId: branch?.id ?? defaultBranch?.id, quantity, price });
}),
shareReplay(1)
shareReplay(1),
);
}
@@ -196,7 +196,7 @@ export class DomainAvailabilityService {
map(([response, supplier]) => {
return this._mapToTakeAwayAvailability({ response, supplier, branchId: branch.id, quantity, price });
}),
shareReplay(1)
shareReplay(1),
);
}
@@ -218,7 +218,7 @@ export class DomainAvailabilityService {
map(([response, supplier, defaultBranch]) => {
return this._mapToTakeAwayAvailability({ response, supplier, branchId: branchId ?? defaultBranch.id, quantity, price });
}),
shareReplay(1)
shareReplay(1),
);
}
@@ -228,7 +228,7 @@ export class DomainAvailabilityService {
switchMap((s) => this._stockService.StockInStockByEAN({ eans: eansFiltered, stockId: s.id })),
withLatestFrom(this.getTakeAwaySupplier(), this.getDefaultBranch()),
map((response) => response[0].result),
shareReplay(1)
shareReplay(1),
);
}
@@ -254,7 +254,7 @@ export class DomainAvailabilityService {
])
.pipe(
map((r) => this._mapToPickUpAvailability(r.result)?.find((_) => true)),
shareReplay(1)
shareReplay(1),
);
}
@@ -270,7 +270,7 @@ export class DomainAvailabilityService {
]).pipe(
timeout(5000),
map((r) => this._mapToShippingAvailability(r.result)?.find((_) => true)),
shareReplay(1)
shareReplay(1),
);
}
@@ -305,7 +305,7 @@ export class DomainAvailabilityService {
priceMaintained: preferred?.priceMaintained,
};
}),
shareReplay(1)
shareReplay(1),
);
}
@@ -329,12 +329,12 @@ export class DomainAvailabilityService {
this.getPickUpAvailability({ item, quantity, branch: branch ?? defaultBranch }).pipe(
mergeMap((availability) =>
logistician$.pipe(
map((logistician) => ({ ...(availability?.length > 0 ? availability[0] : []), logistician: { id: logistician.id } }))
)
map((logistician) => ({ ...(availability?.length > 0 ? availability[0] : []), logistician: { id: logistician.id } })),
),
),
shareReplay(1)
)
)
shareReplay(1),
),
),
);
}
@@ -367,7 +367,7 @@ export class DomainAvailabilityService {
priceMaintained: preferred?.priceMaintained,
};
}),
shareReplay(1)
shareReplay(1),
);
}
@@ -378,7 +378,7 @@ export class DomainAvailabilityService {
switchMap((stockId) =>
stockId
? this._stockService.StockInStock({ articleIds: items.map((i) => i.id), stockId })
: of({ result: [] } as ResponseArgsOfIEnumerableOfStockInfoDTO)
: of({ result: [] } as ResponseArgsOfIEnumerableOfStockInfoDTO),
),
timeout(20000),
withLatestFrom(this.getTakeAwaySupplier()),
@@ -389,10 +389,10 @@ export class DomainAvailabilityService {
supplier,
quantity: 1,
price: items?.find((i) => i.id === stockInfo.itemId)?.price,
})
}),
);
}),
shareReplay(1)
shareReplay(1),
);
}
@@ -400,7 +400,7 @@ export class DomainAvailabilityService {
getPickUpAvailabilities(payload: AvailabilityRequestDTO[], preferred?: boolean) {
return this._availabilityService.AvailabilityStoreAvailability(payload).pipe(
timeout(20000),
map((response) => (preferred ? this._mapToPickUpAvailability(response.result) : response.result))
map((response) => (preferred ? this._mapToPickUpAvailability(response.result) : response.result)),
);
}
@@ -408,7 +408,7 @@ export class DomainAvailabilityService {
getDeliveryAvailabilities(payload: AvailabilityRequestDTO[]) {
return this.memorizedAvailabilityShippingAvailability(payload).pipe(
timeout(20000),
map((response) => this._mapToShippingAvailability(response.result))
map((response) => this._mapToShippingAvailability(response.result)),
);
}
@@ -416,7 +416,7 @@ export class DomainAvailabilityService {
getDigDeliveryAvailabilities(payload: AvailabilityRequestDTO[]) {
return this.memorizedAvailabilityShippingAvailability(payload).pipe(
timeout(20000),
map((response) => this._mapToShippingAvailability(response.result))
map((response) => this._mapToShippingAvailability(response.result)),
);
}
@@ -427,16 +427,16 @@ export class DomainAvailabilityService {
return this.getPickUpAvailabilities(payload, true).pipe(
timeout(20000),
switchMap((availability) =>
logistician$.pipe(map((logistician) => ({ availability: [...availability], logistician: { id: logistician.id } })))
logistician$.pipe(map((logistician) => ({ availability: [...availability], logistician: { id: logistician.id } }))),
),
shareReplay(1)
shareReplay(1),
);
}
getPriceForAvailability(
purchasingOption: string,
catalogAvailability: CatAvailabilityDTO | AvailabilityDTO,
availability: AvailabilityDTO
availability: AvailabilityDTO,
): PriceDTO {
switch (purchasingOption) {
case 'take-away':
@@ -567,12 +567,12 @@ export class DomainAvailabilityService {
if (!params.branchId) {
branchId$ = this.getDefaultBranch().pipe(
first(),
map((b) => b.id)
map((b) => b.id),
);
}
const stock$ = branchId$.pipe(
mergeMap((branchId) => this._stockService.StockGetStocksByBranch({ branchId }).pipe(map((response) => response.result?.[0])))
mergeMap((branchId) => this._stockService.StockGetStocksByBranch({ branchId }).pipe(map((response) => response.result?.[0]))),
);
return stock$.pipe(
@@ -589,17 +589,17 @@ export class DomainAvailabilityService {
acc[stockInfo.ean] = stockInfo;
return acc;
}, {});
})
)
)
}),
),
),
);
}
getInStock({ itemIds, branchId }: { itemIds: number[]; branchId: number }): Observable<StockInfoDTO[]> {
return this.getStockByBranch(branchId).pipe(
mergeMap((stock) =>
this._stockService.StockInStock({ articleIds: itemIds, stockId: stock.id }).pipe(map((response) => response.result))
)
this._stockService.StockInStock({ articleIds: itemIds, stockId: stock.id }).pipe(map((response) => response.result)),
),
);
}
}

View File

@@ -11,7 +11,7 @@ export class ThumbnailUrlPipe implements PipeTransform, OnDestroy {
private input$ = new BehaviorSubject<{ width?: number; height?: number; ean?: string }>(undefined);
private result: string;
private onDestroy$ = new Subject();
private onDestroy$ = new Subject<void>();
constructor(private domainCatalogThumbnailService: DomainCatalogThumbnailService, private cdr: ChangeDetectorRef) {}

View File

@@ -28,7 +28,7 @@ export const metaReducers: MetaReducer<RootState>[] = !environment.production ?
imports: [
StoreModule.forRoot(rootReducer, { metaReducers }),
EffectsModule.forRoot([]),
StoreDevtoolsModule.instrument({ name: 'ISA Ngrx Application Store' }),
StoreDevtoolsModule.instrument({ name: 'ISA Ngrx Application Store', connectInZone: true }),
],
})
export class AppStoreModule {}

View File

@@ -1,18 +1,17 @@
import { DOCUMENT } from '@angular/common';
import { Component, HostListener, Inject, OnInit, Renderer2 } from '@angular/core';
import { Title } from '@angular/platform-browser';
import { SwUpdate, UpdateAvailableEvent } from '@angular/service-worker';
import { SwUpdate } from '@angular/service-worker';
import { ApplicationService } from '@core/application';
import { Config } from '@core/config';
import { NotificationsHub } from '@hub/notifications';
import packageInfo from 'package';
import { asapScheduler, interval, Observable, Subscription } from 'rxjs';
import { asapScheduler, interval, Subscription } from 'rxjs';
import { UserStateService } from '@swagger/isa';
import { IsaLogProvider } from './providers';
import { EnvironmentService } from '@core/environment';
import { AuthService } from '@core/auth';
import { UiMessageModalComponent, UiModalService } from '@ui/modal';
import { tap } from 'rxjs/operators';
@Component({
selector: 'app-root',
@@ -21,7 +20,6 @@ import { tap } from 'rxjs/operators';
})
export class AppComponent implements OnInit {
private _checkForUpdates: number = this._config.get('checkForUpdates');
updateAvailableObs: Observable<UpdateAvailableEvent>;
get checkForUpdates(): number {
return this._checkForUpdates;

View File

@@ -23,7 +23,7 @@
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
*/
import 'web-animations-js'; // Run `npm install --save web-animations-js`.
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/**
* By default, zone.js will patch all possible macroTask and DomEvents
@@ -57,4 +57,3 @@ import 'zone.js'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/
import 'hammerjs';

View File

@@ -47,7 +47,7 @@ export class MockRemissionService extends RemissionService {
>();
private remissionSubjectIdRef = new Map<number, number>();
private reloadProductsSubject = new Subject();
private reloadProductsSubject = new Subject<void>();
private productSubject = new BehaviorSubject<RemissionProduct[]>(
remissionProducts
);

View File

@@ -141,7 +141,7 @@ export class ArticleDetailsComponent implements OnInit, OnDestroy {
this.store.deliveryB2BAvailability$,
]).pipe(
map((availabilities) => {
return availabilities?.some((availability) => availability?.priceMaintained) ?? false;
return availabilities?.some((availability) => (availability as any)?.priceMaintained) ?? false;
})
);
@@ -439,11 +439,10 @@ export class ArticleDetailsComponent implements OnInit, OnDestroy {
async navigateToResultList() {
const processId = this.applicationService.activatedProcessId;
let crumbs = await this.breadcrumb
.getBreadcrumbsByKeyAndTags$(this.applicationService.activatedProcessId, ['catalog'])
.getBreadcrumbsByKeyAndTags$(this.applicationService.activatedProcessId, ['catalog', 'details'])
.pipe(first())
.toPromise();
crumbs = crumbs.filter((crumb) => !crumb.tags?.includes('details'));
const crumb = crumbs[crumbs.length - 1];
if (!!crumb) {
await this._navigationService.getArticleSearchResultsPath(processId, { queryParams: crumb.params }).navigate();

View File

@@ -24,7 +24,7 @@ import { FilterAutocompleteProvider } from '@shared/components/filter';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ArticleSearchComponent implements OnInit, OnDestroy {
private _onDestroy$ = new Subject();
private _onDestroy$ = new Subject<void>();
private _processId$: Observable<number>;
constructor(

View File

@@ -58,7 +58,7 @@ export class ArticleSearchFilterComponent implements OnInit, OnDestroy {
}
}
private _onDestroy$ = new Subject();
private _onDestroy$ = new Subject<void>();
constructor(
private articleSearch: ArticleSearchService,

View File

@@ -33,7 +33,7 @@ export class PageCatalogComponent implements OnInit, AfterViewInit, OnDestroy {
return `${this.breadcrumbRef?.nativeElement?.clientWidth}px`;
}
_onDestroy$ = new Subject<boolean>();
_onDestroy$ = new Subject<void>();
get isTablet$() {
return this._environmentService.matchTablet$;

View File

@@ -33,7 +33,7 @@ export class CheckoutSummaryComponent implements OnInit, OnDestroy {
private _toaster = inject(ToasterService);
private _onDestroy$ = new Subject();
private _onDestroy$ = new Subject<void>();
processId = Date.now();
selectedDate = this.dateAdapter.today();
minDateDatepicker = this.dateAdapter.addCalendarDays(this.dateAdapter.today(), -1);

View File

@@ -141,7 +141,7 @@ export class CustomerOrderDetailsItemComponent extends ComponentStore<CustomerOr
more$ = this.select((s) => s.more);
private _onDestroy$ = new Subject();
private _onDestroy$ = new Subject<void>();
get isNative() {
return this._environment.isNative();

View File

@@ -22,7 +22,7 @@ export class CustomerOrderSearchFilterComponent implements OnInit, OnDestroy {
processId = Number(this._activatedRoute?.parent?.snapshot?.data?.processId);
private _onDestroy$ = new Subject();
private _onDestroy$ = new Subject<void>();
@ViewChild(FilterComponent, { static: false })
uiFilter: FilterComponent;

View File

@@ -23,7 +23,7 @@ import { CustomerOrdersNavigationService } from '@shared/services';
],
})
export class CustomerOrderSearchComponent implements OnInit, OnDestroy {
private _onDestroy$ = new Subject();
private _onDestroy$ = new Subject<void>();
private _processId$: Observable<number>;
get isTablet() {

View File

@@ -201,8 +201,8 @@ export class CustomerOrderSearchStore extends ComponentStore<CustomerOrderSearch
search = this.effect((options$: Observable<{ clear?: boolean; siletReload?: boolean }>) =>
options$.pipe(
tap((_) => {
this.searchStarted.next();
tap((opt) => {
this.searchStarted.next(opt);
this.patchState({ message: undefined });
}),
withLatestFrom(this.results$, this.filter$, this.selectedBranch$),

View File

@@ -99,7 +99,7 @@ export class CustomerOrderSearchResultsComponent extends ComponentStore<Customer
processId$ = this._activatedRoute.parent.data.pipe(map((data) => +data.processId));
private _onDestroy$ = new Subject();
private _onDestroy$ = new Subject<void>();
trackByFn: TrackByFunction<OrderItemListItemDTO> = (index, item) => `${item.orderId}${item.orderItemId}${item.orderItemSubsetId}`;

View File

@@ -30,7 +30,7 @@ export class CustomerOrderComponent implements OnInit {
return `${this.breadcrumbRef?.nativeElement?.clientWidth}px`;
}
_onDestroy$ = new Subject<boolean>();
_onDestroy$ = new Subject<void>();
get isTablet$() {
return this._environmentService.matchTablet$;

View File

@@ -197,7 +197,7 @@ export class CustomerTypeSelectorComponent extends ComponentStore<CustomerTypeSe
setValue(value: { p4mUser?: boolean; customerType?: string } | string) {
const initial = { p4mUser: this.p4mUser, customerType: this.customerType };
if (isString(value)) {
if (typeof value === 'string') {
this.value = value;
} else {
if (isBoolean(value.p4mUser)) {

View File

@@ -72,7 +72,7 @@ export class CreateP4MCustomerComponent extends AbstractCreateCustomer implement
birthDateValidatorFns = [];
existingCustomer$: Observable<CustomerInfoDTO | null>;
existingCustomer$: Observable<CustomerInfoDTO | CustomerDTO | null>;
ngOnInit(): void {
super.ngOnInit();

View File

@@ -29,7 +29,7 @@ export interface CustomerDetailsViewMainState {
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CustomerDetailsViewMainComponent extends ComponentStore<CustomerDetailsViewMainState> implements OnInit, OnDestroy {
private _onDestroy$ = new Subject();
private _onDestroy$ = new Subject<void>();
customerService = inject(CrmCustomerService);

View File

@@ -42,6 +42,7 @@
<img
class="thumbnail"
loading="lazy"
[productImageNavigation]="orderItem?.product?.ean"
*ngIf="orderItem?.product?.ean | productImage; let productImage"
[src]="productImage"
[alt]="orderItem?.product?.name"

View File

@@ -123,7 +123,7 @@ export class GoodsInListItemComponent extends ComponentStore<GoodsInListItemComp
shareReplay()
);
private _onDestroy$ = new Subject();
private _onDestroy$ = new Subject<void>();
constructor(
private _omsService: DomainOmsService,

View File

@@ -1,6 +1,6 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ProductImageModule } from 'apps/cdn/product-image/src/public-api';
import { NavigateOnClickDirective, ProductImageModule } from 'apps/cdn/product-image/src/public-api';
import { UiIconModule } from '@ui/icon';
import { UiInputModule } from '@ui/input';
import { FormsModule } from '@angular/forms';
@@ -8,7 +8,7 @@ import { GoodsInListItemComponent } from './goods-in-list-item.component';
import { PipesModule } from 'apps/shared/components/goods-in-out/src/lib/pipes/pipes.module';
@NgModule({
imports: [CommonModule, PipesModule, UiIconModule, ProductImageModule, FormsModule, UiInputModule],
imports: [CommonModule, PipesModule, UiIconModule, ProductImageModule, FormsModule, UiInputModule, NavigateOnClickDirective],
exports: [GoodsInListItemComponent],
declarations: [GoodsInListItemComponent],
providers: [],

View File

@@ -59,7 +59,7 @@ export class GoodsInListComponent implements OnInit, AfterViewInit, OnDestroy {
showSaveSscSpinner$ = new BehaviorSubject<boolean>(false);
private _onDestroy$ = new Subject();
private _onDestroy$ = new Subject<void>();
private readonly SCROLL_POSITION_TOKEN = 'GOODS_IN_LIST_SCROLL_POSITION';

View File

@@ -59,7 +59,7 @@ export class GoodsOutDetailsComponent extends ComponentStore<GoodsOutDetailsComp
processId$ = this._activatedRoute.parent.data.pipe(map((params) => +params.processId));
private _onDestroy$ = new Subject();
private _onDestroy$ = new Subject<void>();
constructor(
private _activatedRoute: ActivatedRoute,

View File

@@ -37,7 +37,7 @@ export class GoodsOutSearchFilterComponent implements OnInit, OnDestroy {
@Input()
processId: number;
private _onDestroy$ = new Subject();
private _onDestroy$ = new Subject<void>();
@ViewChild(UiFilterComponent, { static: false })
uiFilter: UiFilterComponent;

View File

@@ -30,7 +30,7 @@ import { GoodsOutSearchMainAutocompleteProvider } from './providers/goods-out-se
],
})
export class GoodsOutSearchComponent implements OnInit, OnDestroy {
private _onDestroy$ = new Subject();
private _onDestroy$ = new Subject<void>();
hasFilter$ = combineLatest([this._goodsOutSearchStore.filter$, this._goodsOutSearchStore.defaultSettings$]).pipe(
map(([filter, defaultFilter]) => !isEqual(filter?.getQueryParams(), UiFilter.create(defaultFilter).getQueryParams()))

View File

@@ -70,7 +70,7 @@ export class GoodsOutSearchResultsComponent extends ComponentStore<GoodsOutSearc
processId$ = this._activatedRoute.parent.data.pipe(map((data) => +data.processId));
private _onDestroy$ = new Subject();
private _onDestroy$ = new Subject<void>();
trackByFn: TrackByFunction<OrderItemListItemDTO> = (index, item) => `${item.orderId}${item.orderItemId}${item.orderItemSubsetId}`;

View File

@@ -220,6 +220,7 @@ export class PickupShelfInDetailsComponent extends PickupShelfDetailsBaseCompone
}
updateDate({ date, type }: { date: Date; type?: 'delivery' | 'pickup' | 'preferred' }) {
this.store.updateOrderItemSubsetLoading(true);
switch (type) {
case 'delivery':
this.store.selectedOrderItems.forEach((item) =>

View File

@@ -46,7 +46,9 @@ export class PickupShelfOutDetailsComponent extends PickupShelfDetailsBaseCompon
order$ = this.store.order$;
groupedItems$: Observable<Array<{ type: string; items: DBHOrderItemListItemDTO[] }>> = this.store.orderItems$.pipe(
orderItems$ = this.store.orderItems$;
groupedItems$: Observable<Array<{ type: string; items: DBHOrderItemListItemDTO[] }>> = this.orderItems$.pipe(
map((items) => {
const groups: Array<{ type: string; items: DBHOrderItemListItemDTO[] }> = [];
@@ -66,10 +68,12 @@ export class PickupShelfOutDetailsComponent extends PickupShelfDetailsBaseCompon
})
);
fetching$ = this.store.fetchingOrder$;
fetchingOrder$ = this.store.fetchingOrder$;
fetchingItems$ = this.store.fetchingOrderItems$;
viewFetching$ = combineLatest([this.fetching$, this.fetchingItems$]).pipe(map(([fetching, fetchingItems]) => fetching || fetchingItems));
viewFetching$ = combineLatest([this.orderItems$, this.fetchingItems$]).pipe(
map(([orderItems, fetchingItems]) => orderItems?.length === 0 && fetchingItems)
);
selectedCompartmentInfo = this.store.selectedCompartmentInfo;
@@ -148,6 +152,7 @@ export class PickupShelfOutDetailsComponent extends PickupShelfDetailsBaseCompon
}
updateDate({ date, type }: { date: Date; type?: 'delivery' | 'pickup' | 'preferred' }) {
this.store.updateOrderItemSubsetLoading(true);
switch (type) {
case 'delivery': // vsl. Lieferdatum
this.store.orderItems.forEach((item) =>

View File

@@ -7,7 +7,7 @@ import { PickupShelfIOService, PickupShelfOutService } from '@domain/pickup-shel
import { GetNameForBreadcrumbData, GetPathForBreadcrumbData, PickupShelfBaseComponent } from '../pickup-shelf-base.component';
import { NavigationRoute, PickUpShelfOutNavigationService } from '@shared/services';
import { AsyncPipe } from '@angular/common';
import { DBHOrderItemListItemDTO, ListResponseArgsOfDBHOrderItemListItemDTO } from '@swagger/oms';
import { DBHOrderItemListItemDTO } from '@swagger/oms';
import { Observable, of } from 'rxjs';
import { take } from 'rxjs/operators';
import { provideActionHandlers } from '@core/command';

View File

@@ -20,10 +20,6 @@
</button>
</div>
<ng-template #featureLoading>
<shared-skeleton-loader class="w-64 h-6"></shared-skeleton-loader>
</ng-template>
<div class="page-pickup-shelf-details-header__details bg-white px-4 pt-4 pb-5">
<div class="flex flex-row items-center" [class.mb-8]="!orderItem?.features?.paid && !isKulturpass">
<page-pickup-shelf-details-header-nav-menu class="mr-2" [customer]="customer$ | async"></page-pickup-shelf-details-header-nav-menu>
@@ -105,7 +101,7 @@
<div class="flex flex-row font-bold">
<shared-skeleton-loader class="w-32" *ngIf="fetchingOrder$ | async; else orderSourceTmpl"></shared-skeleton-loader>
<ng-template #orderSourceTmpl>
{{ order?.features?.orderSource }}
{{ order()?.features?.orderSource }}
</ng-template>
</div>
</div>
@@ -162,7 +158,7 @@
<ng-template #abholfrist>
<div class="min-w-[9rem]">Abholfrist</div>
<div *ngIf="!(changeDateLoader$ | async)" class="flex flex-row font-bold">
<div *ngIf="!(orderItemSubsetLoading$ | async); else featureLoading" class="flex flex-row font-bold">
<button
[uiOverlayTrigger]="deadlineDatepicker"
#deadlineDatepickerTrigger="uiOverlayTrigger"
@@ -186,12 +182,11 @@
>
</ui-datepicker>
</div>
<ui-spinner *ngIf="changeDateLoader$ | async; let loader" class="flex flex-row font-bold loader" [show]="loader"></ui-spinner>
</ng-template>
<ng-template #preferredPickUpDate>
<div class="min-w-[9rem]">Zurücklegen bis</div>
<div *ngIf="!(changePreferredDateLoader$ | async)" class="flex flex-row font-bold">
<div *ngIf="!(orderItemSubsetLoading$ | async); else featureLoading" class="flex flex-row font-bold">
<button
[uiOverlayTrigger]="preferredPickUpDatePicker"
#preferredPickUpDatePickerTrigger="uiOverlayTrigger"
@@ -218,12 +213,11 @@
>
</ui-datepicker>
</div>
<ui-spinner *ngIf="changePreferredDateLoader$ | async; let loader" class="flex flex-row font-bold loader" [show]="loader"> </ui-spinner>
</ng-template>
<ng-template #vslLieferdatum>
<div class="min-w-[9rem]">vsl. Lieferdatum</div>
<div *ngIf="!(changeDateLoader$ | async)" class="flex flex-row font-bold">
<div *ngIf="!(orderItemSubsetLoading$ | async); else featureLoading" class="flex flex-row font-bold">
<button
class="cta-datepicker"
[disabled]="changeDateDisabled$ | async"
@@ -247,6 +241,9 @@
>
</ui-datepicker>
</div>
<ui-spinner *ngIf="changeDateLoader$ | async; let loader" class="flex flex-row font-bold loader" [show]="loader"></ui-spinner>
</ng-template>
</ng-container>
<ng-template #featureLoading>
<shared-skeleton-loader class="w-64 h-6"></shared-skeleton-loader>
</ng-template>

View File

@@ -13,6 +13,7 @@ import { PickupShelfProcessingStatusPipe } from '../pipes/processing-status.pipe
import { UiDatepickerModule } from '@ui/datepicker';
import { PickUpShelfDetailsHeaderNavMenuComponent } from '../pickup-shelf-details-header-nav-menu/pickup-shelf-details-header-nav-menu.component';
import { SkeletonLoaderComponent } from '@shared/components/loader';
import { toSignal } from '@angular/core/rxjs-interop';
@Component({
selector: 'page-pickup-shelf-details-header',
@@ -52,15 +53,13 @@ export class PickUpShelfDetailsHeaderComponent {
@Output()
updateDate = new EventEmitter<{ date: Date; type?: 'delivery' | 'pickup' | 'preferred' }>();
orderItemSubsetLoading$ = this._store.orderItemSubsetLoading$;
fetchingOrder$ = this._store.fetchingOrder$;
get order$(): Observable<OrderDTO> {
return this._store.order$;
}
order$ = this._store.order$;
get order(): OrderDTO {
return this._store.order;
}
order = toSignal(this.order$);
findLatestPreferredPickUpDate$: Observable<Date> = this.order$.pipe(
withLatestFrom(this._store.selectedOrderItemIds$),
@@ -90,7 +89,7 @@ export class PickUpShelfDetailsHeaderComponent {
processId?: number;
get isKulturpass() {
return this.order?.features?.orderSource === 'KulturPass';
return this.order()?.features?.orderSource === 'KulturPass';
}
minDateDatepicker = this.dateAdapter.addCalendarDays(this.dateAdapter.today(), -1);

View File

@@ -32,7 +32,11 @@
</div>
<div class="page-pickup-shelf-details-item__item-container">
<div class="page-pickup-shelf-details-item__thumbnail">
<img [src]="orderItem.product?.ean | productImage" [alt]="orderItem.product?.name" />
<img
[productImageNavigation]="orderItem?.product?.ean"
[src]="orderItem.product?.ean | productImage"
[alt]="orderItem.product?.name"
/>
</div>
<div class="page-pickup-shelf-details-item__details">
<div class="flex flex-row justify-between items-start mb-[1.3125rem]">

View File

@@ -12,7 +12,7 @@ import {
inject,
} from '@angular/core';
import { FormsModule, ReactiveFormsModule, UntypedFormControl } from '@angular/forms';
import { ProductImageModule } from '@cdn/product-image';
import { NavigateOnClickDirective, ProductImageModule } from '@cdn/product-image';
import { DBHOrderItemListItemDTO, OrderDTO, ReceiptDTO } from '@swagger/oms';
import { UiCommonModule } from '@ui/common';
import { UiTooltipModule } from '@ui/tooltip';
@@ -57,6 +57,7 @@ export interface PickUpShelfDetailsItemComponentState {
IconModule,
UiQuantityDropdownModule,
NotificationTypePipe,
NavigateOnClickDirective,
],
})
export class PickUpShelfDetailsItemComponent extends ComponentStore<PickUpShelfDetailsItemComponentState> implements OnInit {
@@ -174,7 +175,7 @@ export class PickUpShelfDetailsItemComponent extends ComponentStore<PickUpShelfD
more$ = this.select((s) => s.more);
private _onDestroy$ = new Subject();
private _onDestroy$ = new Subject<void>();
expanded: boolean = false;

View File

@@ -15,6 +15,7 @@
class="page-pickup-shelf-list-item__item-image w-[3.125rem] max-h-[4.9375rem]"
loading="lazy"
*ngIf="item?.product?.ean | productImage; let productImage"
[productImageNavigation]="item?.product?.ean"
[src]="productImage"
[alt]="item?.product?.name"
/>

View File

@@ -1,7 +1,7 @@
import { CurrencyPipe, DatePipe, NgFor, NgIf } from '@angular/common';
import { ChangeDetectionStrategy, Component, ElementRef, Input, inject } from '@angular/core';
import { RouterLink, RouterLinkActive } from '@angular/router';
import { ProductImageModule } from '@cdn/product-image';
import { NavigateOnClickDirective, ProductImageModule } from '@cdn/product-image';
import { EnvironmentService } from '@core/environment';
import { IconModule } from '@shared/components/icon';
import { DBHOrderItemListItemDTO } from '@swagger/oms';
@@ -31,6 +31,7 @@ import { CacheService } from '@core/cache';
ProductImageModule,
UiCommonModule,
PickupShelfProcessingStatusPipe,
NavigateOnClickDirective,
],
providers: [PickupShelfProcessingStatusPipe],
})

View File

@@ -89,6 +89,8 @@ export const selectSelectedOrderItemIds = (s: PickupShelfDetailsState) => s.sele
export const selectPreviousSelectedOrderItemSubsetId = (s: PickupShelfDetailsState) => s.previousSelectedOrderItemSubsetId;
export const selectOrderItemSubsetLoading = (s: PickupShelfDetailsState) => s.orderItemSubsetLoading;
export const selectDisableHeaderStatusDropdown = (s: PickupShelfDetailsState) => s.disableHeaderStatusDropdown;
export const selectedOrderItems = (s: PickupShelfDetailsState) => {

View File

@@ -34,6 +34,7 @@ export interface PickupShelfDetailsState {
fetchingOrderItemSubsetTasks: { [orderItemSubsetId: number]: boolean };
orderItemSubsetTasks: { [orderItemSubsetId: number]: OrderItemSubsetTaskListItemDTO[] };
orderItemSubsetLoading?: boolean;
coverOrderItems?: DBHOrderItemListItemDTO[];
fetchingCoverOrderItems?: boolean;

View File

@@ -1,6 +1,6 @@
import { ComponentStore, tapResponse } from '@ngrx/component-store';
import { PickupShelfDetailsState } from './pickup-shelf-details.state';
import { Observable } from 'rxjs';
import { Observable, combineLatest } from 'rxjs';
import {
DBHOrderItemListItemDTO,
EntityDTOContainerOfOrderItemSubsetDTO,
@@ -18,7 +18,7 @@ import {
} from '@swagger/oms';
import { PickupShelfIOService, PickupShelfService } from '@domain/pickup-shelf';
import { Injectable, inject } from '@angular/core';
import { delayWhen, filter, mergeMap, switchMap, take, tap, withLatestFrom } from 'rxjs/operators';
import { delayWhen, filter, map, mergeMap, switchMap, take, tap, withLatestFrom } from 'rxjs/operators';
import { UiModalService } from '@ui/modal';
import { CrmCustomerService } from '@domain/crm';
import * as Selectors from './pickup-shelf-details.selectors';
@@ -56,10 +56,20 @@ export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsSt
return this.get(Selectors.selectFetchingOrder);
}
orderItems$ = this.select(Selectors.selectOrderItems);
// orderItems$ = this.select(Selectors.selectOrderItems);
// get orderItems() {
// return this.get(Selectors.selectOrderItems);
// }
orderItems$ = combineLatest([
this.select(Selectors.selectOrderItems),
this.select((s) => s).pipe(switchMap((s) => this._listStore.itemsForPreview$(s))),
]).pipe(map(([orderItems, itemsForPreview]) => (orderItems?.length ? orderItems : itemsForPreview)));
get orderItems() {
return this.get(Selectors.selectOrderItems);
const orderItems = this.get(Selectors.selectOrderItems);
return orderItems?.length ? orderItems : this.get((s) => this._listStore.itemsForPreview(s));
}
fetchingOrderItems$ = this.select(Selectors.selectFetchingOrderItems);
@@ -235,6 +245,12 @@ export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsSt
return this.get(Selectors.selectCoverOrderItems);
}
orderItemSubsetLoading$ = this.select(Selectors.selectOrderItemSubsetLoading);
get orderItemSubsetLoading() {
return this.get(Selectors.selectOrderItemSubsetLoading);
}
// Wird benöitgt um das Status Dropdown in der Details Header Komponente zu disablen wenn eine Action gehandled wird
disableHeaderStatusDropdown$ = this.select(Selectors.selectDisableHeaderStatusDropdown);
@@ -569,6 +585,8 @@ export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsSt
if (res.result.preferredPickUpDate) {
this.patchPreferredPickUpDateOnOrderSubsetItemInState({ item, newPreferredPickUpDate: res.result.preferredPickUpDate });
}
this.updateOrderItemSubsetLoading(false);
};
private patchOrderItemSubsetError = (err: any) => {
@@ -713,6 +731,10 @@ export class PickupShelfDetailsStore extends ComponentStore<PickupShelfDetailsSt
this.patchState({ fetchingCoverOrderItems: true });
};
updateOrderItemSubsetLoading(orderItemSubsetLoading: boolean) {
this.patchState({ orderItemSubsetLoading });
}
private fetchCoverOrderItemsDone = (res: ListResponseArgsOfDBHOrderItemListItemDTO) => {
this.patchState({ fetchingCoverOrderItems: false, coverOrderItems: res.result });
};

View File

@@ -1,6 +1,13 @@
import { Filter } from '@shared/components/filter';
import { PickupShelfState } from './pickup-shelf.state';
import { isEmpty } from 'lodash';
import { PickupShelfDetailsState } from './pickup-shelf-details.state';
import {
selectDisplayedCompartmentCode,
selectDisplayedCompartmentInfo,
selectDisplayedOrderItemProcessingStatus,
selectOrder,
} from './pickup-shelf-details.selectors';
export function selectProcessId(state: PickupShelfState) {
return state.processId;
@@ -64,3 +71,27 @@ export function selectListHits(state: PickupShelfState) {
export function selectSelectedListItems(state: PickupShelfState) {
return state.selectedListItems ?? [];
}
export const selectItemsForPreview = (detailsState: PickupShelfDetailsState) => (state: PickupShelfState) => {
const orderId = selectOrder(detailsState)?.id;
let items = state.list.filter((item) => item.orderId === orderId);
const processingStatus = selectDisplayedOrderItemProcessingStatus(detailsState);
if (processingStatus) {
items = items?.filter((oi) => oi.processingStatus === processingStatus);
}
const compartmentCode = selectDisplayedCompartmentCode(detailsState);
if (compartmentCode) {
items = items?.filter((oi) => oi.compartmentCode === compartmentCode);
const compartmentInfo = selectDisplayedCompartmentInfo(detailsState);
if (compartmentInfo) {
items = items?.filter((oi) => oi.compartmentInfo === compartmentInfo);
}
}
return items;
};

View File

@@ -28,6 +28,7 @@ import * as Selectors from './pickup-shelf.selectors';
import { Observable, Subject, combineLatest } from 'rxjs';
import { CacheService } from '@core/cache';
import { Filter } from '@shared/components/filter';
import { PickupShelfDetailsState } from './pickup-shelf-details.state';
@Injectable()
export class PickupShelfStore extends ComponentStore<PickupShelfState> implements OnStoreInit {
@@ -104,6 +105,10 @@ export class PickupShelfStore extends ComponentStore<PickupShelfState> implement
return this.get(Selectors.selectSelectedListItems);
}
itemsForPreview$ = (state: PickupShelfDetailsState) => this.select(Selectors.selectItemsForPreview(state));
itemsForPreview = (state: PickupShelfDetailsState) => this.get(Selectors.selectItemsForPreview(state));
constructor() {
// Nicht entfernen sonst wird der Store nicht initialisiert
super({});

View File

@@ -16,7 +16,7 @@ import { AddProductModalData } from './add-product-modal.data';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AddProductModalComponent implements OnInit, OnDestroy {
private _onDestroy$ = new Subject();
private _onDestroy$ = new Subject<void>();
get processId() {
return this._config.get('process.ids.remission');

View File

@@ -16,7 +16,7 @@ export class RemissionFilterComponent implements OnDestroy {
filter$ = this.store.filter$.pipe(map((filter) => UiFilter.create(filter)));
private _onDestroy$ = new Subject();
private _onDestroy$ = new Subject<void>();
constructor(public store: RemissionListComponentStore) {}

View File

@@ -21,16 +21,24 @@
</div>
<div class="product-data-grid grid gap-6 items-start">
<div>
<img class="max-w-full max-h-full shadow rounded" src="{{ item.ean | productImage }}" alt="item.dto.product.name" />
<img
[productImageNavigation]="item?.ean"
class="max-w-full max-h-full shadow rounded"
src="{{ item.ean | productImage }}"
alt="item.dto.product.name"
/>
</div>
<div class="grid grid-flow-row gap-1 w-48">
<div
class="overflow-hidden overflow-ellipsis whitespace-nowrap"
*ngIf="!!item.format && !!item.formatDetail && item.format !== 'UNKNOWN'"
>
<img class="inline" src="/assets/images/Icon_{{ item.dto.product.format }}.svg" [alt]="item.formatDetail" />
<span class="ml-1 font-bold format-detail">{{ item.formatDetail }}</span>
<div class="overflow-hidden overflow-ellipsis whitespace-nowrap" *ngIf="formatAvailable">
<img
*ngIf="formatIconAvailable"
class="inline mr-1"
[src]="formatIconUrl"
[alt]="item.formatDetail"
(error)="formatIconAvailable = false"
/>
<span class="font-bold format-detail">{{ item.formatDetail }}</span>
</div>
<div class="font-bold ean">
{{ item.ean }}

View File

@@ -10,7 +10,7 @@ import {
} from '@swagger/remi';
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
import { mapFromReturnItemDTO, mapFromReturnSuggestionDTO } from 'apps/domain/remission/src/lib/mappings';
import { BehaviorSubject, Subject } from 'rxjs';
import { Subject } from 'rxjs';
import { first, takeUntil } from 'rxjs/operators';
import { AddProductToShippingDocumentModalComponent } from '../../modals/add-product-to-shipping-document-modal/add-product-to-shipping-document-modal.component';
import { RemissionListComponentStore } from '../remission-list.component-store';
@@ -23,7 +23,7 @@ import { RemissionListComponent } from '../remission-list.component';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class RemissionListItemComponent implements OnDestroy {
private _onDestroy$ = new Subject();
private _onDestroy$ = new Subject<void>();
@Input()
item: RemissionListItem;
@@ -55,6 +55,16 @@ export class RemissionListItemComponent implements OnDestroy {
loading$ = this._listComponent.remittingItem$.asObservable();
get formatIconUrl() {
return `/assets/images/Icon_${this.item.dto.product.format}.svg`;
}
get formatAvailable() {
return !!this.item.format && !!this.item.formatDetail && this.item.format !== 'UNKNOWN';
}
formatIconAvailable: boolean = true;
constructor(
private _modal: UiModalService,
private _remissionService: DomainRemissionService,

View File

@@ -2,7 +2,7 @@ import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RemissionListItemComponent } from './remission-list-item.component';
import { ProductImageModule } from '@cdn/product-image';
import { NavigateOnClickDirective, ProductImageModule } from '@cdn/product-image';
import { RemissionPipeModule } from '../../pipes';
import { UiTooltipModule } from '@ui/tooltip';
import { UiCommonModule } from '@ui/common';
@@ -20,6 +20,7 @@ import { SharedProductGroupPipe } from '@shared/pipes/product-group';
UiTooltipModule,
AddProductToShippingDocumentModalModule,
SharedProductGroupPipe,
NavigateOnClickDirective,
],
exports: [RemissionListItemComponent],
declarations: [RemissionListItemComponent],

View File

@@ -37,7 +37,7 @@ export class RemissionListComponent implements OnInit, OnDestroy {
@ViewChild('scrollContainer', { static: true })
scrollContainer: CdkVirtualScrollViewport;
private _onDestroy$ = new Subject();
private _onDestroy$ = new Subject<void>();
get processId() {
return this._config.get('process.ids.remission');

View File

@@ -16,7 +16,7 @@ import { ToasterService } from '@shared/shell';
providers: [RemissionListComponentStore],
})
export class RemissionComponent implements OnInit, OnDestroy {
private _onDestroy$ = new Subject();
private _onDestroy$ = new Subject<void>();
get processId() {
return this._config.get('process.ids.remission');

View File

@@ -111,7 +111,7 @@ export class TaskInfoComponent implements OnChanges {
indicatorColor$ = this.info$.pipe(map((info) => this.domainTaskCalendarService.getProcessingStatusColorCode(info)));
private noteAdded$ = new Subject();
private noteAdded$ = new Subject<void>();
notes$ = combineLatest([this.info$, this.noteAdded$.pipe(startWith([undefined]))]).pipe(
switchMap(([info]) => this.domainTaskCalendarService.getComments({ infoId: info.id })),

View File

@@ -17,7 +17,7 @@ import { TaskCalendarStore } from '../../task-calendar.store';
exportAs: 'taskSearchbar',
})
export class TaskSearchbarComponent implements OnInit, OnDestroy {
private _onDestroy$ = new Subject();
private _onDestroy$ = new Subject<void>();
control = new UntypedFormControl('', [Validators.minLength(3)]);

View File

@@ -15,7 +15,7 @@ import { TaskCalendarStore } from '../../task-calendar.store';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class TaskCalendarFilterComponent implements OnInit, OnDestroy {
private _onDestroy$ = new Subject();
private _onDestroy$ = new Subject<void>();
@Output() exitFilter = new EventEmitter<void>();
filter$ = new BehaviorSubject<UiFilter>(undefined);

View File

@@ -16,7 +16,7 @@ import { ApplicationService } from '@core/application';
providers: [TaskCalendarStore],
})
export class PageTaskCalendarComponent implements OnInit, OnDestroy {
private _onDestroy$ = new Subject();
private _onDestroy$ = new Subject<void>();
@ViewChild('searchInput', { static: true })
searchInput: ElementRef;

View File

@@ -18,7 +18,7 @@ import { TaskCalendarStore } from '../../task-calendar.store';
providers: [DatePipe],
})
export class TaskSearchComponent implements OnInit, OnDestroy, AfterViewInit {
private _onDestroy$ = new Subject();
private _onDestroy$ = new Subject<void>();
private _taskCalendarKey = this._config.get('process.ids.taskCalendar');
today = this.dateAdapter.today();

View File

@@ -95,7 +95,7 @@ export class BranchSelectorComponent implements OnInit, OnDestroy, AfterViewInit
}
private _value: BranchDTO;
private _onDestroy$ = new Subject<boolean>();
private _onDestroy$ = new Subject<void>();
@Output() valueChange = new EventEmitter<BranchDTO>();

View File

@@ -144,7 +144,7 @@ export class SharedGoodsInOutOrderDetailsItemComponent extends ComponentStore<Sh
showMore$ = this.more$.pipe(map((more) => more || this._auth.hasRole('CallCenter')));
fetchHistory$ = new BehaviorSubject<boolean>(false);
private _onDestroy$ = new Subject();
private _onDestroy$ = new Subject<void>();
constructor(
private _host: SharedGoodsInOutOrderDetailsStore,

View File

@@ -16,7 +16,13 @@
</div>
<div class="goods-in-out-order-group-item-details">
<div class="goods-in-out-order-group-item-details-thumbnail">
<img loading="lazy" *ngIf="item?.product?.ean | productImage; let productImage" [src]="productImage" [alt]="item?.product?.name" />
<img
[productImageNavigation]="item?.product?.ean"
loading="lazy"
*ngIf="item?.product?.ean | productImage; let productImage"
[src]="productImage"
[alt]="item?.product?.name"
/>
</div>
<div data-which="item-details-data" class="goods-in-out-order-group-item-details-data">
<div

View File

@@ -4,7 +4,7 @@ import { CommonModule } from '@angular/common';
import { GoodsInOutOrderGroupComponent } from './goods-in-out-order-group.component';
import { GoodsInOutOrderGroupItemComponent } from './goods-in-out-order-group-item.component';
import { UiIconModule } from '@ui/icon';
import { ProductImageModule } from '@cdn/product-image';
import { NavigateOnClickDirective, ProductImageModule } from '@cdn/product-image';
import { PipesModule } from '../pipes/pipes.module';
import { UiSelectBulletModule } from '@ui/select-bullet';
import { FormsModule } from '@angular/forms';
@@ -23,6 +23,7 @@ import { SharedProductGroupPipe } from '@shared/pipes/product-group';
UiQuantityDropdownModule,
UiSpinnerModule,
SharedProductGroupPipe,
NavigateOnClickDirective,
],
exports: [GoodsInOutOrderGroupComponent, GoodsInOutOrderGroupItemComponent],
declarations: [GoodsInOutOrderGroupComponent, GoodsInOutOrderGroupItemComponent],

View File

@@ -87,7 +87,7 @@ export function getCanAddResults(state: PurchaseOptionsState): CanAdd[] {
export function getCanAddForItemWithPurchaseOption(
itemId: number,
purchaseOption: PurchaseOption
purchaseOption: PurchaseOption,
): (state: PurchaseOptionsState) => CanAdd {
return (state: PurchaseOptionsState) => {
const canAddResults = getCanAddResults(state);
@@ -157,7 +157,7 @@ export function getItemsThatHaveAnAvailabilityAndCanAddForPurchaseOption(purchas
const canAddResults = getCanAddResults(state);
return items.filter((item) =>
canAddResults.some((canAdd) => canAdd.itemId === item.id && canAdd.purchaseOption === purchaseOption && canAdd.canAdd)
canAddResults.some((canAdd) => canAdd.itemId === item.id && canAdd.purchaseOption === purchaseOption && canAdd.canAdd),
);
};
}
@@ -189,7 +189,7 @@ export function getItemsForList(state: PurchaseOptionsState): Item[] {
export function getAvailabilitiesForItem(
itemId: number,
allDeliveryAvailabilities?: boolean
allDeliveryAvailabilities?: boolean,
): (state: PurchaseOptionsState) => Availability[] {
return (state) => {
let availabilities = getAvailabilities(state);
@@ -198,7 +198,7 @@ export function getAvailabilitiesForItem(
// if 'delivery', 'dig-delivery' and 'b2b-delivery' are present remove 'dig-delivery' and 'b2b-delivery'
if (!allDeliveryAvailabilities && availabilities.some((availability) => availability.purchaseOption === 'delivery')) {
availabilities = availabilities.filter(
(availability) => availability.purchaseOption !== 'dig-delivery' && availability.purchaseOption !== 'b2b-delivery'
(availability) => availability.purchaseOption !== 'dig-delivery' && availability.purchaseOption !== 'b2b-delivery',
);
}
const optionsOrder = PURCHASE_OPTIONS;
@@ -247,7 +247,7 @@ export function getIsGiftCard(itemId: number): (state: PurchaseOptionsState) =>
export function getAvailabilityPriceForPurchaseOption(
itemId: number,
purchaseOption: PurchaseOption
purchaseOption: PurchaseOption,
): (state: PurchaseOptionsState) => (PriceDTO & { fromCatalogue?: boolean }) | undefined {
return (state) => {
const item = getItems(state).find((item) => item.id === itemId);
@@ -316,6 +316,20 @@ export function getAvailabilityPriceForPurchaseOption(
return pickupAvailability?.data?.price;
}
}
// #4760 Fehler bei Abholpreisberechnung in Filiale Darmstadt Ernst-Ludwig-Straße (negativ Preis im Warenkorb)
// Überprüfen ob der Preis ungültig ist und der Versandpreis vorhanden ist
// Wenn ja, dann den Versandpreis nehmen
if (!(availability?.data?.price?.value?.value > 0)) {
if (deliveryAvailability?.data?.price?.value?.value > 0) {
const deliveryPrice = deliveryAvailability?.data?.price;
availability.data.price = deliveryPrice;
} else {
// Wenn der Versandpreis auch ungültig ist, dann den Katalogpreis nehmen
const catalogAvailability = availabilities.find((availability) => availability.purchaseOption === 'catalog');
return catalogAvailability?.data?.price ?? DEFAULT_PRICE_DTO;
}
}
}
if (availability && availability.data.price?.value?.value && availability.data.price) {
@@ -376,7 +390,7 @@ export function getCanAddResultForItemAndCurrentPurchaseOption(itemId: number):
export function getAvailabilityWithPurchaseOption(
itemId: number,
purchaseOption: PurchaseOption
purchaseOption: PurchaseOption,
): (state: PurchaseOptionsState) => Availability {
return (state) => {
let availabilities = getAvailabilitiesForItem(itemId, true)(state);

View File

@@ -1,13 +1,14 @@
import { DOCUMENT } from '@angular/common';
import { Inject, Injectable } from '@angular/core';
import { Inject, Injectable, inject } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { map } from 'rxjs/operators';
import { ToasterService } from './toaster/toaster.service';
@Injectable({
providedIn: 'root',
})
export class ShellService {
private _fontSize$ = new BehaviorSubject<'small' | 'normal' | 'large'>('normal');
private _fontSize$ = new BehaviorSubject<'small' | 'normal' | 'large' | 'x-large'>('normal');
fontSize$ = this._fontSize$.asObservable();
@@ -38,33 +39,41 @@ export class ShellService {
} else {
return 1;
}
})
}),
);
private _sideMenuOpen$ = new BehaviorSubject<boolean>(false);
sideMenuOpen$ = this._sideMenuOpen$.asObservable();
private toaster = inject(ToasterService);
constructor(@Inject(DOCUMENT) private _document: Document) {
this._fontSize$.next(this._getStoredFontSize());
this._fontSize$.subscribe((size) => {
if (size === 'small') {
this._document.documentElement.style.setProperty('font-size', '13px');
this.toaster.open({ message: 'Font Size Set To 13px', type: 'info', closeAfter: 3000 });
} else if (size === 'normal') {
this._document.documentElement.style.setProperty('font-size', '16px');
this.toaster.open({ message: 'Font Size Set To 16px', type: 'info', closeAfter: 3000 });
} else if (size === 'large') {
this._document.documentElement.style.setProperty('font-size', '19px');
this.toaster.open({ message: 'Font Size Set To 19px', type: 'info', closeAfter: 3000 });
} else if (size === 'x-large') {
this._document.documentElement.style.setProperty('font-size', '22px');
this.toaster.open({ message: 'Font Size Set To 22px', type: 'info', closeAfter: 3000 });
}
});
}
private _getStoredFontSize() {
const stored = localStorage.getItem('isa-app-font-size');
return stored ? (stored as 'small' | 'normal' | 'large') : 'normal';
return stored ? (stored as 'small' | 'normal' | 'large' | 'x-large') : 'normal';
}
setFontSize(size: 'small' | 'normal' | 'large') {
setFontSize(size: 'small' | 'normal' | 'large' | 'x-large') {
this._fontSize$.next(size);
localStorage.setItem('isa-app-font-size', size);
}

View File

@@ -26,17 +26,15 @@ export class ShellTopBarComponent implements OnInit {
notifications$ = this._notificationsHub.notifications$;
notificationCount$ = this.notifications$.pipe(
map((notifications) => Object.values(notifications).reduce((acc, val) => acc + val?.length ?? 0, 0))
map((notifications) => Object.values(notifications).reduce((acc, val) => acc + val?.length ?? 0, 0)),
);
branchKey$ = this._stockService.StockCurrentBranch().pipe(
retry(3),
map((x) => x.result.key)
map((x) => x.result.key),
);
canNotIncreaseFontSize$ = combineLatest([this._shellService.fontSize$, this.isTablet$]).pipe(
map(([size, isTablet]) => (isTablet ? size === 'normal' : size === 'large'))
);
canNotIncreaseFontSize$ = this._shellService.fontSize$.pipe(map((size) => size === 'x-large'));
canNotDecreaseFontSize$ = this._shellService.fontSize$.pipe(map((size) => size === 'small'));
@@ -51,7 +49,7 @@ export class ShellTopBarComponent implements OnInit {
private readonly _notificationsHub: NotificationsHub,
private _modal: UiModalService,
private _app: ApplicationService,
private _environment: EnvironmentService
private _environment: EnvironmentService,
) {}
ngOnInit() {
@@ -83,13 +81,17 @@ export class ShellTopBarComponent implements OnInit {
this._shellService.setFontSize('normal');
} else if (current === 'normal') {
this._shellService.setFontSize('large');
} else if (current === 'large') {
this._shellService.setFontSize('x-large');
}
}
decreaseFontSize() {
const current = this._shellService.fontSize;
if (current === 'large') {
if (current === 'x-large') {
this._shellService.setFontSize('large');
} else if (current === 'large') {
this._shellService.setFontSize('normal');
} else if (current === 'normal') {
this._shellService.setFontSize('small');

View File

@@ -11,7 +11,7 @@ import { SEARCH_STATE_SEARCH_SERVICE, SEARCH_STATE_SETTINGS_LOADER } from './tok
@Injectable()
export class SearchComponentStoreService<T = any> extends ComponentStore<SearchState<T>> implements OnDestroy {
private _onDestroy$ = new Subject();
private _onDestroy$ = new Subject<void>();
get state() {
return this.get();

View File

@@ -1,23 +1,12 @@
/* tslint:disable */
/**
* Gr<47><72>e / Volumen
*/
export interface SizeOfString {
/**
* H<>he
*/
height: number;
/**
* L<>nge / Tiefe
*/
length: number;
/**
* Ma<4D>einheit
*/
unit?: string;
/**

View File

@@ -28,7 +28,7 @@ export class UiNotesComponent implements OnChanges, OnInit, OnDestroy {
control = new UntypedFormControl('', [Validators.required]);
private onDestroy$ = new Subject();
private onDestroy$ = new Subject<void>();
constructor(private cdr: ChangeDetectorRef) {}

16495
package-lock.json generated
View File

File diff suppressed because it is too large Load Diff

View File

@@ -53,94 +53,73 @@
"gen:swagger:eis": "ng-swagger-gen --config ng-swagger-gen/eis.json",
"gen:swagger:remi": "ng-swagger-gen --config ng-swagger-gen/remi.json",
"gen:swagger:wws": "ng-swagger-gen --config ng-swagger-gen/wws.json",
"prettier": "prettier --write ."
},
"husky": {
"hooks": {
"pre-commit": "pretty-quick --staged"
}
"prettier": "prettier --write .",
"pretty-quick": "pretty-quick --staged",
"prepare": "husky"
},
"private": true,
"dependencies": {
"@angular/animations": "^16.1.7",
"@angular/cdk": "^16.1.6",
"@angular/common": "^16.1.7",
"@angular/compiler": "^16.1.7",
"@angular/core": "^16.1.7",
"@angular/forms": "^16.1.7",
"@angular/localize": "^16.1.7",
"@angular/platform-browser": "^16.1.7",
"@angular/platform-browser-dynamic": "^16.1.7",
"@angular/router": "^16.1.7",
"@angular/service-worker": "^16.1.7",
"@angular/animations": "^17.3.10",
"@angular/cdk": "^17.3.10",
"@angular/common": "^17.3.10",
"@angular/compiler": "^17.3.10",
"@angular/core": "^17.3.10",
"@angular/forms": "^17.3.10",
"@angular/localize": "^17.3.10",
"@angular/platform-browser": "^17.3.10",
"@angular/platform-browser-dynamic": "^17.3.10",
"@angular/router": "^17.3.10",
"@angular/service-worker": "^17.3.10",
"@microsoft/signalr": "^7.0.0",
"@ngrx/component-store": "^16.1.0",
"@ngrx/effects": "^16.1.0",
"@ngrx/entity": "^16.1.0",
"@ngrx/store": "^16.1.0",
"@ngrx/store-devtools": "^16.1.0",
"angular-oauth2-oidc": "^15.0.1",
"angular-oauth2-oidc-jwks": "^15.0.1",
"core-js": "^2.6.5",
"hammerjs": "^2.0.8",
"@ngrx/component-store": "^17.2.0",
"@ngrx/effects": "^17.2.0",
"@ngrx/entity": "^17.2.0",
"@ngrx/store": "^17.2.0",
"@ngrx/store-devtools": "^17.2.0",
"angular-oauth2-oidc": "^17.0.2",
"angular-oauth2-oidc-jwks": "^17.0.2",
"lodash": "^4.17.21",
"moment": "^2.29.4",
"ng2-pdf-viewer": "^9.1.5",
"moment": "^2.30.1",
"ng2-pdf-viewer": "^10.2.2",
"parse-duration": "^1.1.0",
"rxjs": "^6.6.7",
"scandit-sdk": "^5.13.2",
"rxjs": "~7.8.0",
"scandit-sdk": "^5.15.0",
"socket.io": "^4.5.4",
"tslib": "^2.0.0",
"uglify-js": "^3.4.9",
"tslib": "^2.3.0",
"uuid": "^8.3.2",
"web-animations-js": "^2.3.2",
"zone.js": "~0.13.1"
"zone.js": "~0.14.6"
},
"devDependencies": {
"@angular-devkit/build-angular": "^16.1.6",
"@angular/cli": "^16.1.6",
"@angular/compiler-cli": "^16.1.7",
"@angular/language-service": "^16.1.7",
"@angular-devkit/build-angular": "^17.3.8",
"@angular/cli": "^17.3.8",
"@angular/compiler-cli": "^17.3.10",
"@angular/language-service": "^17.3.10",
"@ngneat/spectator": "^15.0.1",
"@types/jasmine": "~3.6.0",
"@types/jasminewd2": "~2.0.3",
"@types/lodash": "^4.14.168",
"@types/moment": "^2.13.0",
"@types/node": "^18.6.1",
"@types/uuid": "^8.3.0",
"autoprefixer": "^10.4.12",
"codelyzer": "^6.0.2",
"husky": "^4.2.3",
"jasmine-core": "~3.8.0",
"jasmine-marbles": "^0.6.0",
"jasmine-spec-reporter": "~5.0.0",
"karma": "~6.3.9",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage": "^2.1.0",
"karma-coverage-istanbul-reporter": "~3.0.2",
"karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "^1.5.0",
"karma-junit-reporter": "^2.0.1",
"ng-mocks": "^14.11.0",
"ng-packagr": "^16.1.0",
"autoprefixer": "^10.4.19",
"husky": "^9.0.11",
"jasmine-core": "~5.1.2",
"jasmine-marbles": "^0.9.2",
"jasmine-spec-reporter": "~7.0.0",
"karma": "~6.4.3",
"karma-chrome-launcher": "~3.2.0",
"karma-coverage": "^2.2.1",
"karma-coverage-istanbul-reporter": "~3.0.3",
"karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.1.0",
"karma-junit-reporter": "~2.0.1",
"ng-swagger-gen": "^2.3.1",
"ngrx-store-freeze": "^0.2.4",
"npm-run-all": "^4.1.5",
"postcss": "^8.4.18",
"postcss-loader": "^4.1.0",
"postcss-scss": "^3.0.4",
"prettier": "2.0.1",
"pretty-quick": "^2.0.1",
"sass": "^1.37.5",
"stylelint": "^13.7.2",
"stylelint-config-standard": "^20.0.0",
"tailwindcss": "^3.1.8",
"ts-node": "~7.0.0",
"tslint": "~6.1.0",
"typescript": "~5.1.6"
"postcss": "^8.4.38",
"prettier": "~3.3.1",
"pretty-quick": "~4.0.0",
"tailwindcss": "^3.4.3",
"typescript": "~5.4.5"
},
"engines": {
"node": "18.x",
"npm": "8.x"
"node": ">=18.13.0",
"npm": ">=10.5.2"
}
}
}

View File

@@ -1,13 +0,0 @@
module.exports = {
//extends: ['stylelint-config-standard'],
rules: {
'at-rule-no-unknown': [
true,
{
ignoreAtRules: ['tailwind', 'apply', 'variants', 'responsive', 'screen'],
},
],
'declaration-block-trailing-semicolon': null,
'no-descending-specificity': null,
},
};