diff --git a/apps/isa-app/src/app/store/root-state.service.ts b/apps/isa-app/src/app/store/root-state.service.ts index 59e437a85..32c99dc3d 100644 --- a/apps/isa-app/src/app/store/root-state.service.ts +++ b/apps/isa-app/src/app/store/root-state.service.ts @@ -1,18 +1,18 @@ -import { Injectable } from '@angular/core'; -import { Logger, LogLevel } from '@core/logger'; -import { Store } from '@ngrx/store'; -import { debounceTime, switchMap, takeUntil } from 'rxjs/operators'; -import { RootState } from './root.state'; -import packageInfo from 'packageJson'; -import { environment } from '../../environments/environment'; -import { Subject } from 'rxjs'; -import { AuthService } from '@core/auth'; -import { injectStorage, UserStorageProvider } from '@isa/core/storage'; -import { isEqual } from 'lodash'; +import { Injectable } from "@angular/core"; +import { Logger, LogLevel } from "@core/logger"; +import { Store } from "@ngrx/store"; +import { debounceTime, switchMap, takeUntil } from "rxjs/operators"; +import { RootState } from "./root.state"; +import packageInfo from "packageJson"; +import { environment } from "../../environments/environment"; +import { Subject } from "rxjs"; +import { AuthService } from "@core/auth"; +import { injectStorage, UserStorageProvider } from "@isa/core/storage"; +import { isEqual } from "lodash"; -@Injectable({ providedIn: 'root' }) +@Injectable({ providedIn: "root" }) export class RootStateService { - static LOCAL_STORAGE_KEY = 'ISA_APP_INITIALSTATE'; + static LOCAL_STORAGE_KEY = "ISA_APP_INITIALSTATE"; #storage = injectStorage(UserStorageProvider); @@ -29,14 +29,17 @@ export class RootStateService { ); } - window['clearUserState'] = () => { + window["clearUserState"] = () => { this.clear(); }; } async init() { await this.load(); - this._store.dispatch({ type: 'HYDRATE', payload: RootStateService.LoadFromLocalStorage() }); + this._store.dispatch({ + type: "HYDRATE", + payload: RootStateService.LoadFromLocalStorage(), + }); this.initSave(); } @@ -50,14 +53,10 @@ export class RootStateService { const data = { ...state, version: packageInfo.version, - sub: this._authService.getClaimByKey('sub'), + sub: this._authService.getClaimByKey("sub"), }; RootStateService.SaveToLocalStorageRaw(JSON.stringify(data)); - return this.#storage.set('state', { - ...state, - version: packageInfo.version, - sub: this._authService.getClaimByKey('sub'), - }); + return this.#storage.set("state", data); }), ) .subscribe(); @@ -68,7 +67,7 @@ export class RootStateService { */ async load(): Promise { try { - const res = await this.#storage.get('state'); + const res = await this.#storage.get("state"); const storageContent = RootStateService.LoadFromLocalStorageRaw(); @@ -88,7 +87,7 @@ export class RootStateService { async clear() { try { this._cancelSave.next(); - await this.#storage.clear('state'); + await this.#storage.clear("state"); await new Promise((resolve) => setTimeout(resolve, 100)); RootStateService.RemoveFromLocalStorage(); await new Promise((resolve) => setTimeout(resolve, 100)); @@ -112,7 +111,7 @@ export class RootStateService { try { return JSON.parse(raw); } catch (error) { - console.error('Error parsing local storage:', error); + console.error("Error parsing local storage:", error); this.RemoveFromLocalStorage(); } } diff --git a/apps/isa-app/src/shared/components/searchbox/searchbox.component.ts b/apps/isa-app/src/shared/components/searchbox/searchbox.component.ts index f9fe6ba21..9efcc4896 100644 --- a/apps/isa-app/src/shared/components/searchbox/searchbox.component.ts +++ b/apps/isa-app/src/shared/components/searchbox/searchbox.component.ts @@ -16,20 +16,20 @@ import { forwardRef, Optional, inject, -} from '@angular/core'; -import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; -import { UiAutocompleteComponent } from '@ui/autocomplete'; -import { UiFormControlDirective } from '@ui/form-control'; -import { containsElement } from '@utils/common'; -import { Subscription } from 'rxjs'; -import { ScanAdapterService } from '@adapter/scan'; -import { injectCancelSearch } from '@shared/services/cancel-subject'; -import { EnvironmentService } from '@core/environment'; +} from "@angular/core"; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms"; +import { UiAutocompleteComponent } from "@ui/autocomplete"; +import { UiFormControlDirective } from "@ui/form-control"; +import { containsElement } from "@utils/common"; +import { Subscription } from "rxjs"; +import { ScanAdapterService } from "@adapter/scan"; +import { injectCancelSearch } from "@shared/services/cancel-subject"; +import { EnvironmentService } from "@core/environment"; @Component({ - selector: 'shared-searchbox', - templateUrl: 'searchbox.component.html', - styleUrls: ['searchbox.component.scss'], + selector: "shared-searchbox", + templateUrl: "searchbox.component.html", + styleUrls: ["searchbox.component.scss"], providers: [ { provide: NG_VALUE_ACCESSOR, @@ -49,9 +49,9 @@ export class SearchboxComponent cancelSearch = injectCancelSearch({ optional: true }); disabled: boolean; - type = 'text'; + type = "text"; - @ViewChild('input', { read: ElementRef, static: true }) + @ViewChild("input", { read: ElementRef, static: true }) input: ElementRef; @ContentChild(UiAutocompleteComponent) @@ -61,9 +61,9 @@ export class SearchboxComponent focusAfterViewInit = true; @Input() - placeholder = ''; + placeholder = ""; - private _query = ''; + private _query = ""; @Input() get query() { @@ -94,7 +94,7 @@ export class SearchboxComponent scanner = false; @Input() - hint = ''; + hint = ""; @Input() autocompleteValueSelector: (item: any) => string = (item: any) => item; @@ -104,11 +104,11 @@ export class SearchboxComponent } clear(): void { - this.setQuery(''); + this.setQuery(""); this.cancelSearch(); } - @HostBinding('class.autocomplete-opend') + @HostBinding("class.autocomplete-opend") get autocompleteOpen() { return this.autocomplete?.opend; } @@ -213,13 +213,13 @@ export class SearchboxComponent } clearHint() { - this.hint = ''; + this.hint = ""; this.focused.emit(true); this.cdr.markForCheck(); } onKeyup(event: KeyboardEvent) { - if (event.key === 'Enter') { + if (event.key === "Enter") { if (this.autocomplete?.opend && this.autocomplete?.activeItem) { this.setQuery(this.autocomplete?.activeItem?.item); this.autocomplete?.close(); @@ -227,7 +227,7 @@ export class SearchboxComponent this.search.emit(this.query); event.preventDefault(); - } else if (event.key === 'ArrowUp' || event.key === 'ArrowDown') { + } else if (event.key === "ArrowUp" || event.key === "ArrowDown") { this.handleArrowUpDownEvent(event); } } @@ -242,7 +242,7 @@ export class SearchboxComponent } } - @HostListener('window:click', ['$event']) + @HostListener("window:click", ["$event"]) focusLost(event: MouseEvent) { if ( this.autocomplete?.opend && @@ -256,9 +256,11 @@ export class SearchboxComponent this.search.emit(this.query); } - @HostListener('focusout', ['$event']) + @HostListener("focusout", ["$event"]) onBlur() { - this.onTouched(); + if (typeof this.onTouched === "function") { + this.onTouched(); + } this.focused.emit(false); this.cdr.markForCheck(); } diff --git a/apps/isa-app/src/ui/searchbox/next/searchbox.component.ts b/apps/isa-app/src/ui/searchbox/next/searchbox.component.ts index eb6503938..7dfee1167 100644 --- a/apps/isa-app/src/ui/searchbox/next/searchbox.component.ts +++ b/apps/isa-app/src/ui/searchbox/next/searchbox.component.ts @@ -16,20 +16,20 @@ import { forwardRef, Optional, inject, -} from '@angular/core'; -import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; -import { UiAutocompleteComponent } from '@ui/autocomplete'; -import { UiFormControlDirective } from '@ui/form-control'; -import { Subscription } from 'rxjs'; -import { ScanAdapterService } from '@adapter/scan'; -import { injectCancelSearch } from '@shared/services/cancel-subject'; -import { containsElement } from '@utils/common'; -import { EnvironmentService } from '@core/environment'; +} from "@angular/core"; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms"; +import { UiAutocompleteComponent } from "@ui/autocomplete"; +import { UiFormControlDirective } from "@ui/form-control"; +import { Subscription } from "rxjs"; +import { ScanAdapterService } from "@adapter/scan"; +import { injectCancelSearch } from "@shared/services/cancel-subject"; +import { containsElement } from "@utils/common"; +import { EnvironmentService } from "@core/environment"; @Component({ - selector: 'ui-searchbox', - templateUrl: 'searchbox.component.html', - styleUrls: ['searchbox.component.scss'], + selector: "ui-searchbox", + templateUrl: "searchbox.component.html", + styleUrls: ["searchbox.component.scss"], providers: [ { provide: NG_VALUE_ACCESSOR, @@ -49,9 +49,9 @@ export class UiSearchboxNextComponent private readonly _cancelSearch = injectCancelSearch({ optional: true }); disabled: boolean; - type = 'text'; + type = "text"; - @ViewChild('input', { read: ElementRef, static: true }) + @ViewChild("input", { read: ElementRef, static: true }) input: ElementRef; @ContentChild(UiAutocompleteComponent) @@ -61,9 +61,9 @@ export class UiSearchboxNextComponent focusAfterViewInit: boolean = true; @Input() - placeholder: string = ''; + placeholder: string = ""; - private _query = ''; + private _query = ""; @Input() get query() { @@ -94,7 +94,7 @@ export class UiSearchboxNextComponent scanner = false; @Input() - hint: string = ''; + hint: string = ""; @Output() hintCleared = new EventEmitter(); @@ -107,11 +107,11 @@ export class UiSearchboxNextComponent } clear(): void { - this.setQuery(''); + this.setQuery(""); this._cancelSearch(); } - @HostBinding('class.autocomplete-opend') + @HostBinding("class.autocomplete-opend") get autocompleteOpen() { return this.autocomplete?.opend; } @@ -212,14 +212,14 @@ export class UiSearchboxNextComponent } clearHint() { - this.hint = ''; + this.hint = ""; this.focused.emit(true); this.hintCleared.emit(); this.cdr.markForCheck(); } onKeyup(event: KeyboardEvent) { - if (event.key === 'Enter') { + if (event.key === "Enter") { if (this.autocomplete?.opend && this.autocomplete?.activeItem) { this.setQuery(this.autocomplete?.activeItem?.item); this.autocomplete?.close(); @@ -227,7 +227,7 @@ export class UiSearchboxNextComponent this.search.emit(this.query); event.preventDefault(); - } else if (event.key === 'ArrowUp' || event.key === 'ArrowDown') { + } else if (event.key === "ArrowUp" || event.key === "ArrowDown") { this.handleArrowUpDownEvent(event); } } @@ -235,12 +235,14 @@ export class UiSearchboxNextComponent handleArrowUpDownEvent(event: KeyboardEvent) { this.autocomplete?.handleKeyboardEvent(event); if (this.autocomplete?.activeItem) { - const query = this.autocompleteValueSelector(this.autocomplete.activeItem.item); + const query = this.autocompleteValueSelector( + this.autocomplete.activeItem.item, + ); this.setQuery(query, false, false); } } - @HostListener('window:click', ['$event']) + @HostListener("window:click", ["$event"]) focusLost(event: MouseEvent) { if ( this.autocomplete?.opend && @@ -254,9 +256,11 @@ export class UiSearchboxNextComponent this.search.emit(this.query); } - @HostListener('focusout', ['$event']) + @HostListener("focusout", ["$event"]) onBlur() { - this.onTouched(); + if (typeof this.onTouched === "function") { + this.onTouched(); + } this.focused.emit(false); this.cdr.markForCheck(); } diff --git a/libs/core/storage/src/lib/user.storage-provider.ts b/libs/core/storage/src/lib/user.storage-provider.ts index 04f330e05..9dc3be7ed 100644 --- a/libs/core/storage/src/lib/user.storage-provider.ts +++ b/libs/core/storage/src/lib/user.storage-provider.ts @@ -1,27 +1,42 @@ -import { inject, Injectable } from '@angular/core'; -import { StorageProvider } from './storage-provider'; -import { UserStateService } from '@generated/swagger/isa-api'; -import { firstValueFrom, map, shareReplay } from 'rxjs'; +import { inject, Injectable } from "@angular/core"; +import { StorageProvider } from "./storage-provider"; +import { UserStateService } from "@generated/swagger/isa-api"; +import { catchError, firstValueFrom, map, of } from "rxjs"; +import { isEmpty } from "lodash"; -@Injectable({ providedIn: 'root' }) +@Injectable({ providedIn: "root" }) export class UserStorageProvider implements StorageProvider { #userStateService = inject(UserStateService); private state$ = this.#userStateService.UserStateGetUserState().pipe( map((res) => { - if (res.result?.content) { + if (res?.result?.content) { return JSON.parse(res.result.content); } return {}; }), - shareReplay(1), + catchError((err) => { + console.warn( + "No UserStateGetUserState found, returning empty object:", + err, + ); + return of({}); // Return empty state fallback + }), + // shareReplay(1), #5249, #5270 Würde beim Fehlerfall den fehlerhaften Zustand behalten + // Aktuell wird nun jedes mal 2 mal der UserState aufgerufen (GET + POST) + // Damit bei der set Funktion immer der aktuelle Zustand verwendet wird ); - async set(key: string, value: unknown): Promise { + async set(key: string, value: Record): Promise { const current = await firstValueFrom(this.state$); - firstValueFrom( + const content = + current && !isEmpty(current) + ? { ...current, [key]: value } + : { [key]: value }; + + await firstValueFrom( this.#userStateService.UserStateSetUserState({ - content: JSON.stringify({ ...current, [key]: value }), + content: JSON.stringify(content), }), ); } @@ -32,7 +47,6 @@ export class UserStorageProvider implements StorageProvider { } async clear(key: string): Promise { - const current = await firstValueFrom(this.state$); delete current[key]; firstValueFrom(this.#userStateService.UserStateResetUserState()); diff --git a/libs/oms/feature/return-summary/src/lib/return-summary-item/return-summary-item.component.html b/libs/oms/feature/return-summary/src/lib/return-summary-item/return-summary-item.component.html index 856db3361..b0d46a695 100644 --- a/libs/oms/feature/return-summary/src/lib/return-summary-item/return-summary-item.component.html +++ b/libs/oms/feature/return-summary/src/lib/return-summary-item/return-summary-item.component.html @@ -35,6 +35,7 @@ name="isaActionEdit" data-what="button" data-which="edit-return-item" - (click)="navigateBack()" + [disabled]="returnItemsAndPrintReciptPending()" + (click)="location.back()" > diff --git a/libs/oms/feature/return-summary/src/lib/return-summary-item/return-summary-item.component.spec.ts b/libs/oms/feature/return-summary/src/lib/return-summary-item/return-summary-item.component.spec.ts index 964a8f7b3..3aeda12a7 100644 --- a/libs/oms/feature/return-summary/src/lib/return-summary-item/return-summary-item.component.spec.ts +++ b/libs/oms/feature/return-summary/src/lib/return-summary-item/return-summary-item.component.spec.ts @@ -1,16 +1,16 @@ -import { createRoutingFactory, Spectator } from '@ngneat/spectator/jest'; -import { ReturnSummaryItemComponent } from './return-summary-item.component'; -import { MockComponents, MockProvider } from 'ng-mocks'; -import { ReturnProductInfoComponent } from '@isa/oms/shared/product-info'; +import { createRoutingFactory, Spectator } from "@ngneat/spectator/jest"; +import { ReturnSummaryItemComponent } from "./return-summary-item.component"; +import { MockComponents, MockProvider } from "ng-mocks"; +import { ReturnProductInfoComponent } from "@isa/oms/shared/product-info"; import { Product, ReturnProcess, ReturnProcessQuestionKey, ReturnProcessService, -} from '@isa/oms/data-access'; -import { NgIcon } from '@ng-icons/core'; -import { IconButtonComponent } from '@isa/ui/buttons'; -import { Router } from '@angular/router'; +} from "@isa/oms/data-access"; +import { NgIcon } from "@ng-icons/core"; +import { IconButtonComponent } from "@isa/ui/buttons"; +import { Location } from "@angular/common"; /** * Creates a mock ReturnProcess with default values that can be overridden @@ -21,20 +21,20 @@ function createMockReturnProcess( return { id: 1, processId: 1, - productCategory: 'Electronics', + productCategory: "Electronics", answers: {}, receiptId: 123, receiptItem: { id: 321, product: { - name: 'Test Product', + name: "Test Product", }, }, ...partial, } as ReturnProcess; } -describe('ReturnSummaryItemComponent', () => { +describe("ReturnSummaryItemComponent", () => { let spectator: Spectator; let returnProcessService: jest.Mocked; @@ -48,7 +48,10 @@ describe('ReturnSummaryItemComponent', () => { providers: [ MockProvider(ReturnProcessService, { getReturnInfo: jest.fn(), - eligibleForReturn: jest.fn().mockReturnValue({ state: 'eligible' }), + eligibleForReturn: jest.fn().mockReturnValue({ state: "eligible" }), + }), + MockProvider(Location, { + back: jest.fn(), }), ], shallow: true, @@ -64,38 +67,38 @@ describe('ReturnSummaryItemComponent', () => { spectator.detectChanges(); }); - describe('Component Creation', () => { - it('should create the component', () => { + describe("Component Creation", () => { + it("should create the component", () => { expect(spectator.component).toBeTruthy(); }); }); - describe('Return Information Display', () => { + describe("Return Information Display", () => { const mockReturnInfo = { - itemCondition: 'itemCondition', - returnDetails: { [ReturnProcessQuestionKey.CaseDamaged]: 'no' }, - returnReason: 'returnReason', + itemCondition: "itemCondition", + returnDetails: { [ReturnProcessQuestionKey.CaseDamaged]: "no" }, + returnReason: "returnReason", otherProduct: { - ean: 'ean', + ean: "ean", } as Product, - comment: 'comment', + comment: "comment", }; beforeEach(() => { jest - .spyOn(returnProcessService, 'getReturnInfo') + .spyOn(returnProcessService, "getReturnInfo") .mockReturnValue(mockReturnInfo); - spectator.setInput('returnProcess', createMockReturnProcess({ id: 2 })); + spectator.setInput("returnProcess", createMockReturnProcess({ id: 2 })); spectator.detectChanges(); }); - it('should provide correct return information array', () => { + it("should provide correct return information array", () => { // Arrange const expectedInfos = [ - 'itemCondition', - 'returnReason', - 'Gehäuse beschädigt: no', - 'Geliefert wurde: ean', - 'comment', + "itemCondition", + "returnReason", + "Gehäuse beschädigt: no", + "Geliefert wurde: ean", + "comment", ]; // Act @@ -105,14 +108,14 @@ describe('ReturnSummaryItemComponent', () => { expect(actualInfos).toEqual(expectedInfos); expect(actualInfos.length).toBe(5); }); - it('should render return info items with correct content', () => { + it("should render return info items with correct content", () => { // Arrange const expectedInfos = [ - 'itemCondition', - 'returnReason', - 'Gehäuse beschädigt: no', - 'Geliefert wurde: ean', - 'comment', + "itemCondition", + "returnReason", + "Gehäuse beschädigt: no", + "Geliefert wurde: ean", + "comment", ]; // Act @@ -125,14 +128,14 @@ describe('ReturnSummaryItemComponent', () => { expect(listItems.length).toBe(expectedInfos.length); listItems.forEach((item, index) => { expect(item).toHaveText(expectedInfos[index]); - expect(item).toHaveAttribute('data-info-index', index.toString()); + expect(item).toHaveAttribute("data-info-index", index.toString()); }); }); - it('should handle undefined return info gracefully', () => { + it("should handle undefined return info gracefully", () => { // Arrange returnProcessService.getReturnInfo.mockReturnValue(undefined); - spectator.setInput('returnProcess', createMockReturnProcess({ id: 3 })); + spectator.setInput("returnProcess", createMockReturnProcess({ id: 3 })); spectator.detectChanges(); // Act @@ -146,26 +149,26 @@ describe('ReturnSummaryItemComponent', () => { expect(listItems.length).toBe(0); }); - describe('returnDetails mapping', () => { - it('should map multiple returnDetails keys to correct info strings', () => { + describe("returnDetails mapping", () => { + it("should map multiple returnDetails keys to correct info strings", () => { const expected = [ - 'itemCondition', - 'returnReason', - 'Gehäuse beschädigt: Ja', - 'Display beschädigt: Nein', - 'Geliefert wurde: ean', - 'comment', + "itemCondition", + "returnReason", + "Gehäuse beschädigt: Ja", + "Display beschädigt: Nein", + "Geliefert wurde: ean", + "comment", ]; // Arrange const details = { - [ReturnProcessQuestionKey.CaseDamaged]: 'Ja', - [ReturnProcessQuestionKey.DisplayDamaged]: 'Nein', + [ReturnProcessQuestionKey.CaseDamaged]: "Ja", + [ReturnProcessQuestionKey.DisplayDamaged]: "Nein", }; returnProcessService.getReturnInfo.mockReturnValue({ ...mockReturnInfo, returnDetails: details, }); - spectator.setInput('returnProcess', createMockReturnProcess({ id: 4 })); + spectator.setInput("returnProcess", createMockReturnProcess({ id: 4 })); spectator.detectChanges(); // Act @@ -173,31 +176,31 @@ describe('ReturnSummaryItemComponent', () => { expect(infos).toEqual(expected); }); - it('should not include returnDetails if empty', () => { + it("should not include returnDetails if empty", () => { // Arrange returnProcessService.getReturnInfo.mockReturnValue({ ...mockReturnInfo, returnDetails: {}, }); - spectator.setInput('returnProcess', createMockReturnProcess({ id: 5 })); + spectator.setInput("returnProcess", createMockReturnProcess({ id: 5 })); spectator.detectChanges(); // Act const infos = spectator.component.returnInfos(); // Assert - expect(infos.some((info) => info.includes('Gehäuse beschädigt'))).toBe( + expect(infos.some((info) => info.includes("Gehäuse beschädigt"))).toBe( false, ); - expect(infos.some((info) => info.includes('Zubehör fehlt'))).toBe( + expect(infos.some((info) => info.includes("Zubehör fehlt"))).toBe( false, ); }); }); }); - describe('Navigation', () => { - it('should render edit button with correct attributes', () => { + describe("Navigation", () => { + it("should render edit button with correct attributes", () => { // Assert const editButton = spectator.query( '[data-what="button"][data-which="edit-return-item"]', @@ -205,7 +208,7 @@ describe('ReturnSummaryItemComponent', () => { expect(editButton).toExist(); }); - it('should navigate back when edit button is clicked', () => { + it("should navigate back when edit button is clicked", () => { // Arrange const editButton = spectator.query( '[data-what="button"][data-which="edit-return-item"]', @@ -217,25 +220,20 @@ describe('ReturnSummaryItemComponent', () => { } // Assert - expect(spectator.inject(Router).navigate).toHaveBeenCalledWith( - ['..'], - expect.objectContaining({ - relativeTo: expect.anything(), - }), - ); + expect(spectator.inject(Location).back).toHaveBeenCalled(); }); }); - it('should render the product info component', () => { + it("should render the product info component", () => { const productInfo = spectator.query(ReturnProductInfoComponent); expect(productInfo).toExist(); }); - it('should compute eligibility state as eligible', () => { + it("should compute eligibility state as eligible", () => { (returnProcessService.eligibleForReturn as jest.Mock).mockReturnValue({ - state: 'eligible', + state: "eligible", }); spectator.detectChanges(); - expect(spectator.component.eligibleForReturn()?.state).toBe('eligible'); + expect(spectator.component.eligibleForReturn()?.state).toBe("eligible"); }); }); diff --git a/libs/oms/feature/return-summary/src/lib/return-summary-item/return-summary-item.component.ts b/libs/oms/feature/return-summary/src/lib/return-summary-item/return-summary-item.component.ts index dc4673b4e..7a901b185 100644 --- a/libs/oms/feature/return-summary/src/lib/return-summary-item/return-summary-item.component.ts +++ b/libs/oms/feature/return-summary/src/lib/return-summary-item/return-summary-item.component.ts @@ -4,13 +4,13 @@ import { computed, inject, input, -} from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; +} from "@angular/core"; +import { Location } from "@angular/common"; import { isaActionChevronRight, isaActionClose, isaActionEdit, -} from '@isa/icons'; +} from "@isa/icons"; import { EligibleForReturn, EligibleForReturnState, @@ -18,10 +18,10 @@ import { ReturnProcessService, ProductCategory, returnDetailsMapping, -} from '@isa/oms/data-access'; -import { ReturnProductInfoComponent } from '@isa/oms/shared/product-info'; -import { IconButtonComponent } from '@isa/ui/buttons'; -import { NgIcon, provideIcons } from '@ng-icons/core'; +} from "@isa/oms/data-access"; +import { ReturnProductInfoComponent } from "@isa/oms/shared/product-info"; +import { IconButtonComponent } from "@isa/ui/buttons"; +import { NgIcon, provideIcons } from "@ng-icons/core"; /** * Displays a single item in the return process summary, showing product details @@ -47,30 +47,34 @@ import { NgIcon, provideIcons } from '@ng-icons/core'; * ``` */ @Component({ - selector: 'oms-feature-return-summary-item', - templateUrl: './return-summary-item.component.html', - styleUrls: ['./return-summary-item.component.scss'], + selector: "oms-feature-return-summary-item", + templateUrl: "./return-summary-item.component.html", + styleUrls: ["./return-summary-item.component.scss"], changeDetection: ChangeDetectionStrategy.OnPush, imports: [ReturnProductInfoComponent, NgIcon, IconButtonComponent], providers: [ provideIcons({ isaActionChevronRight, isaActionEdit, isaActionClose }), ], host: { - 'data-what': 'list-item', - 'data-which': 'return-process-item', - '[attr.data-receipt-id]': 'returnProcess()?.receiptId', - '[attr.data-return-item-id]': 'returnProcess()?.returnItem?.id', + "data-what": "list-item", + "data-which": "return-process-item", + "[attr.data-receipt-id]": "returnProcess()?.receiptId", + "[attr.data-return-item-id]": "returnProcess()?.returnItem?.id", }, }) export class ReturnSummaryItemComponent { EligibleForReturnState = EligibleForReturnState; #returnProcessService = inject(ReturnProcessService); - #router = inject(Router); - #activatedRoute = inject(ActivatedRoute); + + /** Angular Location service for navigation */ + location = inject(Location); /** The return process object containing all information about the return */ returnProcess = input.required(); + /** The status of the return items and print receipt operation */ + returnItemsAndPrintReciptPending = input(false); + /** * Computes whether the current return process is eligible for return. * @@ -149,8 +153,4 @@ export class ReturnSummaryItemComponent { // remove duplicates return Array.from(new Set(result)); }); - - navigateBack() { - this.#router.navigate(['..'], { relativeTo: this.#activatedRoute }); - } } diff --git a/libs/oms/feature/return-summary/src/lib/return-summary.component.html b/libs/oms/feature/return-summary/src/lib/return-summary.component.html index e5698ed3d..933686721 100644 --- a/libs/oms/feature/return-summary/src/lib/return-summary.component.html +++ b/libs/oms/feature/return-summary/src/lib/return-summary.component.html @@ -3,6 +3,7 @@ color="tertiary" size="small" class="px-[0.875rem] py-1 min-w-0 bg-white gap-1 absolute top-0 left-0" + [disabled]="returnItemsAndPrintReciptStatusPending()" (click)="location.back()" > @@ -28,19 +29,22 @@ data-which="return-process-item" [attr.data-item-id]="item.id" [attr.data-item-category]="item.productCategory" + [returnItemsAndPrintReciptPending]=" + returnItemsAndPrintReciptStatusPending() + " > }
- @if (returnItemsAndPrintReciptStatus() !== 'success') { + @if (returnItemsAndPrintReciptStatus() !== "success") {