mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
Merged PR 1942: feat(remission-list, search-item-to-remit-dialog): simplify dialog flow by re...
feat(remission-list, search-item-to-remit-dialog): simplify dialog flow by removing conditional views Refactor the search item to remit dialog to use a dedicated quantity and reason dialog instead of conditional views within the main dialog. This change improves user experience by providing clearer navigation and better separation of concerns. Key changes: - Remove item signal and conditional template logic from SearchItemToRemitDialogComponent - Create new SelectRemiQuantityAndReasonDialogComponent for quantity/reason selection - Update SearchItemToRemitComponent to open quantity dialog instead of setting item state - Simplify dialog data interface by removing isDepartment property - Improve stock filtering logic to show only items with available stock - Fix import path for QuantityAndReason interface This refactor eliminates complex state management within the dialog and provides a more intuitive user flow with dedicated dialogs for each step. Ref: #5326
This commit is contained in:
committed by
Lorenz Hilpert
parent
8cf80a60a0
commit
0ca58fe1bf
@@ -418,7 +418,6 @@ export class RemissionListComponent {
|
||||
this.searchItemToRemitDialog({
|
||||
data: {
|
||||
searchTerm,
|
||||
isDepartment: this.isDepartment(),
|
||||
},
|
||||
}).closed.subscribe(async (result) => {
|
||||
if (result) {
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
DropdownButtonComponent,
|
||||
DropdownOptionComponent,
|
||||
} from '@isa/ui/input-controls';
|
||||
import { QuantityAndReason } from './select-remi-quantity-and-reason.component';
|
||||
import { QuantityAndReason } from './select-remi-quantity-and-reason-dialog.component';
|
||||
import { ReturnValue } from '@isa/common/data-access';
|
||||
import { provideIcons } from '@ng-icons/core';
|
||||
import { isaActionChevronDown, isaActionChevronUp } from '@isa/icons';
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
@if (item()) {
|
||||
<remi-select-remi-quantity-and-reason></remi-select-remi-quantity-and-reason>
|
||||
} @else {
|
||||
<button
|
||||
class="absolute top-4 right-[1.33rem]"
|
||||
type="button"
|
||||
uiTextButton
|
||||
size="small"
|
||||
color="subtle"
|
||||
(click)="close(undefined)"
|
||||
tabindex="-1"
|
||||
data-what="button"
|
||||
data-which="close-dialog"
|
||||
>
|
||||
Schließen
|
||||
</button>
|
||||
<remi-search-item-to-remit-list></remi-search-item-to-remit-list>
|
||||
}
|
||||
<button
|
||||
class="absolute top-4 right-[1.33rem]"
|
||||
type="button"
|
||||
uiTextButton
|
||||
size="small"
|
||||
color="subtle"
|
||||
(click)="close(undefined)"
|
||||
tabindex="-1"
|
||||
data-what="button"
|
||||
data-which="close-dialog"
|
||||
>
|
||||
Schließen
|
||||
</button>
|
||||
<remi-search-item-to-remit-list></remi-search-item-to-remit-list>
|
||||
|
||||
@@ -1,33 +1,23 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
effect,
|
||||
isSignal,
|
||||
linkedSignal,
|
||||
signal,
|
||||
Signal,
|
||||
} from '@angular/core';
|
||||
import { DialogContentDirective, NumberInputValidation } from '@isa/ui/dialog';
|
||||
import { Item } from '@isa/catalogue/data-access';
|
||||
import { TextButtonComponent } from '@isa/ui/buttons';
|
||||
import { provideIcons } from '@ng-icons/core';
|
||||
import { isaActionSearch } from '@isa/icons';
|
||||
import { SearchItemToRemitListComponent } from './search-item-to-remit-list.component';
|
||||
import { SelectRemiQuantityAndReasonComponent } from './select-remi-quantity-and-reason.component';
|
||||
import { Validators } from '@angular/forms';
|
||||
import { ReturnSuggestion, ReturnItem } from '@isa/remission/data-access';
|
||||
import { ReturnItem } from '@isa/remission/data-access';
|
||||
|
||||
export type SearchItemToRemitDialogData = {
|
||||
searchTerm: string | Signal<string>;
|
||||
isDepartment: boolean;
|
||||
};
|
||||
|
||||
export type SearchItemToRemitDialogResult =
|
||||
SearchItemToRemitDialogData extends { isDepartment: infer D }
|
||||
? D extends true
|
||||
? ReturnSuggestion
|
||||
: ReturnItem
|
||||
: never;
|
||||
// #5273, #4768 Fix - Nur ReturnItems sind zugelassen und dürfen zur Pflichtremission hinzugefügt werden
|
||||
export type SearchItemToRemitDialogResult = ReturnItem;
|
||||
|
||||
@Component({
|
||||
selector: 'remi-search-item-to-remit-dialog',
|
||||
@@ -35,11 +25,7 @@ export type SearchItemToRemitDialogResult =
|
||||
styleUrls: ['./search-item-to-remit-dialog.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
imports: [
|
||||
TextButtonComponent,
|
||||
SearchItemToRemitListComponent,
|
||||
SelectRemiQuantityAndReasonComponent,
|
||||
],
|
||||
imports: [TextButtonComponent, SearchItemToRemitListComponent],
|
||||
providers: [provideIcons({ isaActionSearch })],
|
||||
})
|
||||
export class SearchItemToRemitDialogComponent extends DialogContentDirective<
|
||||
@@ -51,35 +37,4 @@ export class SearchItemToRemitDialogComponent extends DialogContentDirective<
|
||||
? this.data.searchTerm()
|
||||
: this.data.searchTerm,
|
||||
);
|
||||
|
||||
item = signal<Item | undefined>(undefined);
|
||||
|
||||
itemEffect = effect(() => {
|
||||
const item = this.item();
|
||||
this.dialogRef.updateSize(item ? '36rem' : 'auto');
|
||||
|
||||
if (item) {
|
||||
this.dialog.title.set(`Dieser Artikel steht nicht auf der Remi Liste`);
|
||||
} else {
|
||||
this.dialog.title.set(undefined);
|
||||
}
|
||||
});
|
||||
|
||||
quantityValidators: NumberInputValidation[] = [
|
||||
{
|
||||
errorKey: 'required',
|
||||
inputValidator: Validators.required,
|
||||
errorText: 'Bitte geben Sie eine Menge an.',
|
||||
},
|
||||
{
|
||||
errorKey: 'min',
|
||||
inputValidator: Validators.min(1),
|
||||
errorText: 'Die Menge muss mindestens 1 sein.',
|
||||
},
|
||||
{
|
||||
errorKey: 'max',
|
||||
inputValidator: Validators.max(1000),
|
||||
errorText: 'Die Menge darf höchstens 1000 sein.',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
name="isaActionSearch"
|
||||
color="brand"
|
||||
(click)="triggerSearch()"
|
||||
[pending]="searchResource.isLoading()"
|
||||
[pending]="searchResource.isLoading() || inStockResource.isLoading()"
|
||||
data-what="button"
|
||||
data-which="search-submit"
|
||||
></ui-icon-button>
|
||||
@@ -34,24 +34,23 @@
|
||||
<ng-icon size="1.5rem" name="isaOtherInfo"></ng-icon>
|
||||
</button>
|
||||
</p>
|
||||
<div class="overflow-y-auto">
|
||||
<div class="overflow-y-auto overflow-x-hidden">
|
||||
@if (searchResource.value()?.result; as items) {
|
||||
@for (item of items; track item.id) {
|
||||
@for (item of availableSearchResults(); track item.id) {
|
||||
@defer {
|
||||
@let inStock = getAvailableStockForItem(item);
|
||||
@if (inStock > 0) {
|
||||
<remi-search-item-to-remit
|
||||
[item]="item"
|
||||
[inStock]="inStock"
|
||||
data-what="list-item"
|
||||
data-which="search-result"
|
||||
[attr.data-item-id]="item.id"
|
||||
></remi-search-item-to-remit>
|
||||
}
|
||||
<remi-search-item-to-remit
|
||||
[item]="item"
|
||||
[inStock]="getAvailableStockForItem(item)"
|
||||
data-what="list-item"
|
||||
data-which="search-result"
|
||||
[attr.data-item-id]="item.id"
|
||||
></remi-search-item-to-remit>
|
||||
}
|
||||
}
|
||||
}
|
||||
@if (!hasItems() && !searchResource.isLoading()) {
|
||||
@if (
|
||||
!hasItems() && !searchResource.isLoading() && !inStockResource.isLoading()
|
||||
) {
|
||||
<ui-empty-state
|
||||
class="w-full justify-self-center"
|
||||
title="Keine Suchergebnisse"
|
||||
|
||||
@@ -57,6 +57,14 @@ export class SearchItemToRemitListComponent implements OnInit {
|
||||
|
||||
searchParams = signal<SearchByTermInput | undefined>(undefined);
|
||||
|
||||
availableSearchResults = computed(() => {
|
||||
return (
|
||||
this.searchResource.value()?.result?.filter((item) => {
|
||||
return this.getAvailableStockForItem(item) > 0;
|
||||
}) ?? []
|
||||
);
|
||||
});
|
||||
|
||||
inStockResource = createInStockResource(() => {
|
||||
return {
|
||||
itemIds:
|
||||
@@ -69,7 +77,7 @@ export class SearchItemToRemitListComponent implements OnInit {
|
||||
inStockResponseValue = computed(() => this.inStockResource.value());
|
||||
|
||||
hasItems = computed(() => {
|
||||
return (this.searchResource.value()?.result?.length ?? 0) > 0;
|
||||
return (this.availableSearchResults()?.length ?? 0) > 0;
|
||||
});
|
||||
|
||||
stockInfoMap = computed(() => {
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
type="button"
|
||||
uiTextButton
|
||||
color="strong"
|
||||
(click)="host.item.set(item())"
|
||||
(click)="openQuantityAndReasonDialog()"
|
||||
>
|
||||
Remimenge auswählen
|
||||
</button>
|
||||
|
||||
@@ -10,6 +10,9 @@ import { ProductInfoComponent } from '@isa/remission/shared/product';
|
||||
import { TextButtonComponent } from '@isa/ui/buttons';
|
||||
import { Breakpoint, breakpoint } from '@isa/ui/layout';
|
||||
import { SearchItemToRemitDialogComponent } from './search-item-to-remit-dialog.component';
|
||||
import { injectDialog } from '@isa/ui/dialog';
|
||||
import { SelectRemiQuantityAndReasonDialogComponent } from './select-remi-quantity-and-reason-dialog.component';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'remi-search-item-to-remit',
|
||||
@@ -20,6 +23,9 @@ import { SearchItemToRemitDialogComponent } from './search-item-to-remit-dialog.
|
||||
})
|
||||
export class SearchItemToRemitComponent {
|
||||
host = inject(SearchItemToRemitDialogComponent);
|
||||
quantityAndReasonDialog = injectDialog(
|
||||
SelectRemiQuantityAndReasonDialogComponent,
|
||||
);
|
||||
|
||||
item = input.required<Item>();
|
||||
inStock = input.required<number>();
|
||||
@@ -29,4 +35,22 @@ export class SearchItemToRemitComponent {
|
||||
productInfoOrientation = computed(() => {
|
||||
return this.desktopBreakpoint() ? 'vertical' : 'horizontal';
|
||||
});
|
||||
|
||||
async openQuantityAndReasonDialog() {
|
||||
if (this.item()) {
|
||||
const dialogRef = this.quantityAndReasonDialog({
|
||||
title: 'Dieser Artikel steht nicht auf der Remi Liste',
|
||||
data: {
|
||||
item: this.item(),
|
||||
inStock: this.inStock(),
|
||||
},
|
||||
width: '36rem',
|
||||
});
|
||||
const dialogResult = await firstValueFrom(dialogRef.closed);
|
||||
|
||||
if (dialogResult) {
|
||||
this.host.close(dialogResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,84 +1,94 @@
|
||||
<p class="text-isa-neutral-600 isa-text-body-1-regular">
|
||||
Wie viele Exemplare können remittiert werden?
|
||||
</p>
|
||||
<div class="flex flex-col gap-4">
|
||||
@for (
|
||||
quantityAndReason of quantitiesAndResons();
|
||||
track $index;
|
||||
let i = $index
|
||||
) {
|
||||
<div class="flex items-center gap-1">
|
||||
<remi-quantity-and-reason-item
|
||||
[position]="$index + 1"
|
||||
[quantityAndReason]="quantityAndReason"
|
||||
(quantityAndReasonChange)="setQuantityAndReason($index, $event)"
|
||||
class="flex-1"
|
||||
data-what="component"
|
||||
data-which="quantity-reason-item"
|
||||
[attr.data-position]="$index + 1"
|
||||
></remi-quantity-and-reason-item>
|
||||
@if (i > 0) {
|
||||
<ui-icon-button
|
||||
type="button"
|
||||
(click)="removeQuantityReasonItem($index)"
|
||||
data-what="button"
|
||||
data-which="remove-quantity"
|
||||
[attr.data-position]="$index + 1"
|
||||
name="isaActionClose"
|
||||
color="neutral"
|
||||
></ui-icon-button>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
class="flex items-center gap-2 -ml-5"
|
||||
uiTextButton
|
||||
color="strong"
|
||||
(click)="addQuantityReasonItem()"
|
||||
data-what="button"
|
||||
data-which="add-quantity"
|
||||
>
|
||||
<ng-icon name="isaActionPlus" size="1.5rem"></ng-icon>
|
||||
<div>Menge hinzufügen</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="text-isa-accent-red isa-text-body-1-regular">
|
||||
<span>
|
||||
@if (canReturnErrors(); as errors) {
|
||||
@for (error of errors; track $index) {
|
||||
{{ error }}
|
||||
}
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
color="secondary"
|
||||
size="large"
|
||||
uiButton
|
||||
(click)="host.item.set(undefined)"
|
||||
data-what="button"
|
||||
data-which="back"
|
||||
>
|
||||
Zurück
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
color="primary"
|
||||
size="large"
|
||||
uiButton
|
||||
[pending]="canAddToRemiListResource.isLoading()"
|
||||
[disabled]="canAddToRemiListResource.isLoading() || canReturn() === false"
|
||||
(click)="addToRemiList()"
|
||||
data-what="button"
|
||||
data-which="save-remission"
|
||||
>
|
||||
Speichern
|
||||
</button>
|
||||
</div>
|
||||
<remi-product-info
|
||||
[item]="{
|
||||
product: data.item.product,
|
||||
retailPrice: data.item.catalogAvailability.price,
|
||||
}"
|
||||
></remi-product-info>
|
||||
<div class="text-isa-neutral-900 flex flex-row items-center justify-end gap-8">
|
||||
<span class="isa-text-body-2-regular">Aktueller Bestand</span>
|
||||
<span class="isa-text-body-2-bold">{{ data.inStock }}x</span>
|
||||
</div>
|
||||
<p class="text-isa-neutral-600 isa-text-body-1-regular">
|
||||
Wie viele Exemplare können remittiert werden?
|
||||
</p>
|
||||
<div class="flex flex-col gap-4">
|
||||
@for (
|
||||
quantityAndReason of quantitiesAndResons();
|
||||
track $index;
|
||||
let i = $index
|
||||
) {
|
||||
<div class="flex items-center gap-1">
|
||||
<remi-quantity-and-reason-item
|
||||
[position]="$index + 1"
|
||||
[quantityAndReason]="quantityAndReason"
|
||||
(quantityAndReasonChange)="setQuantityAndReason($index, $event)"
|
||||
class="flex-1"
|
||||
data-what="component"
|
||||
data-which="quantity-reason-item"
|
||||
[attr.data-position]="$index + 1"
|
||||
></remi-quantity-and-reason-item>
|
||||
@if (i > 0) {
|
||||
<ui-icon-button
|
||||
type="button"
|
||||
(click)="removeQuantityReasonItem($index)"
|
||||
data-what="button"
|
||||
data-which="remove-quantity"
|
||||
[attr.data-position]="$index + 1"
|
||||
name="isaActionClose"
|
||||
color="neutral"
|
||||
></ui-icon-button>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
class="flex items-center gap-2 -ml-5"
|
||||
uiTextButton
|
||||
color="strong"
|
||||
(click)="addQuantityReasonItem()"
|
||||
data-what="button"
|
||||
data-which="add-quantity"
|
||||
>
|
||||
<ng-icon name="isaActionPlus" size="1.5rem"></ng-icon>
|
||||
<div>Menge hinzufügen</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="text-isa-accent-red isa-text-body-1-regular">
|
||||
<span>
|
||||
@if (canReturnErrors(); as errors) {
|
||||
@for (error of errors; track $index) {
|
||||
{{ error }}
|
||||
}
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 items-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
color="secondary"
|
||||
size="large"
|
||||
uiButton
|
||||
(click)="close(undefined)"
|
||||
data-what="button"
|
||||
data-which="back"
|
||||
>
|
||||
Zurück
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
color="primary"
|
||||
size="large"
|
||||
uiButton
|
||||
[pending]="canAddToRemiListResource.isLoading()"
|
||||
[disabled]="canAddToRemiListResource.isLoading() || canReturn() === false"
|
||||
(click)="addToRemiList()"
|
||||
data-what="button"
|
||||
data-which="save-remission"
|
||||
>
|
||||
Speichern
|
||||
</button>
|
||||
</div>
|
||||
@@ -0,0 +1,3 @@
|
||||
:host {
|
||||
@apply grid grid-flow-row gap-6 h-full;
|
||||
}
|
||||
@@ -1,184 +1,196 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
computed,
|
||||
inject,
|
||||
model,
|
||||
resource,
|
||||
} from '@angular/core';
|
||||
import { SearchItemToRemitDialogComponent } from './search-item-to-remit-dialog.component';
|
||||
import { QuantityAndReasonItemComponent } from './quantity-and-reason-item.component';
|
||||
import {
|
||||
ButtonComponent,
|
||||
TextButtonComponent,
|
||||
IconButtonComponent,
|
||||
} from '@isa/ui/buttons';
|
||||
import { NgIcon, provideIcons } from '@ng-icons/core';
|
||||
import { isaActionPlus, isaActionClose } from '@isa/icons';
|
||||
import {
|
||||
RemissionSearchService,
|
||||
RemissionStore,
|
||||
ReturnItem,
|
||||
ReturnSuggestion,
|
||||
} from '@isa/remission/data-access';
|
||||
import { injectFeedbackDialog } from '@isa/ui/dialog';
|
||||
import { BatchResponseArgs } from '@isa/common/data-access';
|
||||
|
||||
export interface QuantityAndReason {
|
||||
quantity: number;
|
||||
reason: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'remi-select-remi-quantity-and-reason',
|
||||
templateUrl: './select-remi-quantity-and-reason.component.html',
|
||||
styleUrls: ['./select-remi-quantity-and-reason.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
imports: [
|
||||
QuantityAndReasonItemComponent,
|
||||
TextButtonComponent,
|
||||
NgIcon,
|
||||
ButtonComponent,
|
||||
IconButtonComponent,
|
||||
],
|
||||
providers: [provideIcons({ isaActionPlus, isaActionClose })],
|
||||
})
|
||||
export class SelectRemiQuantityAndReasonComponent {
|
||||
#remiService = inject(RemissionSearchService);
|
||||
#remiStore = inject(RemissionStore);
|
||||
#feedbackDialog = injectFeedbackDialog();
|
||||
host = inject(SearchItemToRemitDialogComponent);
|
||||
|
||||
initialItem: QuantityAndReason = { quantity: 0, reason: '' };
|
||||
|
||||
quantitiesAndResons = model<QuantityAndReason[]>([this.initialItem]);
|
||||
|
||||
addQuantityReasonItem(): void {
|
||||
this.quantitiesAndResons.update((items) => [...items, this.initialItem]);
|
||||
}
|
||||
|
||||
removeQuantityReasonItem(position: number): void {
|
||||
const currentItems = this.quantitiesAndResons();
|
||||
if (currentItems.length > 1) {
|
||||
this.quantitiesAndResons.update((items) =>
|
||||
items.filter((_, index) => index !== position),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
setQuantityAndReason(position: number, qar: QuantityAndReason): void {
|
||||
this.quantitiesAndResons.update((items) => {
|
||||
const newItems = [...items];
|
||||
newItems[position] = qar;
|
||||
return newItems;
|
||||
});
|
||||
}
|
||||
|
||||
params = computed(() => {
|
||||
const items = this.quantitiesAndResons();
|
||||
const item = this.host.item();
|
||||
|
||||
if (!item) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return items.map((qar) => ({
|
||||
item,
|
||||
quantity: qar.quantity,
|
||||
reason: qar.reason,
|
||||
}));
|
||||
});
|
||||
|
||||
canAddToRemiListResource = resource({
|
||||
params: this.params,
|
||||
loader: async ({ params, abortSignal }) => {
|
||||
if (
|
||||
!this.host.item() ||
|
||||
params.some((p) => !p.reason) ||
|
||||
params.some((p) => !p.quantity)
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const maxQuantityErrors = params.filter((p) => !(p.quantity <= 999));
|
||||
if (maxQuantityErrors.length > 0) {
|
||||
const errRes: BatchResponseArgs<ReturnItem> = {
|
||||
completed: false,
|
||||
error: true,
|
||||
total: maxQuantityErrors.length,
|
||||
invalidProperties: {
|
||||
quantity: 'Die Menge darf maximal 999 sein.',
|
||||
},
|
||||
};
|
||||
return errRes;
|
||||
}
|
||||
|
||||
return this.#remiService.canAddItemToRemiList(params, abortSignal);
|
||||
},
|
||||
});
|
||||
|
||||
canReturn = computed(() => {
|
||||
const results = this.canAddToRemiListResource.value();
|
||||
|
||||
if (!results) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (results.failed && results.failed.length > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
results.successful &&
|
||||
results.successful.length === this.quantitiesAndResons().length
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
canReturnErrors = computed(() => {
|
||||
const results = this.canAddToRemiListResource.value();
|
||||
if (results?.invalidProperties) {
|
||||
return Object.values(results.invalidProperties);
|
||||
}
|
||||
|
||||
if (!results?.failed) {
|
||||
return [];
|
||||
}
|
||||
return results.failed.map((item) =>
|
||||
item.invalidProperties
|
||||
? Object.values(item.invalidProperties).join(', ')
|
||||
: [],
|
||||
) as string[];
|
||||
});
|
||||
|
||||
async addToRemiList() {
|
||||
const canAddValue = this.canAddToRemiListResource.value();
|
||||
|
||||
if (!canAddValue) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (canAddValue.failed?.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// #5273, #4768 Fix - Items dürfen nur zur Pflichtremission hinzugefügt werden
|
||||
const result: Array<ReturnItem> = await this.#remiService.addToList(
|
||||
this.params(),
|
||||
);
|
||||
|
||||
this.#feedbackDialog({
|
||||
data: {
|
||||
message: this.#remiStore.remissionStarted()
|
||||
? 'Wurde zum Warenbegleitschein hinzugefügt'
|
||||
: 'Wurde zur Remi Liste hinzugefügt',
|
||||
},
|
||||
});
|
||||
|
||||
this.host.close(result);
|
||||
}
|
||||
}
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
computed,
|
||||
inject,
|
||||
model,
|
||||
resource,
|
||||
} from '@angular/core';
|
||||
import { QuantityAndReasonItemComponent } from './quantity-and-reason-item.component';
|
||||
import {
|
||||
ButtonComponent,
|
||||
TextButtonComponent,
|
||||
IconButtonComponent,
|
||||
} from '@isa/ui/buttons';
|
||||
import { NgIcon, provideIcons } from '@ng-icons/core';
|
||||
import { isaActionPlus, isaActionClose } from '@isa/icons';
|
||||
import {
|
||||
RemissionSearchService,
|
||||
RemissionStore,
|
||||
ReturnItem,
|
||||
} from '@isa/remission/data-access';
|
||||
import { DialogContentDirective, injectFeedbackDialog } from '@isa/ui/dialog';
|
||||
import { BatchResponseArgs } from '@isa/common/data-access';
|
||||
import { Item } from '@isa/catalogue/data-access';
|
||||
import { ProductInfoComponent } from '@isa/remission/shared/product';
|
||||
|
||||
export type SelectRemiQuantityAndReasonDialogData = {
|
||||
item: Item;
|
||||
inStock: number;
|
||||
};
|
||||
|
||||
export type SelectRemiQuantityAndReasonDialogResult =
|
||||
| undefined
|
||||
| Array<ReturnItem>;
|
||||
|
||||
export interface QuantityAndReason {
|
||||
quantity: number;
|
||||
reason: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'remi-select-remi-quantity-and-reason-dialog',
|
||||
templateUrl: './select-remi-quantity-and-reason-dialog.component.html',
|
||||
styleUrls: ['./select-remi-quantity-and-reason-dialog.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: true,
|
||||
imports: [
|
||||
QuantityAndReasonItemComponent,
|
||||
TextButtonComponent,
|
||||
NgIcon,
|
||||
ButtonComponent,
|
||||
IconButtonComponent,
|
||||
ProductInfoComponent,
|
||||
],
|
||||
providers: [provideIcons({ isaActionPlus, isaActionClose })],
|
||||
})
|
||||
export class SelectRemiQuantityAndReasonDialogComponent extends DialogContentDirective<
|
||||
SelectRemiQuantityAndReasonDialogData,
|
||||
SelectRemiQuantityAndReasonDialogResult
|
||||
> {
|
||||
#remiService = inject(RemissionSearchService);
|
||||
#remiStore = inject(RemissionStore);
|
||||
#feedbackDialog = injectFeedbackDialog();
|
||||
|
||||
initialItem: QuantityAndReason = { quantity: 0, reason: '' };
|
||||
|
||||
quantitiesAndResons = model<QuantityAndReason[]>([this.initialItem]);
|
||||
|
||||
addQuantityReasonItem(): void {
|
||||
this.quantitiesAndResons.update((items) => [...items, this.initialItem]);
|
||||
}
|
||||
|
||||
removeQuantityReasonItem(position: number): void {
|
||||
const currentItems = this.quantitiesAndResons();
|
||||
if (currentItems.length > 1) {
|
||||
this.quantitiesAndResons.update((items) =>
|
||||
items.filter((_, index) => index !== position),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
setQuantityAndReason(position: number, qar: QuantityAndReason): void {
|
||||
this.quantitiesAndResons.update((items) => {
|
||||
const newItems = [...items];
|
||||
newItems[position] = qar;
|
||||
return newItems;
|
||||
});
|
||||
}
|
||||
|
||||
params = computed(() => {
|
||||
const items = this.quantitiesAndResons();
|
||||
const item = this.data.item;
|
||||
|
||||
if (!item) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return items.map((qar) => ({
|
||||
item,
|
||||
quantity: qar.quantity,
|
||||
reason: qar.reason,
|
||||
}));
|
||||
});
|
||||
|
||||
canAddToRemiListResource = resource({
|
||||
params: this.params,
|
||||
loader: async ({ params, abortSignal }) => {
|
||||
if (
|
||||
!this.data.item ||
|
||||
params.some((p) => !p.reason) ||
|
||||
params.some((p) => !p.quantity)
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const maxQuantityErrors = params.filter((p) => !(p.quantity <= 999));
|
||||
if (maxQuantityErrors.length > 0) {
|
||||
const errRes: BatchResponseArgs<ReturnItem> = {
|
||||
completed: false,
|
||||
error: true,
|
||||
total: maxQuantityErrors.length,
|
||||
invalidProperties: {
|
||||
quantity: 'Die Menge darf maximal 999 sein.',
|
||||
},
|
||||
};
|
||||
return errRes;
|
||||
}
|
||||
|
||||
return this.#remiService.canAddItemToRemiList(params, abortSignal);
|
||||
},
|
||||
});
|
||||
|
||||
canReturn = computed(() => {
|
||||
const results = this.canAddToRemiListResource.value();
|
||||
|
||||
if (!results) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (results.failed && results.failed.length > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
results.successful &&
|
||||
results.successful.length === this.quantitiesAndResons().length
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
canReturnErrors = computed(() => {
|
||||
const results = this.canAddToRemiListResource.value();
|
||||
if (results?.invalidProperties) {
|
||||
return Object.values(results.invalidProperties);
|
||||
}
|
||||
|
||||
if (!results?.failed) {
|
||||
return [];
|
||||
}
|
||||
return results.failed.map((item) =>
|
||||
item.invalidProperties
|
||||
? Object.values(item.invalidProperties).join(', ')
|
||||
: [],
|
||||
) as string[];
|
||||
});
|
||||
|
||||
async addToRemiList() {
|
||||
const canAddValue = this.canAddToRemiListResource.value();
|
||||
|
||||
if (!canAddValue) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (canAddValue.failed?.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// #5273, #4768 Fix - Items dürfen nur zur Pflichtremission hinzugefügt werden
|
||||
const result: Array<ReturnItem> = await this.#remiService.addToList(
|
||||
this.params(),
|
||||
);
|
||||
|
||||
this.#feedbackDialog({
|
||||
data: {
|
||||
message: this.#remiStore.remissionStarted()
|
||||
? 'Wurde zum Warenbegleitschein hinzugefügt'
|
||||
: 'Wurde zur Remi Liste hinzugefügt',
|
||||
},
|
||||
});
|
||||
|
||||
this.close(result);
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
:host {
|
||||
@apply grid grid-flow-row gap-6;
|
||||
}
|
||||
Reference in New Issue
Block a user