mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
feat(remission-list, remission-shared-product-stock-info): implement product stock info display Add product stock information to the remission list and shared product components. This enhances user visibility into current stock levels directly within remission-related views, improving workflow efficiency and reducing the need for context switching. Ref: #4769, #5194
This commit is contained in:
committed by
Lorenz Hilpert
parent
b28c204f23
commit
b7e7155577
@@ -1,5 +1,9 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ItemDTO, ListResponseArgsOfItemDTO, SearchService } from '@generated/swagger/cat-search-api';
|
||||
import {
|
||||
ItemDTO,
|
||||
ListResponseArgsOfItemDTO,
|
||||
SearchService,
|
||||
} from '@generated/swagger/cat-search-api';
|
||||
import {
|
||||
RemiService,
|
||||
StockService,
|
||||
@@ -18,7 +22,11 @@ import { memorize } from '@utils/common';
|
||||
import { Observable, of, throwError } from 'rxjs';
|
||||
import { catchError, map, shareReplay, switchMap } from 'rxjs/operators';
|
||||
import { RemissionListItem } from './defs';
|
||||
import { fromItemDto, mapFromReturnItemDTO, mapFromReturnSuggestionDTO } from './mappings';
|
||||
import {
|
||||
fromItemDto,
|
||||
mapFromReturnItemDTO,
|
||||
mapFromReturnSuggestionDTO,
|
||||
} from './mappings';
|
||||
import { Logger } from '@core/logger';
|
||||
import { RemissionPlacementType } from '@domain/remission';
|
||||
|
||||
@@ -204,7 +212,10 @@ export class DomainRemissionService {
|
||||
);
|
||||
}
|
||||
|
||||
getStockInformation(items: RemissionListItem[], recalculate: boolean = false) {
|
||||
getStockInformation(
|
||||
items: RemissionListItem[],
|
||||
recalculate: boolean = false,
|
||||
) {
|
||||
return this.getCurrentStock().pipe(
|
||||
switchMap((stock) =>
|
||||
this._stockService
|
||||
@@ -218,7 +229,8 @@ export class DomainRemissionService {
|
||||
map((res) => {
|
||||
const o = items.map((item) => {
|
||||
const stockInfo = res?.result?.find(
|
||||
(stockInfo) => stockInfo.itemId === +item.dto.product.catalogProductNumber,
|
||||
(stockInfo) =>
|
||||
stockInfo.itemId === +item.dto.product.catalogProductNumber,
|
||||
);
|
||||
|
||||
if (!stockInfo) {
|
||||
@@ -231,7 +243,8 @@ export class DomainRemissionService {
|
||||
return { ...item, ...defaultStockData };
|
||||
}
|
||||
|
||||
const availableStock = stockInfo.inStock - stockInfo.removedFromStock;
|
||||
const availableStock =
|
||||
stockInfo.inStock - stockInfo.removedFromStock;
|
||||
const inStock = availableStock < 0 ? 0 : availableStock;
|
||||
|
||||
let { remainingQuantity, remissionQuantity } = item;
|
||||
@@ -249,7 +262,12 @@ export class DomainRemissionService {
|
||||
}
|
||||
}
|
||||
|
||||
return { ...item, remainingQuantity, remissionQuantity, inStock };
|
||||
return {
|
||||
...item,
|
||||
remainingQuantity,
|
||||
remissionQuantity,
|
||||
inStock,
|
||||
};
|
||||
});
|
||||
|
||||
return o;
|
||||
@@ -259,7 +277,10 @@ export class DomainRemissionService {
|
||||
);
|
||||
}
|
||||
|
||||
getRequiredCapacities(params: { departments?: string[]; supplierId: number }) {
|
||||
getRequiredCapacities(params: {
|
||||
departments?: string[];
|
||||
supplierId: number;
|
||||
}) {
|
||||
return this.getCurrentStock().pipe(
|
||||
switchMap((stock) =>
|
||||
this._remiService
|
||||
@@ -301,13 +322,18 @@ export class DomainRemissionService {
|
||||
);
|
||||
}
|
||||
|
||||
canAddReturnItem(item: ReturnItemDTO): Observable<BatchResponseArgsOfReturnItemDTOAndReturnItemDTO> {
|
||||
canAddReturnItem(
|
||||
item: ReturnItemDTO,
|
||||
): Observable<BatchResponseArgsOfReturnItemDTOAndReturnItemDTO> {
|
||||
return this._remiService.RemiCanAddReturnItem({
|
||||
data: [item],
|
||||
});
|
||||
}
|
||||
|
||||
async createReturn(supplierId: number, returnGroup?: string): Promise<ReturnDTO> {
|
||||
async createReturn(
|
||||
supplierId: number,
|
||||
returnGroup?: string,
|
||||
): Promise<ReturnDTO> {
|
||||
const response = await this._returnService
|
||||
.ReturnCreateReturn({
|
||||
data: {
|
||||
@@ -343,7 +369,10 @@ export class DomainRemissionService {
|
||||
.toPromise();
|
||||
}
|
||||
|
||||
getReturns(params: { start?: Date; returncompleted: boolean }): Observable<ReturnDTO[]> {
|
||||
getReturns(params: {
|
||||
start?: Date;
|
||||
returncompleted: boolean;
|
||||
}): Observable<ReturnDTO[]> {
|
||||
const queryToken: ReturnQueryTokenDTO = {
|
||||
start: params.start?.toISOString(),
|
||||
filter: {
|
||||
@@ -360,13 +389,20 @@ export class DomainRemissionService {
|
||||
});
|
||||
|
||||
return this.getCurrentStock().pipe(
|
||||
switchMap((stock) => this._returnService.ReturnQueryReturns({ stockId: stock.id, queryToken })),
|
||||
switchMap((stock) =>
|
||||
this._returnService.ReturnQueryReturns({
|
||||
stockId: stock.id,
|
||||
queryToken,
|
||||
}),
|
||||
),
|
||||
map((res) => res.result),
|
||||
);
|
||||
}
|
||||
|
||||
getReturn(returnId: number): Observable<ReturnDTO> {
|
||||
return this._returnService.ReturnGetReturn({ returnId, eagerLoading: 3 }).pipe(map((res) => res.result));
|
||||
return this._returnService
|
||||
.ReturnGetReturn({ returnId, eagerLoading: 3 })
|
||||
.pipe(map((res) => res.result));
|
||||
}
|
||||
|
||||
async deleteReturn(returnId: number) {
|
||||
@@ -393,7 +429,11 @@ export class DomainRemissionService {
|
||||
inStock: number;
|
||||
}) {
|
||||
return this._returnService
|
||||
.ReturnAddReturnItem({ returnId, receiptId, data: { returnItemId, quantity, placementType, inStock } })
|
||||
.ReturnAddReturnItem({
|
||||
returnId,
|
||||
receiptId,
|
||||
data: { returnItemId, quantity, placementType, inStock },
|
||||
})
|
||||
.pipe(map((r) => r.result));
|
||||
}
|
||||
|
||||
@@ -420,7 +460,14 @@ export class DomainRemissionService {
|
||||
.ReturnAddReturnSuggestion({
|
||||
returnId,
|
||||
receiptId,
|
||||
data: { returnSuggestionId, quantity, placementType, inStock, impedimentComment, remainingQuantity },
|
||||
data: {
|
||||
returnSuggestionId,
|
||||
quantity,
|
||||
placementType,
|
||||
inStock,
|
||||
impedimentComment,
|
||||
remainingQuantity,
|
||||
},
|
||||
})
|
||||
.pipe(map((r) => r.result));
|
||||
}
|
||||
@@ -438,18 +485,28 @@ export class DomainRemissionService {
|
||||
receiptId: number;
|
||||
receiptItemId: number;
|
||||
}) {
|
||||
return this._returnService.ReturnRemoveReturnItem({ returnId, receiptItemId, receiptId });
|
||||
return this._returnService.ReturnRemoveReturnItem({
|
||||
returnId,
|
||||
receiptItemId,
|
||||
receiptId,
|
||||
});
|
||||
}
|
||||
|
||||
returnImpediment(itemId: number) {
|
||||
return this._returnService
|
||||
.ReturnReturnItemImpediment({ itemId, data: { comment: 'Produkt nicht gefunden' } })
|
||||
.ReturnReturnItemImpediment({
|
||||
itemId,
|
||||
data: { comment: 'Produkt nicht gefunden' },
|
||||
})
|
||||
.pipe(map((r) => r.result));
|
||||
}
|
||||
|
||||
returnSuggestion(itemId: number) {
|
||||
return this._returnService
|
||||
.ReturnReturnSuggestionImpediment({ itemId, data: { comment: 'Produkt nicht gefunden' } })
|
||||
.ReturnReturnSuggestionImpediment({
|
||||
itemId,
|
||||
data: { comment: 'Produkt nicht gefunden' },
|
||||
})
|
||||
.pipe(map((r) => r.result));
|
||||
}
|
||||
|
||||
@@ -459,7 +516,10 @@ export class DomainRemissionService {
|
||||
* @param receiptNumber Receipt number
|
||||
* @returns ReceiptDTO
|
||||
*/
|
||||
async createReceipt(returnDTO: ReturnDTO, receiptNumber?: string): Promise<ReceiptDTO> {
|
||||
async createReceipt(
|
||||
returnDTO: ReturnDTO,
|
||||
receiptNumber?: string,
|
||||
): Promise<ReceiptDTO> {
|
||||
const stock = await this._getStock();
|
||||
|
||||
const response = await this._returnService
|
||||
@@ -510,7 +570,10 @@ export class DomainRemissionService {
|
||||
return receipt;
|
||||
}
|
||||
|
||||
async completeReceipt(returnId: number, receiptId: number): Promise<ReceiptDTO> {
|
||||
async completeReceipt(
|
||||
returnId: number,
|
||||
receiptId: number,
|
||||
): Promise<ReceiptDTO> {
|
||||
const res = await this._returnService
|
||||
.ReturnFinalizeReceipt({
|
||||
returnId,
|
||||
|
||||
@@ -20,24 +20,42 @@ const meta: Meta<ProductStockInfoComponent> = {
|
||||
}),
|
||||
],
|
||||
args: {
|
||||
stock: 100,
|
||||
stockToRemit: 20,
|
||||
stock: 92,
|
||||
removedFromStock: 0,
|
||||
predefinedReturnQuantity: 4,
|
||||
remainingQuantityInStock: 0,
|
||||
zob: 0,
|
||||
},
|
||||
argTypes: {
|
||||
stock: {
|
||||
control: { type: 'number' },
|
||||
description: 'The current stock of the product.',
|
||||
defaultValue: undefined,
|
||||
defaultValue: 0,
|
||||
},
|
||||
stockToRemit: {
|
||||
removedFromStock: {
|
||||
control: { type: 'number' },
|
||||
description: 'The amount of stock to remit.',
|
||||
defaultValue: undefined,
|
||||
description: 'The amount of stock that has been removed.',
|
||||
defaultValue: 0,
|
||||
},
|
||||
predefinedReturnQuantity: {
|
||||
control: { type: 'number' },
|
||||
description: 'The predefined return quantity for the product.',
|
||||
defaultValue: 0,
|
||||
},
|
||||
remainingQuantityInStock: {
|
||||
control: { type: 'number' },
|
||||
description: 'The remaining quantity in stock after returns.',
|
||||
defaultValue: 0,
|
||||
},
|
||||
zob: {
|
||||
control: { type: 'number' },
|
||||
description: 'Min Stock Category Management Information.',
|
||||
defaultValue: 0,
|
||||
},
|
||||
},
|
||||
render: (args) => ({
|
||||
props: args,
|
||||
template: `<remi-product-stock-info ${argsToTemplate({ info: args })}></remi-product-stock-info>`,
|
||||
template: `<remi-product-stock-info ${argsToTemplate(args)}></remi-product-stock-info>`,
|
||||
}),
|
||||
};
|
||||
|
||||
@@ -46,5 +64,11 @@ export default meta;
|
||||
type Story = StoryObj<ProductStockInfoComponent>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {},
|
||||
args: {
|
||||
stock: 92,
|
||||
removedFromStock: 0,
|
||||
predefinedReturnQuantity: 4,
|
||||
remainingQuantityInStock: 0,
|
||||
zob: 0,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -37,7 +37,10 @@ export class RemissionStockService {
|
||||
return res.result as Stock;
|
||||
}
|
||||
|
||||
async fetchStock(params: FetchStockInStock): Promise<StockInfo[]> {
|
||||
async fetchStock(
|
||||
params: FetchStockInStock,
|
||||
abortSignal?: AbortSignal,
|
||||
): Promise<StockInfo[]> {
|
||||
const parsed = FetchStockInStockSchema.parse(params);
|
||||
|
||||
const req$ = this.#stockService.StockInStock({
|
||||
@@ -45,11 +48,14 @@ export class RemissionStockService {
|
||||
articleIds: parsed.itemIds,
|
||||
});
|
||||
|
||||
if (abortSignal) {
|
||||
req$.pipe(takeUntilAborted(abortSignal));
|
||||
}
|
||||
|
||||
const res = await firstValueFrom(req$);
|
||||
// TODO: Alte Remi Logik beinhaltet diverse Stock Berechnungen (DomainRemissionService -> getStockInformation()) - Das muss später noch beachtet werden
|
||||
|
||||
if (res.error || !res.result) {
|
||||
throw new Error(res.message || 'Failed to fetch InStock data');
|
||||
throw new ResponseArgsError(res);
|
||||
}
|
||||
|
||||
return res.result as StockInfo[];
|
||||
|
||||
@@ -1 +1,20 @@
|
||||
<remi-product-info [item]="item()"></remi-product-info>
|
||||
@let i = item();
|
||||
@let s = stock();
|
||||
<ui-client-row data-what="remission-list-item" [attr.data-which]="i.id">
|
||||
<ui-client-row-content>
|
||||
<remi-product-info
|
||||
[item]="i"
|
||||
[orientation]="'vertical'"
|
||||
></remi-product-info>
|
||||
</ui-client-row-content>
|
||||
<ui-item-row-data> Shelfinfos... (TODO) </ui-item-row-data>
|
||||
<ui-item-row-data>
|
||||
<remi-product-stock-info
|
||||
[predefinedReturnQuantity]="predefinedReturnQuantity()"
|
||||
[remainingQuantityInStock]="i?.remainingQuantityInStock ?? 0"
|
||||
[stock]="s?.inStock ?? 0"
|
||||
[removedFromStock]="s?.removedFromStock ?? 0"
|
||||
[zob]="s?.minStockCategoryManagement ?? 0"
|
||||
></remi-product-stock-info>
|
||||
</ui-item-row-data>
|
||||
</ui-client-row>
|
||||
|
||||
@@ -1,14 +1,49 @@
|
||||
import { ChangeDetectionStrategy, Component, input } from '@angular/core';
|
||||
import { ReturnItem, ReturnSuggestion } from '@isa/remission/data-access';
|
||||
import { ProductInfoComponent } from '@isa/remission/shared/product';
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
computed,
|
||||
input,
|
||||
} from '@angular/core';
|
||||
import {
|
||||
ReturnItem,
|
||||
ReturnSuggestion,
|
||||
StockInfo,
|
||||
} from '@isa/remission/data-access';
|
||||
import {
|
||||
ProductInfoComponent,
|
||||
ProductStockInfoComponent,
|
||||
} from '@isa/remission/shared/product';
|
||||
import { ClientRowImports, ItemRowDataImports } from '@isa/ui/item-rows';
|
||||
|
||||
@Component({
|
||||
selector: 'remission-feature-remission-list-item',
|
||||
templateUrl: './remission-list-item.component.html',
|
||||
styleUrl: './remission-list-item.component.scss',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [ProductInfoComponent],
|
||||
imports: [
|
||||
ProductInfoComponent,
|
||||
ProductStockInfoComponent,
|
||||
ClientRowImports,
|
||||
ItemRowDataImports,
|
||||
],
|
||||
})
|
||||
export class RemissionListItemComponent {
|
||||
item = input.required<ReturnItem | ReturnSuggestion>();
|
||||
stock = input.required<StockInfo>();
|
||||
|
||||
predefinedReturnQuantity = computed(() => {
|
||||
const item = this.item();
|
||||
|
||||
// ReturnSuggestion
|
||||
if ('returnItem' in item && item?.returnItem?.data) {
|
||||
return item.returnItem.data.predefinedReturnQuantity ?? 0;
|
||||
}
|
||||
|
||||
// ReturnItem
|
||||
if ('predefinedReturnQuantity' in item) {
|
||||
return item.predefinedReturnQuantity ?? 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import {
|
||||
RemissionListType,
|
||||
RemissionListTypeKey,
|
||||
RemissionSearchService,
|
||||
} from '@isa/remission/data-access';
|
||||
import {
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
<remission-feature-remission-list-item
|
||||
#listElement
|
||||
[item]="item"
|
||||
[stock]="getStockForItem(item)"
|
||||
></remission-feature-remission-list-item>
|
||||
</a>
|
||||
} @placeholder {
|
||||
|
||||
@@ -16,15 +16,38 @@ import { injectRestoreScrollPosition } from '@isa/utils/scroll-position';
|
||||
import { RemissionStartCardComponent } from './remission-start-card/remission-start-card.component';
|
||||
import { RemissionListSelectComponent } from './remission-list-select/remission-list-select.component';
|
||||
import { toSignal } from '@angular/core/rxjs-interop';
|
||||
import { createRemissionListResource } from './resources';
|
||||
import {
|
||||
createRemissionInStockResource,
|
||||
createRemissionListResource,
|
||||
} from './resources';
|
||||
import { injectRemissionListType } from './injects/inject-remission-list-type';
|
||||
import { RemissionListItemComponent } from './remission-list-item/remission-list-item.component';
|
||||
import { IconButtonComponent } from '@isa/ui/buttons';
|
||||
import {
|
||||
ReturnItem,
|
||||
StockInfo,
|
||||
ReturnSuggestion,
|
||||
} from '@isa/remission/data-access';
|
||||
|
||||
function querySettingsFactory() {
|
||||
return inject(ActivatedRoute).snapshot.data['querySettings'];
|
||||
}
|
||||
|
||||
/**
|
||||
* RemissionListComponent
|
||||
*
|
||||
* Displays and manages a list of remission items with filtering and stock information.
|
||||
* Implements local state using Angular signals and computed properties.
|
||||
* Follows SOLID and Clean Code principles for maintainability and testability.
|
||||
*
|
||||
* @remarks
|
||||
* - Uses OnPush change detection for performance.
|
||||
* - All state is managed locally via signals.
|
||||
* - Filtering is handled via FilterService.
|
||||
* - Stock information is dynamically loaded for visible items.
|
||||
*
|
||||
* @see {@link https://angular.dev/style-guide} for Angular best practices.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'remission-feature-remission-list',
|
||||
templateUrl: './remission-list.component.html',
|
||||
@@ -50,15 +73,41 @@ function querySettingsFactory() {
|
||||
},
|
||||
})
|
||||
export class RemissionListComponent {
|
||||
/**
|
||||
* Activated route instance for accessing route data and params.
|
||||
*/
|
||||
route = inject(ActivatedRoute);
|
||||
|
||||
/**
|
||||
* Signal for the current route URL segments.
|
||||
*/
|
||||
routeUrl = toSignal(this.route.url);
|
||||
|
||||
/**
|
||||
* FilterService instance for managing filter state and queries.
|
||||
* @private
|
||||
*/
|
||||
#filterService = inject(FilterService);
|
||||
|
||||
/**
|
||||
* Restores scroll position when navigating back to this component.
|
||||
*/
|
||||
restoreScrollPosition = injectRestoreScrollPosition();
|
||||
|
||||
/**
|
||||
* Signal containing the current route data snapshot.
|
||||
*/
|
||||
routeData = toSignal(this.route.data);
|
||||
|
||||
/**
|
||||
* Signal representing the currently selected remission list type.
|
||||
*/
|
||||
selectedRemissionListType = injectRemissionListType();
|
||||
|
||||
/**
|
||||
* Resource signal for fetching the remission list based on current filters.
|
||||
* @returns Remission list resource state.
|
||||
*/
|
||||
remissionResource = createRemissionListResource(() => {
|
||||
return {
|
||||
remissionListType: this.selectedRemissionListType(),
|
||||
@@ -66,20 +115,77 @@ export class RemissionListComponent {
|
||||
};
|
||||
});
|
||||
|
||||
responseValue = computed(() => this.remissionResource.value());
|
||||
// TODO (Info): Bei Add Item und
|
||||
// Bei remittieren eines Stapels die StockInformation für alle anderen Stapel mit der selben EAN
|
||||
// Muss InStock nochmal aufgerufen werden um die StockInformationen zu aktualisieren
|
||||
/**
|
||||
* Resource signal for fetching stock information for the current remission items.
|
||||
* Updates when the list of items changes.
|
||||
* @returns Stock info resource state.
|
||||
*/
|
||||
inStockResource = createRemissionInStockResource(() => {
|
||||
return {
|
||||
itemIds: this.items()
|
||||
.map((item) => item?.product?.catalogProductNumber)
|
||||
.filter(
|
||||
(catalogProductNumber): catalogProductNumber is string =>
|
||||
typeof catalogProductNumber === 'string',
|
||||
),
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Computed signal for the current remission list response.
|
||||
* @returns The latest remission list response or undefined.
|
||||
*/
|
||||
listResponseValue = computed(() => this.remissionResource.value());
|
||||
|
||||
/**
|
||||
* Computed signal for the current in-stock response.
|
||||
* @returns Array of StockInfo or undefined.
|
||||
*/
|
||||
inStockResponseValue = computed(() => this.inStockResource.value());
|
||||
|
||||
/**
|
||||
* Computed signal for the remission items to display.
|
||||
* @returns Array of ReturnItem or ReturnSuggestion.
|
||||
*/
|
||||
items = computed(() => {
|
||||
const value = this.responseValue();
|
||||
const value = this.listResponseValue();
|
||||
return value?.result ? value.result : [];
|
||||
});
|
||||
|
||||
/**
|
||||
* Computed signal for the total number of hits in the remission list.
|
||||
* @returns Number of hits, or 0 if unavailable.
|
||||
*/
|
||||
hits = computed(() => {
|
||||
const value = this.responseValue();
|
||||
const value = this.listResponseValue();
|
||||
return value?.hits ? value.hits : 0;
|
||||
});
|
||||
|
||||
search() {
|
||||
/**
|
||||
* Computed signal mapping item IDs to their StockInfo.
|
||||
* @returns Map of itemId to StockInfo.
|
||||
*/
|
||||
stockInfoMap = computed(() => {
|
||||
const infos = this.inStockResponseValue() ?? [];
|
||||
return new Map(infos.map((info) => [info.itemId, info]));
|
||||
});
|
||||
|
||||
/**
|
||||
* Commits the current filter state and triggers a new search.
|
||||
*/
|
||||
search(): void {
|
||||
this.#filterService.commit();
|
||||
this.remissionResource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the StockInfo for a given item.
|
||||
* @param item - The ReturnItem or ReturnSuggestion to look up.
|
||||
* @returns The StockInfo for the item, or undefined if not found.
|
||||
*/
|
||||
getStockForItem(item: ReturnItem | ReturnSuggestion): StockInfo | undefined {
|
||||
return this.stockInfoMap().get(Number(item?.product?.catalogProductNumber));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export * from './remission-list.resource';
|
||||
export * from './remission-instock.resource';
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import { inject, resource } from '@angular/core';
|
||||
import { RemissionStockService } from '@isa/remission/data-access';
|
||||
|
||||
export const createRemissionInStockResource = (
|
||||
params: () => {
|
||||
itemIds: string[];
|
||||
},
|
||||
) => {
|
||||
const remissionStockService = inject(RemissionStockService);
|
||||
return resource({
|
||||
params,
|
||||
loader: async ({ abortSignal, params }) => {
|
||||
if (!params?.itemIds || params.itemIds.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const assignedStock =
|
||||
await remissionStockService.fetchAssignedStock(abortSignal);
|
||||
|
||||
if (!assignedStock || !assignedStock.id) {
|
||||
throw new Error('No current stock available');
|
||||
}
|
||||
|
||||
const itemIds = params.itemIds.map((id) => Number(id));
|
||||
|
||||
if (itemIds.some((id) => isNaN(id))) {
|
||||
throw new Error('Invalid Catalog Product Number provided');
|
||||
}
|
||||
|
||||
return await remissionStockService.fetchStock(
|
||||
{
|
||||
itemIds,
|
||||
assignedStockId: assignedStock.id,
|
||||
},
|
||||
abortSignal,
|
||||
);
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -11,7 +11,7 @@
|
||||
data-what="stock-value"
|
||||
data-which="current-stock"
|
||||
>
|
||||
{{ stock() }}x
|
||||
{{ availableStock() }}x
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
|
||||
@@ -1,376 +1,339 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ProductStockInfoComponent } from './product-stock-info.component';
|
||||
import { By } from '@angular/platform-browser';
|
||||
|
||||
describe('ProductStockInfoComponent', () => {
|
||||
let component: ProductStockInfoComponent;
|
||||
let fixture: ComponentFixture<ProductStockInfoComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [ProductStockInfoComponent],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ProductStockInfoComponent);
|
||||
component = fixture.componentInstance;
|
||||
});
|
||||
|
||||
describe('Component Setup and Initialization', () => {
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should have default stock value of 0', () => {
|
||||
fixture.detectChanges();
|
||||
expect(component.stock()).toBe(0);
|
||||
});
|
||||
|
||||
it('should have default stockToRemit value of 0', () => {
|
||||
fixture.detectChanges();
|
||||
expect(component.stockToRemit()).toBe(0);
|
||||
});
|
||||
|
||||
it('should have default zob signal value of 0', () => {
|
||||
fixture.detectChanges();
|
||||
expect(component.zob()).toBe(0);
|
||||
});
|
||||
|
||||
it('should calculate targetStock correctly with default values', () => {
|
||||
fixture.detectChanges();
|
||||
expect(component.targetStock()).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Input Signal Behavior', () => {
|
||||
it('should accept and update stock input', () => {
|
||||
fixture.componentRef.setInput('stock', 100);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.stock()).toBe(100);
|
||||
});
|
||||
|
||||
it('should accept and update stockToRemit input', () => {
|
||||
fixture.componentRef.setInput('stockToRemit', 25);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.stockToRemit()).toBe(25);
|
||||
});
|
||||
|
||||
it('should update both inputs simultaneously', () => {
|
||||
fixture.componentRef.setInput('stock', 150);
|
||||
fixture.componentRef.setInput('stockToRemit', 40);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.stock()).toBe(150);
|
||||
expect(component.stockToRemit()).toBe(40);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Computed Signal Behavior', () => {
|
||||
it('should calculate targetStock correctly', () => {
|
||||
fixture.componentRef.setInput('stock', 100);
|
||||
fixture.componentRef.setInput('stockToRemit', 30);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.targetStock()).toBe(70);
|
||||
});
|
||||
|
||||
it('should recalculate targetStock when stock changes', () => {
|
||||
fixture.componentRef.setInput('stock', 50);
|
||||
fixture.componentRef.setInput('stockToRemit', 10);
|
||||
fixture.detectChanges();
|
||||
expect(component.targetStock()).toBe(40);
|
||||
|
||||
fixture.componentRef.setInput('stock', 80);
|
||||
fixture.detectChanges();
|
||||
expect(component.targetStock()).toBe(70);
|
||||
});
|
||||
|
||||
it('should recalculate targetStock when stockToRemit changes', () => {
|
||||
fixture.componentRef.setInput('stock', 100);
|
||||
fixture.componentRef.setInput('stockToRemit', 20);
|
||||
fixture.detectChanges();
|
||||
expect(component.targetStock()).toBe(80);
|
||||
|
||||
fixture.componentRef.setInput('stockToRemit', 35);
|
||||
fixture.detectChanges();
|
||||
expect(component.targetStock()).toBe(65);
|
||||
});
|
||||
|
||||
it('should handle negative targetStock values', () => {
|
||||
fixture.componentRef.setInput('stock', 10);
|
||||
fixture.componentRef.setInput('stockToRemit', 25);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.targetStock()).toBe(-15);
|
||||
});
|
||||
|
||||
it('should handle zero stock with positive stockToRemit', () => {
|
||||
fixture.componentRef.setInput('stock', 0);
|
||||
fixture.componentRef.setInput('stockToRemit', 10);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.targetStock()).toBe(-10);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Template Rendering', () => {
|
||||
beforeEach(() => {
|
||||
fixture.componentRef.setInput('stock', 100);
|
||||
fixture.componentRef.setInput('stockToRemit', 25);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should display current stock value', () => {
|
||||
const currentStockElement = fixture.debugElement.query(
|
||||
By.css('[data-which="current-stock"] [data-what="stock-value"]')
|
||||
);
|
||||
expect(currentStockElement).toBeTruthy();
|
||||
expect(currentStockElement.nativeElement.textContent.trim()).toBe('100x');
|
||||
});
|
||||
|
||||
it('should display current stock label', () => {
|
||||
const currentStockLabel = fixture.debugElement.query(
|
||||
By.css('[data-which="current-stock"] [data-what="stock-label"]')
|
||||
);
|
||||
expect(currentStockLabel).toBeTruthy();
|
||||
expect(currentStockLabel.nativeElement.textContent.trim()).toBe('Aktueller Bestand');
|
||||
});
|
||||
|
||||
it('should display remit amount value', () => {
|
||||
const remitAmountElement = fixture.debugElement.query(
|
||||
By.css('[data-which="remit-amount"] [data-what="stock-value"]')
|
||||
);
|
||||
expect(remitAmountElement).toBeTruthy();
|
||||
expect(remitAmountElement.nativeElement.textContent.trim()).toBe('25x');
|
||||
});
|
||||
|
||||
it('should display remit amount label', () => {
|
||||
const remitAmountLabel = fixture.debugElement.query(
|
||||
By.css('[data-which="remit-amount"] [data-what="stock-label"]')
|
||||
);
|
||||
expect(remitAmountLabel).toBeTruthy();
|
||||
expect(remitAmountLabel.nativeElement.textContent.trim()).toBe('Remi Menge');
|
||||
});
|
||||
|
||||
it('should display remaining stock value', () => {
|
||||
const remainingStockElement = fixture.debugElement.query(
|
||||
By.css('[data-which="remaining-stock"] [data-what="stock-value"]')
|
||||
);
|
||||
expect(remainingStockElement).toBeTruthy();
|
||||
expect(remainingStockElement.nativeElement.textContent.trim()).toBe('75x');
|
||||
});
|
||||
|
||||
it('should display remaining stock label', () => {
|
||||
const remainingStockLabel = fixture.debugElement.query(
|
||||
By.css('[data-which="remaining-stock"] [data-what="stock-label"]')
|
||||
);
|
||||
expect(remainingStockLabel).toBeTruthy();
|
||||
expect(remainingStockLabel.nativeElement.textContent.trim()).toBe('Übriger Bestand');
|
||||
});
|
||||
|
||||
it('should display ZOB value', () => {
|
||||
const zobElement = fixture.debugElement.query(
|
||||
By.css('[data-which="zob"] [data-what="stock-value"]')
|
||||
);
|
||||
expect(zobElement).toBeTruthy();
|
||||
expect(zobElement.nativeElement.textContent.trim()).toBe('0x');
|
||||
});
|
||||
|
||||
it('should display ZOB label', () => {
|
||||
const zobLabel = fixture.debugElement.query(
|
||||
By.css('[data-which="zob"] [data-what="stock-label"]')
|
||||
);
|
||||
expect(zobLabel).toBeTruthy();
|
||||
expect(zobLabel.nativeElement.textContent.trim()).toBe('ZOB');
|
||||
});
|
||||
|
||||
it('should have proper CSS classes on stock info rows', () => {
|
||||
const stockInfoRows = fixture.debugElement.queryAll(
|
||||
By.css('.product-stock-info-row')
|
||||
);
|
||||
expect(stockInfoRows.length).toBe(4);
|
||||
|
||||
stockInfoRows.forEach(row => {
|
||||
expect(row.nativeElement.classList.contains('product-stock-info-row')).toBe(true);
|
||||
expect(row.nativeElement.getAttribute('data-what')).toBe('stock-info-row');
|
||||
});
|
||||
});
|
||||
|
||||
it('should have correct data-which attributes', () => {
|
||||
const dataWhichValues = ['current-stock', 'remit-amount', 'remaining-stock', 'zob'];
|
||||
|
||||
dataWhichValues.forEach(value => {
|
||||
const element = fixture.debugElement.query(
|
||||
By.css(`[data-which="${value}"]`)
|
||||
);
|
||||
expect(element).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Signal Updates', () => {
|
||||
it('should update zob signal value', () => {
|
||||
expect(component.zob()).toBe(0);
|
||||
|
||||
component.zob.set(15);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.zob()).toBe(15);
|
||||
|
||||
const zobElement = fixture.debugElement.query(
|
||||
By.css('[data-which="zob"] [data-what="stock-value"]')
|
||||
);
|
||||
expect(zobElement.nativeElement.textContent.trim()).toBe('15x');
|
||||
});
|
||||
|
||||
it('should handle multiple zob signal updates', () => {
|
||||
component.zob.set(10);
|
||||
fixture.detectChanges();
|
||||
expect(component.zob()).toBe(10);
|
||||
|
||||
component.zob.set(25);
|
||||
fixture.detectChanges();
|
||||
expect(component.zob()).toBe(25);
|
||||
|
||||
component.zob.set(0);
|
||||
fixture.detectChanges();
|
||||
expect(component.zob()).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Template Value Updates', () => {
|
||||
it('should update template when stock input changes', () => {
|
||||
fixture.componentRef.setInput('stock', 50);
|
||||
fixture.detectChanges();
|
||||
|
||||
const currentStockElement = fixture.debugElement.query(
|
||||
By.css('[data-which="current-stock"] [data-what="stock-value"]')
|
||||
);
|
||||
expect(currentStockElement.nativeElement.textContent.trim()).toBe('50x');
|
||||
|
||||
fixture.componentRef.setInput('stock', 75);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(currentStockElement.nativeElement.textContent.trim()).toBe('75x');
|
||||
});
|
||||
|
||||
it('should update template when stockToRemit input changes', () => {
|
||||
fixture.componentRef.setInput('stockToRemit', 15);
|
||||
fixture.detectChanges();
|
||||
|
||||
const remitAmountElement = fixture.debugElement.query(
|
||||
By.css('[data-which="remit-amount"] [data-what="stock-value"]')
|
||||
);
|
||||
expect(remitAmountElement.nativeElement.textContent.trim()).toBe('15x');
|
||||
|
||||
fixture.componentRef.setInput('stockToRemit', 20);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(remitAmountElement.nativeElement.textContent.trim()).toBe('20x');
|
||||
});
|
||||
|
||||
it('should update remaining stock in template when computed value changes', () => {
|
||||
fixture.componentRef.setInput('stock', 100);
|
||||
fixture.componentRef.setInput('stockToRemit', 30);
|
||||
fixture.detectChanges();
|
||||
|
||||
const remainingStockElement = fixture.debugElement.query(
|
||||
By.css('[data-which="remaining-stock"] [data-what="stock-value"]')
|
||||
);
|
||||
expect(remainingStockElement.nativeElement.textContent.trim()).toBe('70x');
|
||||
|
||||
fixture.componentRef.setInput('stockToRemit', 45);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(remainingStockElement.nativeElement.textContent.trim()).toBe('55x');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Edge Cases', () => {
|
||||
it('should handle large stock numbers', () => {
|
||||
fixture.componentRef.setInput('stock', 999999);
|
||||
fixture.componentRef.setInput('stockToRemit', 100000);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.stock()).toBe(999999);
|
||||
expect(component.stockToRemit()).toBe(100000);
|
||||
expect(component.targetStock()).toBe(899999);
|
||||
});
|
||||
|
||||
it('should handle negative stock values', () => {
|
||||
fixture.componentRef.setInput('stock', -10);
|
||||
fixture.componentRef.setInput('stockToRemit', 5);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.stock()).toBe(-10);
|
||||
expect(component.stockToRemit()).toBe(5);
|
||||
expect(component.targetStock()).toBe(-15);
|
||||
});
|
||||
|
||||
it('should handle decimal stock values', () => {
|
||||
fixture.componentRef.setInput('stock', 10.5);
|
||||
fixture.componentRef.setInput('stockToRemit', 2.3);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.stock()).toBe(10.5);
|
||||
expect(component.stockToRemit()).toBe(2.3);
|
||||
expect(component.targetStock()).toBe(8.2);
|
||||
});
|
||||
|
||||
it('should maintain change detection strategy', () => {
|
||||
expect(component.constructor.name).toContain('ProductStockInfoComponent');
|
||||
|
||||
// Initial render
|
||||
fixture.detectChanges();
|
||||
|
||||
// Verify OnPush change detection by checking that manual changes
|
||||
// require detectChanges to be reflected in the template
|
||||
const zobElement = fixture.debugElement.query(
|
||||
By.css('[data-which="zob"] [data-what="stock-value"]')
|
||||
);
|
||||
expect(zobElement.nativeElement.textContent.trim()).toBe('0x');
|
||||
|
||||
// Change the signal value
|
||||
component.zob.set(50);
|
||||
|
||||
// After detectChanges, template should update
|
||||
fixture.detectChanges();
|
||||
expect(zobElement.nativeElement.textContent.trim()).toBe('50x');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Component Structure', () => {
|
||||
beforeEach(() => {
|
||||
fixture.componentRef.setInput('stock', 100);
|
||||
fixture.componentRef.setInput('stockToRemit', 25);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should render all required stock info rows', () => {
|
||||
const stockInfoRows = fixture.debugElement.queryAll(
|
||||
By.css('[data-what="stock-info-row"]')
|
||||
);
|
||||
expect(stockInfoRows.length).toBe(4);
|
||||
});
|
||||
|
||||
it('should have proper styling classes', () => {
|
||||
const boldElements = fixture.debugElement.queryAll(
|
||||
By.css('.isa-text-body-2-bold')
|
||||
);
|
||||
expect(boldElements.length).toBe(4);
|
||||
});
|
||||
|
||||
it('should maintain proper semantic structure', () => {
|
||||
const stockLabels = fixture.debugElement.queryAll(
|
||||
By.css('[data-what="stock-label"]')
|
||||
);
|
||||
const stockValues = fixture.debugElement.queryAll(
|
||||
By.css('[data-what="stock-value"]')
|
||||
);
|
||||
|
||||
expect(stockLabels.length).toBe(4);
|
||||
expect(stockValues.length).toBe(4);
|
||||
});
|
||||
});
|
||||
});
|
||||
import { createComponentFactory, Spectator } from '@ngneat/spectator/jest';
|
||||
import { ProductStockInfoComponent } from './product-stock-info.component';
|
||||
|
||||
describe('ProductStockInfoComponent', () => {
|
||||
let spectator: Spectator<ProductStockInfoComponent>;
|
||||
const createComponent = createComponentFactory(ProductStockInfoComponent);
|
||||
|
||||
beforeEach(() => {
|
||||
spectator = createComponent();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(spectator.component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should display the current stock', () => {
|
||||
// Arrange
|
||||
spectator.setInput('stock', 42);
|
||||
|
||||
// Act
|
||||
spectator.detectChanges();
|
||||
const value = spectator.query(
|
||||
'[data-what="stock-value"][data-which="current-stock"]',
|
||||
);
|
||||
|
||||
// Assert
|
||||
expect(value).toHaveText('42x');
|
||||
});
|
||||
|
||||
it('should display the remit amount (computed)', () => {
|
||||
// Arrange
|
||||
spectator.setInput('stock', 20);
|
||||
spectator.setInput('removedFromStock', 5);
|
||||
spectator.setInput('remainingQuantityInStock', 10);
|
||||
|
||||
// Act
|
||||
spectator.detectChanges();
|
||||
const value = spectator.query(
|
||||
'[data-what="stock-value"][data-which="remit-amount"]',
|
||||
);
|
||||
|
||||
// Assert
|
||||
// availableStock = 20 - 5 = 15; stockToRemit = 15 - 10 = 5
|
||||
expect(value).toHaveText('5x');
|
||||
});
|
||||
|
||||
it('should display the remit amount as 0 when remainingQuantityInStock > availableStock', () => {
|
||||
// Arrange
|
||||
spectator.setInput('stock', 5);
|
||||
spectator.setInput('removedFromStock', 2);
|
||||
spectator.setInput('remainingQuantityInStock', 10);
|
||||
|
||||
// Act
|
||||
spectator.detectChanges();
|
||||
const value = spectator.query(
|
||||
'[data-what="stock-value"][data-which="remit-amount"]',
|
||||
);
|
||||
|
||||
// Assert
|
||||
expect(value).toHaveText('0x');
|
||||
});
|
||||
|
||||
it('should display the remaining stock (targetStock, computed)', () => {
|
||||
// Arrange
|
||||
spectator.setInput('stock', 20);
|
||||
spectator.setInput('removedFromStock', 5);
|
||||
spectator.setInput('predefinedReturnQuantity', 5);
|
||||
|
||||
// Act
|
||||
spectator.detectChanges();
|
||||
const value = spectator.query(
|
||||
'[data-what="stock-value"][data-which="remaining-stock"]',
|
||||
);
|
||||
|
||||
// Assert
|
||||
// availableStock = 20 - 5 = 15; targetStock = 15 - 5 = 10
|
||||
expect(value).toHaveText('10x');
|
||||
});
|
||||
|
||||
it('should display the remaining stock as 0 when predefinedReturnQuantity > availableStock', () => {
|
||||
// Arrange
|
||||
spectator.setInput('stock', 8);
|
||||
spectator.setInput('removedFromStock', 3);
|
||||
spectator.setInput('predefinedReturnQuantity', 10);
|
||||
|
||||
// Act
|
||||
spectator.detectChanges();
|
||||
const value = spectator.query(
|
||||
'[data-what="stock-value"][data-which="remaining-stock"]',
|
||||
);
|
||||
|
||||
// Assert
|
||||
// availableStock = 8 - 3 = 5; targetStock = 5 - 10 = -5 => 0
|
||||
expect(value).toHaveText('0x');
|
||||
});
|
||||
|
||||
it('should display the zob value', () => {
|
||||
// Arrange
|
||||
spectator.setInput('zob', 99);
|
||||
|
||||
// Act
|
||||
spectator.detectChanges();
|
||||
const value = spectator.query(
|
||||
'[data-what="stock-value"][data-which="zob"]',
|
||||
);
|
||||
|
||||
// Assert
|
||||
expect(value).toHaveText('99x');
|
||||
});
|
||||
|
||||
it('should render all labels with correct e2e attributes', () => {
|
||||
// Arrange
|
||||
const labels = [
|
||||
{ which: 'current-stock', text: 'Aktueller Bestand' },
|
||||
{ which: 'remit-amount', text: 'Remi Menge' },
|
||||
{ which: 'remaining-stock', text: 'Übriger Bestand' },
|
||||
{ which: 'zob', text: 'ZOB' },
|
||||
];
|
||||
|
||||
// Act & Assert
|
||||
labels.forEach(({ which, text }) => {
|
||||
const label = spectator.query(
|
||||
`[data-what="stock-label"][data-which="${which}"]`,
|
||||
);
|
||||
expect(label).toHaveText(text);
|
||||
});
|
||||
});
|
||||
|
||||
it('should compute availableStock correctly (stock > removedFromStock)', () => {
|
||||
// Arrange
|
||||
spectator.setInput('stock', 10);
|
||||
spectator.setInput('removedFromStock', 3);
|
||||
|
||||
// Act
|
||||
const result = spectator.component.availableStock();
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(7);
|
||||
});
|
||||
|
||||
it('should compute availableStock as 0 when removedFromStock > stock', () => {
|
||||
// Arrange
|
||||
spectator.setInput('stock', 5);
|
||||
spectator.setInput('removedFromStock', 10);
|
||||
|
||||
// Act
|
||||
const result = spectator.component.availableStock();
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(0);
|
||||
});
|
||||
|
||||
it('should compute stockToRemit correctly (positive result)', () => {
|
||||
// Arrange
|
||||
spectator.setInput('stock', 20);
|
||||
spectator.setInput('removedFromStock', 5);
|
||||
spectator.setInput('remainingQuantityInStock', 10);
|
||||
|
||||
// Act
|
||||
const result = spectator.component.stockToRemit();
|
||||
|
||||
// Assert
|
||||
// availableStock = 20 - 5 = 15; stockToRemit = 15 - 10 = 5
|
||||
expect(result).toBe(5);
|
||||
});
|
||||
|
||||
it('should compute stockToRemit as 0 when remainingQuantityInStock > availableStock', () => {
|
||||
// Arrange
|
||||
spectator.setInput('stock', 5);
|
||||
spectator.setInput('removedFromStock', 2);
|
||||
spectator.setInput('remainingQuantityInStock', 10);
|
||||
|
||||
// Act
|
||||
const result = spectator.component.stockToRemit();
|
||||
|
||||
// Assert
|
||||
// availableStock = 5 - 2 = 3; stockToRemit = 3 - 10 = -7 => 0
|
||||
expect(result).toBe(0);
|
||||
});
|
||||
|
||||
it('should compute targetStock correctly (positive result)', () => {
|
||||
// Arrange
|
||||
spectator.setInput('stock', 30);
|
||||
spectator.setInput('removedFromStock', 5);
|
||||
spectator.setInput('predefinedReturnQuantity', 10);
|
||||
|
||||
// Act
|
||||
const result = spectator.component.targetStock();
|
||||
|
||||
// Assert
|
||||
// availableStock = 30 - 5 = 25; targetStock = 25 - 10 = 15
|
||||
expect(result).toBe(15);
|
||||
});
|
||||
|
||||
it('should compute targetStock as 0 when predefinedReturnQuantity > availableStock', () => {
|
||||
// Arrange
|
||||
spectator.setInput('stock', 8);
|
||||
spectator.setInput('removedFromStock', 3);
|
||||
spectator.setInput('predefinedReturnQuantity', 10);
|
||||
|
||||
// Act
|
||||
const result = spectator.component.targetStock();
|
||||
|
||||
// Assert
|
||||
// availableStock = 8 - 3 = 5; targetStock = 5 - 10 = -5 => 0
|
||||
expect(result).toBe(0);
|
||||
});
|
||||
|
||||
it('should compute targetStock using stockToRemit when remainingQuantityInStock is zero or falsy', () => {
|
||||
// Arrange
|
||||
spectator.setInput('stock', 15);
|
||||
spectator.setInput('removedFromStock', 5);
|
||||
spectator.setInput('predefinedReturnQuantity', 0);
|
||||
spectator.setInput('remainingQuantityInStock', 0);
|
||||
|
||||
// Act
|
||||
const result = spectator.component.targetStock();
|
||||
|
||||
// Assert
|
||||
// availableStock = 15 - 5 = 10
|
||||
// stockToRemit = 10 - 0 = 10
|
||||
// targetStock = 10 - 10 = 0
|
||||
expect(result).toBe(0);
|
||||
});
|
||||
|
||||
it('should compute targetStock using remainingQuantityInStock when it is set (non-zero)', () => {
|
||||
// Arrange
|
||||
spectator.setInput('stock', 20);
|
||||
spectator.setInput('removedFromStock', 5);
|
||||
spectator.setInput('remainingQuantityInStock', 7);
|
||||
|
||||
// Act
|
||||
const result = spectator.component.targetStock();
|
||||
|
||||
// Assert
|
||||
// Should return remainingQuantityInStock directly
|
||||
expect(result).toBe(7);
|
||||
});
|
||||
|
||||
it('should compute targetStock as 0 if stockToRemit is greater than availableStock', () => {
|
||||
// Arrange
|
||||
spectator.setInput('stock', 5);
|
||||
spectator.setInput('removedFromStock', 2);
|
||||
spectator.setInput('remainingQuantityInStock', 0);
|
||||
spectator.setInput('predefinedReturnQuantity', 0);
|
||||
|
||||
// Act
|
||||
const result = spectator.component.targetStock();
|
||||
|
||||
// Assert
|
||||
// availableStock = 5 - 2 = 3
|
||||
// stockToRemit = 3 - 0 = 3
|
||||
// targetStock = 3 - 3 = 0
|
||||
expect(result).toBe(0);
|
||||
});
|
||||
|
||||
it('should compute stockToRemit as predefinedReturnQuantity if set (non-zero)', () => {
|
||||
// Arrange
|
||||
spectator.setInput('stock', 10);
|
||||
spectator.setInput('removedFromStock', 2);
|
||||
spectator.setInput('predefinedReturnQuantity', 4);
|
||||
spectator.setInput('remainingQuantityInStock', 5);
|
||||
|
||||
// Act
|
||||
const result = spectator.component.stockToRemit();
|
||||
|
||||
// Assert
|
||||
// Should return predefinedReturnQuantity directly
|
||||
expect(result).toBe(4);
|
||||
});
|
||||
|
||||
it('should compute stockToRemit as 0 if availableStock and remainingQuantityInStock are both zero', () => {
|
||||
// Arrange
|
||||
spectator.setInput('stock', 0);
|
||||
spectator.setInput('removedFromStock', 0);
|
||||
spectator.setInput('predefinedReturnQuantity', 0);
|
||||
spectator.setInput('remainingQuantityInStock', 0);
|
||||
|
||||
// Act
|
||||
const result = spectator.component.stockToRemit();
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(0);
|
||||
});
|
||||
|
||||
it('should handle all-zero inputs for computed properties', () => {
|
||||
// Arrange
|
||||
spectator.setInput('stock', 0);
|
||||
spectator.setInput('removedFromStock', 0);
|
||||
spectator.setInput('remainingQuantityInStock', 0);
|
||||
spectator.setInput('predefinedReturnQuantity', 0);
|
||||
|
||||
// Act & Assert
|
||||
expect(spectator.component.availableStock()).toBe(0);
|
||||
expect(spectator.component.stockToRemit()).toBe(0);
|
||||
expect(spectator.component.targetStock()).toBe(0);
|
||||
});
|
||||
|
||||
it('should display all values as 0x when all inputs are zero', () => {
|
||||
// Arrange
|
||||
spectator.setInput('stock', 0);
|
||||
spectator.setInput('removedFromStock', 0);
|
||||
spectator.setInput('remainingQuantityInStock', 0);
|
||||
spectator.setInput('predefinedReturnQuantity', 0);
|
||||
spectator.setInput('zob', 0);
|
||||
|
||||
// Act
|
||||
spectator.detectChanges();
|
||||
|
||||
// Assert
|
||||
expect(
|
||||
spectator.query('[data-what="stock-value"][data-which="current-stock"]'),
|
||||
).toHaveText('0x');
|
||||
expect(
|
||||
spectator.query('[data-what="stock-value"][data-which="remit-amount"]'),
|
||||
).toHaveText('0x');
|
||||
expect(
|
||||
spectator.query(
|
||||
'[data-what="stock-value"][data-which="remaining-stock"]',
|
||||
),
|
||||
).toHaveText('0x');
|
||||
expect(
|
||||
spectator.query('[data-what="stock-value"][data-which="zob"]'),
|
||||
).toHaveText('0x');
|
||||
});
|
||||
|
||||
it('should display correct values when only zob is set', () => {
|
||||
// Arrange
|
||||
spectator.setInput('zob', 123);
|
||||
|
||||
// Act
|
||||
spectator.detectChanges();
|
||||
|
||||
// Assert
|
||||
expect(
|
||||
spectator.query('[data-what="stock-value"][data-which="zob"]'),
|
||||
).toHaveText('123x');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,9 +3,26 @@ import {
|
||||
Component,
|
||||
computed,
|
||||
input,
|
||||
signal,
|
||||
} from '@angular/core';
|
||||
|
||||
/**
|
||||
* Displays and computes stock information for a product.
|
||||
*
|
||||
* ## Inputs
|
||||
* - `stock`: The initial stock quantity (from InStock Request, maps to `stockInfo.inStock`).
|
||||
* - `removedFromStock`: The quantity removed from stock (from InStock Request, maps to `stockInfo.removedFromStock`).
|
||||
* - `predefinedReturnQuantity`: The predefined return quantity (from Pflicht- or Abteilungsremission Request, maps to `ReturnItem.predefinedReturnQuantity` or `ReturnSuggestion.returnItem.data.predefinedReturnQuantity`).
|
||||
* - `remainingQuantityInStock`: The remaining quantity in stock (from Pflicht- or Abteilungsremission Request, maps to `ReturnItem.remainingQuantityInStock` or `ReturnSuggestion.remainingQuantityInStock`).
|
||||
* - `zob`: Min Stock Category Management Information (from InStock Request, maps to `stockInfo.minStockCategoryManagement`).
|
||||
*
|
||||
* ## Computed Properties
|
||||
* - `availableStock`: Current available stock, calculated as `stock - removedFromStock`. Returns 0 if negative.
|
||||
* - `stockToRemit`: Remission quantity, calculated as `availableStock - remainingQuantityInStock`. Returns 0 if negative.
|
||||
* - `targetStock`: Remaining stock after predefined return, calculated as `availableStock - predefinedReturnQuantity`. Returns 0 if negative.
|
||||
*
|
||||
* @remarks
|
||||
* Implements OnPush change detection for performance.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'remi-product-stock-info',
|
||||
templateUrl: './product-stock-info.component.html',
|
||||
@@ -13,15 +30,95 @@ import {
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ProductStockInfoComponent {
|
||||
/**
|
||||
* The initial stock quantity.
|
||||
* (InStock Request) → stockInfo.inStock
|
||||
*/
|
||||
stock = input<number>(0);
|
||||
|
||||
stockToRemit = input<number>(0);
|
||||
/**
|
||||
* The quantity removed from stock.
|
||||
* (InStock Request) → stockInfo.removedFromStock
|
||||
*/
|
||||
removedFromStock = input<number>(0);
|
||||
|
||||
targetStock = computed(() => {
|
||||
/**
|
||||
* The predefined return quantity.
|
||||
* (Pflicht- oder Abteilungsremission Request) → ReturnItem.predefinedReturnQuantity | ReturnSuggestion.returnItem.data.predefinedReturnQuantity
|
||||
*/
|
||||
predefinedReturnQuantity = input<number>(0);
|
||||
|
||||
/**
|
||||
* The remaining quantity in stock.
|
||||
* (Pflicht- oder Abteilungsremission Request) → ReturnItem.remainingQuantityInStock | ReturnSuggestion.remainingQuantityInStock
|
||||
*/
|
||||
remainingQuantityInStock = input<number>(0);
|
||||
|
||||
/**
|
||||
* Min Stock Category Management Information.
|
||||
* (InStock Request) → stockInfo.minStockCategoryManagement
|
||||
*/
|
||||
zob = input<number>(0);
|
||||
|
||||
/**
|
||||
* Current available stock.
|
||||
* Calculation: stock - removedFromStock
|
||||
* Returns 0 if result is negative.
|
||||
*
|
||||
* @remarks
|
||||
* Used as the base for further stock calculations.
|
||||
*/
|
||||
availableStock = computed(() => {
|
||||
const stock = this.stock();
|
||||
const stockToRemit = this.stockToRemit();
|
||||
return stock - stockToRemit;
|
||||
const removedFromStock = this.removedFromStock();
|
||||
const availableStock = stock - removedFromStock;
|
||||
return availableStock < 0 ? 0 : availableStock;
|
||||
});
|
||||
|
||||
zob = signal(0);
|
||||
/**
|
||||
* Quantity to remit (remission quantity).
|
||||
*
|
||||
* - If `predefinedReturnQuantity` is set (non-zero), returns that value.
|
||||
* - Otherwise, calculates as `availableStock - remainingQuantityInStock`.
|
||||
* - Returns 0 if the result is negative.
|
||||
*
|
||||
* @remarks
|
||||
* This value is used as an input for `targetStock`.
|
||||
*/
|
||||
stockToRemit = computed(() => {
|
||||
const stock = this.availableStock();
|
||||
const predefinedReturnQuantity = this.predefinedReturnQuantity();
|
||||
const remainingQuantityInStock = this.remainingQuantityInStock();
|
||||
|
||||
if (!predefinedReturnQuantity) {
|
||||
const stockToRemit = stock - (remainingQuantityInStock ?? 0);
|
||||
return stockToRemit < 0 ? 0 : stockToRemit;
|
||||
}
|
||||
|
||||
return predefinedReturnQuantity;
|
||||
});
|
||||
|
||||
/**
|
||||
* Target stock after remission.
|
||||
*
|
||||
* - If `remainingQuantityInStock` is set (non-zero), returns that value.
|
||||
* - Otherwise, calculates as `availableStock - stockToRemit`.
|
||||
* - Returns 0 if the result is negative.
|
||||
*
|
||||
* @remarks
|
||||
* Depends on `stockToRemit` for calculation.
|
||||
* Represents the expected stock after the remission process.
|
||||
*/
|
||||
targetStock = computed(() => {
|
||||
const stock = this.availableStock();
|
||||
const stockToRemit = this.stockToRemit();
|
||||
const remainingQuantityInStock = this.remainingQuantityInStock();
|
||||
|
||||
if (!remainingQuantityInStock) {
|
||||
const targetStock = stock - (stockToRemit ?? 0);
|
||||
return targetStock < 0 ? 0 : targetStock;
|
||||
}
|
||||
|
||||
return remainingQuantityInStock;
|
||||
});
|
||||
}
|
||||
|
||||
267
package-lock.json
generated
267
package-lock.json
generated
@@ -1616,17 +1616,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-define-polyfill-provider": {
|
||||
"version": "0.6.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.4.tgz",
|
||||
"integrity": "sha512-jljfR1rGnXXNWnmQg2K3+bvhkxB51Rl32QRaOTuwwjviGrHzIbSc8+x9CpraDtbT7mfyjXObULP4w/adunNwAw==",
|
||||
"version": "0.6.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.5.tgz",
|
||||
"integrity": "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-compilation-targets": "^7.22.6",
|
||||
"@babel/helper-plugin-utils": "^7.22.5",
|
||||
"debug": "^4.1.1",
|
||||
"@babel/helper-compilation-targets": "^7.27.2",
|
||||
"@babel/helper-plugin-utils": "^7.27.1",
|
||||
"debug": "^4.4.1",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"resolve": "^1.14.2"
|
||||
"resolve": "^1.22.10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
|
||||
@@ -3346,9 +3346,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@bufbuild/protobuf": {
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.5.2.tgz",
|
||||
"integrity": "sha512-foZ7qr0IsUBjzWIq+SuBLfdQCpJ1j8cTuNNT4owngTHoN5KsJb8L9t65fzz7SCeSWzescoOil/0ldqiL041ABg==",
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.6.0.tgz",
|
||||
"integrity": "sha512-6cuonJVNOIL7lTj5zgo/Rc2bKAo4/GvN+rKCrUj7GdEHRzCk8zKOfFwUsL9nAVk5rSIsRmlgcpLzTRysopEeeg==",
|
||||
"dev": true,
|
||||
"license": "(Apache-2.0 AND BSD-3-Clause)"
|
||||
},
|
||||
@@ -3463,9 +3463,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/config-array": {
|
||||
"version": "0.20.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.1.tgz",
|
||||
"integrity": "sha512-OL0RJzC/CBzli0DrrR31qzj6d6i6Mm3HByuhflhl4LOBiWxN+3i6/t/ZQQNii4tjksXi8r2CRW1wMpWA2ULUEw==",
|
||||
"version": "0.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz",
|
||||
"integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
@@ -3502,9 +3502,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/config-helpers": {
|
||||
"version": "0.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.3.tgz",
|
||||
"integrity": "sha512-u180qk2Um1le4yf0ruXH3PYFeEZeYC3p/4wCTKrr2U1CmGdzGi3KtY0nuPDH48UJxlKCC5RDzbcbh4X0XlqgHg==",
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz",
|
||||
"integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
@@ -3623,9 +3623,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/js": {
|
||||
"version": "9.29.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.29.0.tgz",
|
||||
"integrity": "sha512-3PIF4cBw/y+1u2EazflInpV+lYsSG0aByVIQzAgb1m1MhHFSbqTyNqtBKHgWf/9Ykud+DhILS9EGkmekVhbKoQ==",
|
||||
"version": "9.30.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.30.0.tgz",
|
||||
"integrity": "sha512-Wzw3wQwPvc9sHM+NjakWTcPx11mbZyiYHuwWa/QfZ7cIRX7WK54PSk7bdyXDaoaopUcMatv1zaQvOAAO8hCdww==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -4600,17 +4600,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.3.8",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
|
||||
"integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
|
||||
"version": "0.3.10",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.10.tgz",
|
||||
"integrity": "sha512-HM2F4B9N4cA0RH2KQiIZOHAZqtP4xGS4IZ+SFe1SIbO4dyjf9MTY2Bo3vHYnm0hglWfXqBrzUBSa+cJfl3Xvrg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/set-array": "^1.2.1",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10",
|
||||
"@jridgewell/sourcemap-codec": "^1.5.0",
|
||||
"@jridgewell/trace-mapping": "^0.3.24"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/resolve-uri": {
|
||||
@@ -4622,19 +4618,10 @@
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/set-array": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
|
||||
"integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/source-map": {
|
||||
"version": "0.3.6",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
|
||||
"integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
|
||||
"version": "0.3.8",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.8.tgz",
|
||||
"integrity": "sha512-3EDAPd0B8X1gsQQgGHU8vyxSp2MB414z3roN67fY7nI0GV3GDthHfaWcbCfrC95tpAzA5xUvAuoO9Dxx/ywwRQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -4643,15 +4630,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/sourcemap-codec": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
|
||||
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
|
||||
"version": "1.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.2.tgz",
|
||||
"integrity": "sha512-gKYheCylLIedI+CSZoDtGkFV9YEBxRRVcfCH7OfAqh4TyUyRjEE6WVE/aXDXX0p8BIe/QgLcaAoI0220KRRFgg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.25",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
|
||||
"integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
|
||||
"version": "0.3.27",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.27.tgz",
|
||||
"integrity": "sha512-VO95AxtSFMelbg3ouljAYnfvTEwSWVt/2YLf+U5Ejd8iT5mXE2Sa/1LGyvySMne2CGsepGLI7KpF3EzE3Aq9Mg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "^3.1.0",
|
||||
@@ -7935,9 +7922,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rspack/dev-server/node_modules/ws": {
|
||||
"version": "8.18.2",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz",
|
||||
"integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==",
|
||||
"version": "8.18.3",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
|
||||
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -8111,16 +8098,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@storybook/addon-docs": {
|
||||
"version": "9.0.13",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-9.0.13.tgz",
|
||||
"integrity": "sha512-e9mCzgxsSz/FIHz08Gex71jF2tz5WNPgSxEg6Lb8jMWbfdgb2lEFyby/RV5qj8ajTXhheiGKZ3K9JESfLWm0ng==",
|
||||
"version": "9.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-9.0.14.tgz",
|
||||
"integrity": "sha512-vjWH2FamLzoPZXitecbhRSUvQDj27q/dDaCKXSwCIwEVziIQrqHBGDmuJPCWoroCkKxLo8s8gwMi6wk5Minaqg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@mdx-js/react": "^3.0.0",
|
||||
"@storybook/csf-plugin": "9.0.13",
|
||||
"@storybook/csf-plugin": "9.0.14",
|
||||
"@storybook/icons": "^1.2.12",
|
||||
"@storybook/react-dom-shim": "9.0.13",
|
||||
"@storybook/react-dom-shim": "9.0.14",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"ts-dedent": "^2.0.0"
|
||||
@@ -8130,18 +8117,18 @@
|
||||
"url": "https://opencollective.com/storybook"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"storybook": "^9.0.13"
|
||||
"storybook": "^9.0.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@storybook/angular": {
|
||||
"version": "9.0.13",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/angular/-/angular-9.0.13.tgz",
|
||||
"integrity": "sha512-cAlfffHGv/7F5twUK/T6QH54uotUA/AeRj9QlB8FzsM/Eqg+o0NrUOarmR3GOT+N4AOeX/a9cLkCOfuBwE/Tqg==",
|
||||
"version": "9.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/angular/-/angular-9.0.14.tgz",
|
||||
"integrity": "sha512-sGg/lMGoUSpCNAvzXRBjOYfm2wo2JHdNFQpu+2H6yA3qZ5CnrehbxkkaiBkna0+K6PM/Q6eVU4nugXa41HSdjw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@storybook/builder-webpack5": "9.0.13",
|
||||
"@storybook/core-webpack": "9.0.13",
|
||||
"@storybook/builder-webpack5": "9.0.14",
|
||||
"@storybook/core-webpack": "9.0.14",
|
||||
"@storybook/global": "^5.0.0",
|
||||
"@types/webpack-env": "^1.18.0",
|
||||
"fd-package-json": "^1.2.0",
|
||||
@@ -8172,7 +8159,7 @@
|
||||
"@angular/platform-browser": ">=18.0.0 < 21.0.0",
|
||||
"@angular/platform-browser-dynamic": ">=18.0.0 < 21.0.0",
|
||||
"rxjs": "^6.5.3 || ^7.4.0",
|
||||
"storybook": "^9.0.13",
|
||||
"storybook": "^9.0.14",
|
||||
"typescript": "^4.9.0 || ^5.0.0",
|
||||
"zone.js": ">=0.14.0"
|
||||
},
|
||||
@@ -8205,13 +8192,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@storybook/builder-webpack5": {
|
||||
"version": "9.0.13",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/builder-webpack5/-/builder-webpack5-9.0.13.tgz",
|
||||
"integrity": "sha512-uQ6x4ReMzRPkiaEJbflxGf7qkLDqz2rI7xdrs98Vuu8lyejOFN5nT5NUi0VXsxRczlVeSO0fQq/H/kSFXOxlvg==",
|
||||
"version": "9.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/builder-webpack5/-/builder-webpack5-9.0.14.tgz",
|
||||
"integrity": "sha512-wu7OC+WE+PK7IaKKUEFN7a04CeSKJWkAqlgTV7BrrExHIbbOVTrmQSn/q02SYZJRebr4lllAD9cD90TlO8aV+g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@storybook/core-webpack": "9.0.13",
|
||||
"@storybook/core-webpack": "9.0.14",
|
||||
"case-sensitive-paths-webpack-plugin": "^2.4.0",
|
||||
"cjs-module-lexer": "^1.2.3",
|
||||
"css-loader": "^6.7.1",
|
||||
@@ -8232,7 +8219,7 @@
|
||||
"url": "https://opencollective.com/storybook"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"storybook": "^9.0.13"
|
||||
"storybook": "^9.0.14"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
@@ -8491,9 +8478,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@storybook/core-webpack": {
|
||||
"version": "9.0.13",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/core-webpack/-/core-webpack-9.0.13.tgz",
|
||||
"integrity": "sha512-jC6M5FOoTjsJ2FGmp8TAuQwjR22kGE30MfGav27XC1GZFghnzqgvAmoEXNR0QU6dBYinR5jEgx2O/iPgVGsOXw==",
|
||||
"version": "9.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/core-webpack/-/core-webpack-9.0.14.tgz",
|
||||
"integrity": "sha512-LBWyCPKKBAb+Gb9QyIO2QcCf6QxRgzXfSyO1rnDqICrPdzukFtpEt4214v9cPw8wQqTclj7XADiOOsQMQjKmAA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -8504,13 +8491,13 @@
|
||||
"url": "https://opencollective.com/storybook"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"storybook": "^9.0.13"
|
||||
"storybook": "^9.0.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@storybook/csf-plugin": {
|
||||
"version": "9.0.13",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-9.0.13.tgz",
|
||||
"integrity": "sha512-yVBZERU2+FEqYFoRxK1sebP4aYZAwUFFG2MpD8YHM1g51lWpWDQsKkW57jPZ65GbuaK/DDLSldva6kF+tBk1DQ==",
|
||||
"version": "9.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-9.0.14.tgz",
|
||||
"integrity": "sha512-PKUmF5y/SfPOifC2bRo79YwfGv6TYISM5JK6r6FHVKMwV1nWLmj7Xx2t5aHa/5JggdBz/iGganTP7oo7QOn+0A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -8521,7 +8508,7 @@
|
||||
"url": "https://opencollective.com/storybook"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"storybook": "^9.0.13"
|
||||
"storybook": "^9.0.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@storybook/global": {
|
||||
@@ -8546,9 +8533,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@storybook/react-dom-shim": {
|
||||
"version": "9.0.13",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-9.0.13.tgz",
|
||||
"integrity": "sha512-k7fucEJu39cE7V31fX+cM2wbW869vuj9hFZJDIEPWwo33nKFFqr0f31ar01gCcYuMiad8KezbdzXnDcBw6c6Ww==",
|
||||
"version": "9.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-9.0.14.tgz",
|
||||
"integrity": "sha512-fXMzhgFMnGZUhWm9zWiR8qOB90OykPhkB/qiebFbD/wUedPyp3H1+NAzX1/UWV2SYqr+aFK9vH1PokAYbpTRsw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
@@ -8558,7 +8545,7 @@
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta",
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta",
|
||||
"storybook": "^9.0.13"
|
||||
"storybook": "^9.0.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@swc-node/core": {
|
||||
@@ -9798,14 +9785,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/expect": {
|
||||
"version": "3.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.9.tgz",
|
||||
"integrity": "sha512-5eCqRItYgIML7NNVgJj6TVCmdzE7ZVgJhruW0ziSQV4V7PvLkDL1bBkBdcTs/VuIz0IxPb5da1IDSqc1TR9eig==",
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz",
|
||||
"integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/spy": "3.0.9",
|
||||
"@vitest/utils": "3.0.9",
|
||||
"@types/chai": "^5.2.2",
|
||||
"@vitest/spy": "3.2.4",
|
||||
"@vitest/utils": "3.2.4",
|
||||
"chai": "^5.2.0",
|
||||
"tinyrainbow": "^2.0.0"
|
||||
},
|
||||
@@ -9814,14 +9802,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/expect/node_modules/@vitest/utils": {
|
||||
"version": "3.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.9.tgz",
|
||||
"integrity": "sha512-ilHM5fHhZ89MCp5aAaM9uhfl1c2JdxVxl3McqsdVyVNN6JffnEen8UMCdRTzOhGXNQGo5GNL9QugHrz727Wnng==",
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz",
|
||||
"integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/pretty-format": "3.0.9",
|
||||
"loupe": "^3.1.3",
|
||||
"@vitest/pretty-format": "3.2.4",
|
||||
"loupe": "^3.1.4",
|
||||
"tinyrainbow": "^2.0.0"
|
||||
},
|
||||
"funding": {
|
||||
@@ -9879,9 +9867,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/pretty-format": {
|
||||
"version": "3.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.9.tgz",
|
||||
"integrity": "sha512-OW9F8t2J3AwFEwENg3yMyKWweF7oRJlMyHOMIhO5F3n0+cgQAJZBjNgrF8dLwFTEXl5jUqBLXd9QyyKv8zEcmA==",
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz",
|
||||
"integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -9935,13 +9923,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/spy": {
|
||||
"version": "3.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.9.tgz",
|
||||
"integrity": "sha512-/CcK2UDl0aQ2wtkp3YVWldrpLRNCfVcIOFGlVGKO4R5eajsH393Z1yiXLVQ7vWsj26JOEjeZI0x5sm5P4OGUNQ==",
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz",
|
||||
"integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tinyspy": "^3.0.2"
|
||||
"tinyspy": "^4.0.3"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
@@ -10959,14 +10947,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/babel-plugin-polyfill-corejs2": {
|
||||
"version": "0.4.13",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.13.tgz",
|
||||
"integrity": "sha512-3sX/eOms8kd3q2KZ6DAhKPc0dgm525Gqq5NtWKZ7QYYZEv57OQ54KtblzJzH1lQF/eQxO8KjWGIK9IPUJNus5g==",
|
||||
"version": "0.4.14",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.14.tgz",
|
||||
"integrity": "sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/compat-data": "^7.22.6",
|
||||
"@babel/helper-define-polyfill-provider": "^0.6.4",
|
||||
"@babel/compat-data": "^7.27.7",
|
||||
"@babel/helper-define-polyfill-provider": "^0.6.5",
|
||||
"semver": "^6.3.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
@@ -10998,13 +10986,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/babel-plugin-polyfill-regenerator": {
|
||||
"version": "0.6.4",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.4.tgz",
|
||||
"integrity": "sha512-7gD3pRadPrbjhjLyxebmx/WrFYcuSjZ0XbdUujQMZ/fcE9oeewk2U/7PCvez84UeuK3oSjmPZ0Ch0dlupQvGzw==",
|
||||
"version": "0.6.5",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.5.tgz",
|
||||
"integrity": "sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-define-polyfill-provider": "^0.6.4"
|
||||
"@babel/helper-define-polyfill-provider": "^0.6.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
|
||||
@@ -12613,9 +12601,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/css-select": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
|
||||
"integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz",
|
||||
"integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
@@ -12644,9 +12632,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/css-what": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
|
||||
"integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
|
||||
"version": "6.2.2",
|
||||
"resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz",
|
||||
"integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
@@ -13787,19 +13775,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "9.29.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.29.0.tgz",
|
||||
"integrity": "sha512-GsGizj2Y1rCWDu6XoEekL3RLilp0voSePurjZIkxL3wlm5o5EC9VpgaP7lrCvjnkuLvzFBQWB3vWB3K5KQTveQ==",
|
||||
"version": "9.30.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.30.0.tgz",
|
||||
"integrity": "sha512-iN/SiPxmQu6EVkf+m1qpBxzUhE12YqFLOSySuOyVLJLEF9nzTf+h/1AJYc1JWzCnktggeNrjvQGLngDzXirU6g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@eslint-community/regexpp": "^4.12.1",
|
||||
"@eslint/config-array": "^0.20.1",
|
||||
"@eslint/config-helpers": "^0.2.1",
|
||||
"@eslint/config-array": "^0.21.0",
|
||||
"@eslint/config-helpers": "^0.3.0",
|
||||
"@eslint/core": "^0.14.0",
|
||||
"@eslint/eslintrc": "^3.3.1",
|
||||
"@eslint/js": "9.29.0",
|
||||
"@eslint/js": "9.30.0",
|
||||
"@eslint/plugin-kit": "^0.3.1",
|
||||
"@humanfs/node": "^0.16.6",
|
||||
"@humanwhocodes/module-importer": "^1.0.1",
|
||||
@@ -17500,9 +17488,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/jest-environment-jsdom/node_modules/ws": {
|
||||
"version": "8.18.2",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz",
|
||||
"integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==",
|
||||
"version": "8.18.3",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
|
||||
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -18336,9 +18324,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/jsdom/node_modules/ws": {
|
||||
"version": "8.18.2",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz",
|
||||
"integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==",
|
||||
"version": "8.18.3",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
|
||||
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -23831,10 +23819,11 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/rslog": {
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/rslog/-/rslog-1.2.8.tgz",
|
||||
"integrity": "sha512-BXUB5LnElxG0n9dSS+1Num4q+U+GGuCasi2/8I6hYMyZm2+L5kUGvv7pAc6z7+ODxFXVV6AHy9mSa2VSoauk+g==",
|
||||
"dev": true
|
||||
"version": "1.2.9",
|
||||
"resolved": "https://registry.npmjs.org/rslog/-/rslog-1.2.9.tgz",
|
||||
"integrity": "sha512-KSjM8jJKYYaKgI4jUGZZ4kdTBTM/EIGH1JnoB0ptMkzcyWaHeXW9w6JVLCYs37gh8sFZkLLqAyBb2sT02bqpcQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/run-applescript": {
|
||||
"version": "7.0.0",
|
||||
@@ -25045,17 +25034,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/storybook": {
|
||||
"version": "9.0.13",
|
||||
"resolved": "https://registry.npmjs.org/storybook/-/storybook-9.0.13.tgz",
|
||||
"integrity": "sha512-RMCTZ24IF8cMu1Ru1vUfnsP+w/OfI6Wm/wjjXaDMoTrriuQG24AF+VL+UipEoqkmZtqoZcfwRyiOgnEWQX8akw==",
|
||||
"version": "9.0.14",
|
||||
"resolved": "https://registry.npmjs.org/storybook/-/storybook-9.0.14.tgz",
|
||||
"integrity": "sha512-PfVo9kSa4XsDTD2gXFvMRGix032+clBDcUMI4MhUzYxONLiZifnhwch4p/1lG+c3IVN4qkOEgGNc9PEgVMgApw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@storybook/global": "^5.0.0",
|
||||
"@testing-library/jest-dom": "^6.6.3",
|
||||
"@testing-library/user-event": "^14.6.1",
|
||||
"@vitest/expect": "3.0.9",
|
||||
"@vitest/spy": "3.0.9",
|
||||
"@vitest/expect": "3.2.4",
|
||||
"@vitest/spy": "3.2.4",
|
||||
"better-opn": "^3.0.2",
|
||||
"esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0",
|
||||
"esbuild-register": "^3.5.0",
|
||||
@@ -25080,9 +25069,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/storybook/node_modules/ws": {
|
||||
"version": "8.18.2",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz",
|
||||
"integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==",
|
||||
"version": "8.18.3",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
|
||||
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -26141,9 +26130,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tinyspy": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz",
|
||||
"integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==",
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.3.tgz",
|
||||
"integrity": "sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -27711,9 +27700,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-dev-server/node_modules/ws": {
|
||||
"version": "8.18.2",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz",
|
||||
"integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==",
|
||||
"version": "8.18.3",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
|
||||
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
||||
Reference in New Issue
Block a user