refactor(notifications): update remission path logic to use Date.now()

This commit is contained in:
Lorenz Hilpert
2025-09-19 11:01:01 +02:00
parent 24a9ddc09c
commit 8391d0bd18
2 changed files with 309 additions and 309 deletions

View File

@@ -1,55 +1,55 @@
import {
ChangeDetectionStrategy,
Component,
EventEmitter,
Input,
Output,
inject,
linkedSignal,
} from '@angular/core';
import { Router } from '@angular/router';
import { PickupShelfInNavigationService } from '@shared/services/navigation';
import { UiFilter } from '@ui/filter';
import { MessageBoardItemDTO } from '@hub/notifications';
import { TabService } from '@isa/core/tabs';
@Component({
selector: 'modal-notifications-remission-group',
templateUrl: 'notifications-remission-group.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: false,
})
export class ModalNotificationsRemissionGroupComponent {
tabService = inject(TabService);
private _pickupShelfInNavigationService = inject(
PickupShelfInNavigationService,
);
@Input()
notifications: MessageBoardItemDTO[];
@Output()
navigated = new EventEmitter<void>();
remissionPath = linkedSignal(() => [
'/',
this.tabService.activatedTab()?.id || this.tabService.nextId(),
'remission',
]);
constructor(private _router: Router) {}
itemSelected(item: MessageBoardItemDTO) {
const defaultNav = this._pickupShelfInNavigationService.listRoute();
const queryParams = UiFilter.getQueryParamsFromQueryTokenDTO(
item.queryToken,
);
this._router.navigate(defaultNav.path, {
queryParams: {
...defaultNav.queryParams,
...queryParams,
},
});
this.navigated.emit();
}
}
import {
ChangeDetectionStrategy,
Component,
EventEmitter,
Input,
Output,
inject,
linkedSignal,
} from '@angular/core';
import { Router } from '@angular/router';
import { PickupShelfInNavigationService } from '@shared/services/navigation';
import { UiFilter } from '@ui/filter';
import { MessageBoardItemDTO } from '@hub/notifications';
import { TabService } from '@isa/core/tabs';
@Component({
selector: 'modal-notifications-remission-group',
templateUrl: 'notifications-remission-group.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: false,
})
export class ModalNotificationsRemissionGroupComponent {
tabService = inject(TabService);
private _pickupShelfInNavigationService = inject(
PickupShelfInNavigationService,
);
@Input()
notifications: MessageBoardItemDTO[];
@Output()
navigated = new EventEmitter<void>();
remissionPath = linkedSignal(() => [
'/',
this.tabService.activatedTab()?.id || Date.now(),
'remission',
]);
constructor(private _router: Router) {}
itemSelected(item: MessageBoardItemDTO) {
const defaultNav = this._pickupShelfInNavigationService.listRoute();
const queryParams = UiFilter.getQueryParamsFromQueryTokenDTO(
item.queryToken,
);
this._router.navigate(defaultNav.path, {
queryParams: {
...defaultNav.queryParams,
...queryParams,
},
});
this.navigated.emit();
}
}

View File

@@ -1,254 +1,254 @@
import {
ChangeDetectionStrategy,
Component,
OnDestroy,
OnInit,
ViewChild,
inject,
linkedSignal,
} from '@angular/core';
import { Router } from '@angular/router';
import { BreadcrumbService } from '@core/breadcrumb';
import {
KeyValueDTOOfStringAndString,
OrderItemListItemDTO,
} from '@generated/swagger/oms-api';
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
import { UiScrollContainerComponent } from '@ui/scroll-container';
import { BehaviorSubject, combineLatest, Subject } from 'rxjs';
import { first, map, shareReplay, takeUntil } from 'rxjs/operators';
import { GoodsInRemissionPreviewStore } from './goods-in-remission-preview.store';
import { Config } from '@core/config';
import { ToasterService } from '@shared/shell';
import { PickupShelfInNavigationService } from '@shared/services/navigation';
import { CacheService } from '@core/cache';
import { TabService } from '@isa/core/tabs';
@Component({
selector: 'page-goods-in-remission-preview',
templateUrl: 'goods-in-remission-preview.component.html',
styleUrls: ['goods-in-remission-preview.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [GoodsInRemissionPreviewStore],
standalone: false,
})
export class GoodsInRemissionPreviewComponent implements OnInit, OnDestroy {
tabService = inject(TabService);
private _pickupShelfInNavigationService = inject(
PickupShelfInNavigationService,
);
@ViewChild(UiScrollContainerComponent)
scrollContainer: UiScrollContainerComponent;
items$ = this._store.results$;
itemLength$ = this.items$.pipe(map((items) => items?.length));
hits$ = this._store.hits$;
loading$ = this._store.fetching$.pipe(shareReplay());
changeActionLoader$ = new BehaviorSubject<boolean>(false);
listEmpty$ = combineLatest([this.loading$, this.hits$]).pipe(
map(([loading, hits]) => !loading && hits === 0),
shareReplay(),
);
actions$ = this.items$.pipe(map((items) => items[0]?.actions));
private _onDestroy$ = new Subject<void>();
byBuyerNumberFn = (item: OrderItemListItemDTO) => item.buyerNumber;
byOrderNumberFn = (item: OrderItemListItemDTO) => item.orderNumber;
byProcessingStatusFn = (item: OrderItemListItemDTO) => item.processingStatus;
byCompartmentCodeFn = (item: OrderItemListItemDTO) =>
item.compartmentInfo
? `${item.compartmentCode}_${item.compartmentInfo}`
: item.compartmentCode;
private readonly SCROLL_POSITION_TOKEN = 'REMISSION_PREVIEW_SCROLL_POSITION';
remissionPath = linkedSignal(() => [
'/',
this.tabService.activatedTab()?.id || this.tabService.nextId(),
'remission',
]);
constructor(
private _breadcrumb: BreadcrumbService,
private _store: GoodsInRemissionPreviewStore,
private _router: Router,
private _modal: UiModalService,
private _config: Config,
private _toast: ToasterService,
private _cache: CacheService,
) {}
ngOnInit(): void {
this.initInitialSearch();
this.createBreadcrumb();
this.removeBreadcrumbs();
}
ngOnDestroy(): void {
this._onDestroy$.next();
this._onDestroy$.complete();
this._addScrollPositionToCache();
this.updateBreadcrumb();
}
private _removeScrollPositionFromCache(): void {
this._cache.delete({
processId: this._config.get('process.ids.goodsIn'),
token: this.SCROLL_POSITION_TOKEN,
});
}
private _addScrollPositionToCache(): void {
this._cache.set<number>(
{
processId: this._config.get('process.ids.goodsIn'),
token: this.SCROLL_POSITION_TOKEN,
},
this.scrollContainer?.scrollPos,
);
}
private async _getScrollPositionFromCache(): Promise<number> {
return await this._cache.get<number>({
processId: this._config.get('process.ids.goodsIn'),
token: this.SCROLL_POSITION_TOKEN,
});
}
async createBreadcrumb() {
await this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({
key: this._config.get('process.ids.goodsIn'),
name: 'Abholfachremissionsvorschau',
path: '/filiale/goods/in/preview',
section: 'branch',
params: { view: 'remission' },
tags: ['goods-in', 'preview'],
});
}
async updateBreadcrumb() {
const crumbs = await this._breadcrumb
.getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), [
'goods-in',
'preview',
])
.pipe(first())
.toPromise();
for (const crumb of crumbs) {
this._breadcrumb.patchBreadcrumb(crumb.id, {
name: crumb.name,
});
}
}
async removeBreadcrumbs() {
let breadcrumbsToDelete = await this._breadcrumb
.getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), [
'goods-in',
])
.pipe(first())
.toPromise();
breadcrumbsToDelete = breadcrumbsToDelete.filter(
(crumb) =>
!crumb.tags.includes('preview') && !crumb.tags.includes('main'),
);
breadcrumbsToDelete.forEach((crumb) => {
this._breadcrumb.removeBreadcrumb(crumb.id, true);
});
const detailsCrumbs = await this._breadcrumb
.getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), [
'goods-in',
'details',
])
.pipe(first())
.toPromise();
const editCrumbs = await this._breadcrumb
.getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), [
'goods-in',
'edit',
])
.pipe(first())
.toPromise();
detailsCrumbs.forEach((crumb) => {
this._breadcrumb.removeBreadcrumb(crumb.id, true);
});
editCrumbs.forEach((crumb) => {
this._breadcrumb.removeBreadcrumb(crumb.id, true);
});
}
initInitialSearch() {
if (this._store.hits === 0) {
this._store.searchResult$
.pipe(takeUntil(this._onDestroy$))
.subscribe(async (result) => {
await this.createBreadcrumb();
this.scrollContainer?.scrollTo(
(await this._getScrollPositionFromCache()) ?? 0,
);
this._removeScrollPositionFromCache();
});
}
this._store.search();
}
async navigateToRemission() {
await this._router.navigate(this.remissionPath());
}
navigateToDetails(orderItem: OrderItemListItemDTO) {
const nav = this._pickupShelfInNavigationService.detailRoute({
item: orderItem,
side: false,
});
this._router.navigate(nav.path, {
queryParams: { ...nav.queryParams, view: 'remission' },
});
}
async handleAction(action: KeyValueDTOOfStringAndString) {
this.changeActionLoader$.next(true);
try {
const response = await this._store
.createRemissionFromPreview()
.pipe(first())
.toPromise();
if (!response?.dialog) {
this._toast.open({
title: 'Abholfachremission',
message: response?.message,
});
}
await this.navigateToRemission();
} catch (error) {
this._modal.open({
content: UiErrorModalComponent,
data: error,
});
console.error(error);
}
this.changeActionLoader$.next(false);
}
}
import {
ChangeDetectionStrategy,
Component,
OnDestroy,
OnInit,
ViewChild,
inject,
linkedSignal,
} from '@angular/core';
import { Router } from '@angular/router';
import { BreadcrumbService } from '@core/breadcrumb';
import {
KeyValueDTOOfStringAndString,
OrderItemListItemDTO,
} from '@generated/swagger/oms-api';
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
import { UiScrollContainerComponent } from '@ui/scroll-container';
import { BehaviorSubject, combineLatest, Subject } from 'rxjs';
import { first, map, shareReplay, takeUntil } from 'rxjs/operators';
import { GoodsInRemissionPreviewStore } from './goods-in-remission-preview.store';
import { Config } from '@core/config';
import { ToasterService } from '@shared/shell';
import { PickupShelfInNavigationService } from '@shared/services/navigation';
import { CacheService } from '@core/cache';
import { TabService } from '@isa/core/tabs';
@Component({
selector: 'page-goods-in-remission-preview',
templateUrl: 'goods-in-remission-preview.component.html',
styleUrls: ['goods-in-remission-preview.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [GoodsInRemissionPreviewStore],
standalone: false,
})
export class GoodsInRemissionPreviewComponent implements OnInit, OnDestroy {
tabService = inject(TabService);
private _pickupShelfInNavigationService = inject(
PickupShelfInNavigationService,
);
@ViewChild(UiScrollContainerComponent)
scrollContainer: UiScrollContainerComponent;
items$ = this._store.results$;
itemLength$ = this.items$.pipe(map((items) => items?.length));
hits$ = this._store.hits$;
loading$ = this._store.fetching$.pipe(shareReplay());
changeActionLoader$ = new BehaviorSubject<boolean>(false);
listEmpty$ = combineLatest([this.loading$, this.hits$]).pipe(
map(([loading, hits]) => !loading && hits === 0),
shareReplay(),
);
actions$ = this.items$.pipe(map((items) => items[0]?.actions));
private _onDestroy$ = new Subject<void>();
byBuyerNumberFn = (item: OrderItemListItemDTO) => item.buyerNumber;
byOrderNumberFn = (item: OrderItemListItemDTO) => item.orderNumber;
byProcessingStatusFn = (item: OrderItemListItemDTO) => item.processingStatus;
byCompartmentCodeFn = (item: OrderItemListItemDTO) =>
item.compartmentInfo
? `${item.compartmentCode}_${item.compartmentInfo}`
: item.compartmentCode;
private readonly SCROLL_POSITION_TOKEN = 'REMISSION_PREVIEW_SCROLL_POSITION';
remissionPath = linkedSignal(() => [
'/',
this.tabService.activatedTab()?.id || Date.now(),
'remission',
]);
constructor(
private _breadcrumb: BreadcrumbService,
private _store: GoodsInRemissionPreviewStore,
private _router: Router,
private _modal: UiModalService,
private _config: Config,
private _toast: ToasterService,
private _cache: CacheService,
) {}
ngOnInit(): void {
this.initInitialSearch();
this.createBreadcrumb();
this.removeBreadcrumbs();
}
ngOnDestroy(): void {
this._onDestroy$.next();
this._onDestroy$.complete();
this._addScrollPositionToCache();
this.updateBreadcrumb();
}
private _removeScrollPositionFromCache(): void {
this._cache.delete({
processId: this._config.get('process.ids.goodsIn'),
token: this.SCROLL_POSITION_TOKEN,
});
}
private _addScrollPositionToCache(): void {
this._cache.set<number>(
{
processId: this._config.get('process.ids.goodsIn'),
token: this.SCROLL_POSITION_TOKEN,
},
this.scrollContainer?.scrollPos,
);
}
private async _getScrollPositionFromCache(): Promise<number> {
return await this._cache.get<number>({
processId: this._config.get('process.ids.goodsIn'),
token: this.SCROLL_POSITION_TOKEN,
});
}
async createBreadcrumb() {
await this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({
key: this._config.get('process.ids.goodsIn'),
name: 'Abholfachremissionsvorschau',
path: '/filiale/goods/in/preview',
section: 'branch',
params: { view: 'remission' },
tags: ['goods-in', 'preview'],
});
}
async updateBreadcrumb() {
const crumbs = await this._breadcrumb
.getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), [
'goods-in',
'preview',
])
.pipe(first())
.toPromise();
for (const crumb of crumbs) {
this._breadcrumb.patchBreadcrumb(crumb.id, {
name: crumb.name,
});
}
}
async removeBreadcrumbs() {
let breadcrumbsToDelete = await this._breadcrumb
.getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), [
'goods-in',
])
.pipe(first())
.toPromise();
breadcrumbsToDelete = breadcrumbsToDelete.filter(
(crumb) =>
!crumb.tags.includes('preview') && !crumb.tags.includes('main'),
);
breadcrumbsToDelete.forEach((crumb) => {
this._breadcrumb.removeBreadcrumb(crumb.id, true);
});
const detailsCrumbs = await this._breadcrumb
.getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), [
'goods-in',
'details',
])
.pipe(first())
.toPromise();
const editCrumbs = await this._breadcrumb
.getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), [
'goods-in',
'edit',
])
.pipe(first())
.toPromise();
detailsCrumbs.forEach((crumb) => {
this._breadcrumb.removeBreadcrumb(crumb.id, true);
});
editCrumbs.forEach((crumb) => {
this._breadcrumb.removeBreadcrumb(crumb.id, true);
});
}
initInitialSearch() {
if (this._store.hits === 0) {
this._store.searchResult$
.pipe(takeUntil(this._onDestroy$))
.subscribe(async (result) => {
await this.createBreadcrumb();
this.scrollContainer?.scrollTo(
(await this._getScrollPositionFromCache()) ?? 0,
);
this._removeScrollPositionFromCache();
});
}
this._store.search();
}
async navigateToRemission() {
await this._router.navigate(this.remissionPath());
}
navigateToDetails(orderItem: OrderItemListItemDTO) {
const nav = this._pickupShelfInNavigationService.detailRoute({
item: orderItem,
side: false,
});
this._router.navigate(nav.path, {
queryParams: { ...nav.queryParams, view: 'remission' },
});
}
async handleAction(action: KeyValueDTOOfStringAndString) {
this.changeActionLoader$.next(true);
try {
const response = await this._store
.createRemissionFromPreview()
.pipe(first())
.toPromise();
if (!response?.dialog) {
this._toast.open({
title: 'Abholfachremission',
message: response?.message,
});
}
await this.navigateToRemission();
} catch (error) {
this._modal.open({
content: UiErrorModalComponent,
data: error,
});
console.error(error);
}
this.changeActionLoader$.next(false);
}
}