diff --git a/apps/isa-app/stories/shared/barcode/barcode-component.stories.ts b/apps/isa-app/stories/shared/barcode/barcode-component.stories.ts new file mode 100644 index 000000000..419769916 --- /dev/null +++ b/apps/isa-app/stories/shared/barcode/barcode-component.stories.ts @@ -0,0 +1,214 @@ +import { + argsToTemplate, + type Meta, + type StoryObj, + moduleMetadata, +} from '@storybook/angular'; +import { BarcodeComponent } from '@isa/shared/barcode'; + +type BarcodeInputs = { + value: string; + format: string; + width: number; + height: number; + displayValue: boolean; + lineColor: string; + background: string; + fontSize: number; + margin: number; +}; + +const meta: Meta = { + title: 'shared/barcode/BarcodeComponent', + + decorators: [ + moduleMetadata({ + imports: [BarcodeComponent], + }), + ], + argTypes: { + value: { + control: { + type: 'text', + }, + description: 'The barcode value to encode', + }, + format: { + control: { + type: 'select', + }, + options: ['CODE128', 'CODE39', 'EAN13', 'EAN8', 'UPC'], + description: 'Barcode format', + }, + width: { + control: { + type: 'number', + min: 1, + max: 10, + }, + description: 'Width of a single bar in pixels', + }, + height: { + control: { + type: 'number', + min: 20, + max: 300, + }, + description: 'Height of the barcode in pixels', + }, + displayValue: { + control: { + type: 'boolean', + }, + description: 'Whether to display the human-readable text', + }, + lineColor: { + control: { + type: 'color', + }, + description: 'Color of the barcode bars and text', + }, + background: { + control: { + type: 'color', + }, + description: 'Background color', + }, + fontSize: { + control: { + type: 'number', + min: 10, + max: 40, + }, + description: 'Font size for the human-readable text', + }, + margin: { + control: { + type: 'number', + min: 0, + max: 50, + }, + description: 'Margin around the barcode in pixels', + }, + }, + render: (args) => ({ + props: args, + template: ``, + }), +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + value: '123456789012', + format: 'CODE128', + width: 2, + height: 100, + displayValue: true, + lineColor: '#000000', + background: '#ffffff', + fontSize: 20, + margin: 10, + }, +}; + +export const ProductEAN: Story = { + args: { + value: '9783161484100', + format: 'CODE128', + width: 2, + height: 100, + displayValue: true, + lineColor: '#000000', + background: '#ffffff', + fontSize: 20, + margin: 10, + }, +}; + +export const CompactNoText: Story = { + args: { + value: '987654321', + format: 'CODE128', + width: 1, + height: 60, + displayValue: false, + lineColor: '#000000', + background: '#ffffff', + fontSize: 20, + margin: 5, + }, +}; + +export const LargeBarcode: Story = { + args: { + value: 'WAREHOUSE2024', + format: 'CODE128', + width: 4, + height: 200, + displayValue: true, + lineColor: '#000000', + background: '#ffffff', + fontSize: 28, + margin: 20, + }, +}; + +export const ColoredBarcode: Story = { + args: { + value: 'PRODUCT001', + format: 'CODE128', + width: 2, + height: 100, + displayValue: true, + lineColor: '#0066CC', + background: '#F0F8FF', + fontSize: 20, + margin: 10, + }, +}; + +export const RedBarcode: Story = { + args: { + value: 'URGENT2024', + format: 'CODE128', + width: 3, + height: 120, + displayValue: true, + lineColor: '#CC0000', + background: '#FFEEEE', + fontSize: 22, + margin: 15, + }, +}; + +export const MinimalMargin: Story = { + args: { + value: '1234567890', + format: 'CODE128', + width: 2, + height: 80, + displayValue: true, + lineColor: '#000000', + background: '#ffffff', + fontSize: 16, + margin: 0, + }, +}; + +export const SmallFont: Story = { + args: { + value: '555123456789', + format: 'CODE128', + width: 2, + height: 100, + displayValue: true, + lineColor: '#000000', + background: '#ffffff', + fontSize: 12, + margin: 10, + }, +}; diff --git a/apps/isa-app/stories/shared/barcode/barcode.stories.ts b/apps/isa-app/stories/shared/barcode/barcode.stories.ts new file mode 100644 index 000000000..ab3cfd973 --- /dev/null +++ b/apps/isa-app/stories/shared/barcode/barcode.stories.ts @@ -0,0 +1,214 @@ +import { + argsToTemplate, + type Meta, + type StoryObj, + moduleMetadata, +} from '@storybook/angular'; +import { BarcodeDirective } from '@isa/shared/barcode'; + +type BarcodeInputs = { + value: string; + format: string; + width: number; + height: number; + displayValue: boolean; + lineColor: string; + background: string; + fontSize: number; + margin: number; +}; + +const meta: Meta = { + title: 'shared/barcode/Barcode', + + decorators: [ + moduleMetadata({ + imports: [BarcodeDirective], + }), + ], + argTypes: { + value: { + control: { + type: 'text', + }, + description: 'The barcode value to encode', + }, + format: { + control: { + type: 'select', + }, + options: ['CODE128', 'CODE39', 'EAN13', 'EAN8', 'UPC'], + description: 'Barcode format', + }, + width: { + control: { + type: 'number', + min: 1, + max: 10, + }, + description: 'Width of a single bar in pixels', + }, + height: { + control: { + type: 'number', + min: 20, + max: 300, + }, + description: 'Height of the barcode in pixels', + }, + displayValue: { + control: { + type: 'boolean', + }, + description: 'Whether to display the human-readable text', + }, + lineColor: { + control: { + type: 'color', + }, + description: 'Color of the barcode bars and text', + }, + background: { + control: { + type: 'color', + }, + description: 'Background color', + }, + fontSize: { + control: { + type: 'number', + min: 10, + max: 40, + }, + description: 'Font size for the human-readable text', + }, + margin: { + control: { + type: 'number', + min: 0, + max: 50, + }, + description: 'Margin around the barcode in pixels', + }, + }, + render: (args) => ({ + props: args, + template: ``, + }), +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + value: '123456789012', + format: 'CODE128', + width: 2, + height: 100, + displayValue: true, + lineColor: '#000000', + background: '#ffffff', + fontSize: 20, + margin: 10, + }, +}; + +export const ProductEAN: Story = { + args: { + value: '9783161484100', + format: 'CODE128', + width: 2, + height: 100, + displayValue: true, + lineColor: '#000000', + background: '#ffffff', + fontSize: 20, + margin: 10, + }, +}; + +export const CompactNoText: Story = { + args: { + value: '987654321', + format: 'CODE128', + width: 1, + height: 60, + displayValue: false, + lineColor: '#000000', + background: '#ffffff', + fontSize: 20, + margin: 5, + }, +}; + +export const LargeBarcode: Story = { + args: { + value: 'WAREHOUSE2024', + format: 'CODE128', + width: 4, + height: 200, + displayValue: true, + lineColor: '#000000', + background: '#ffffff', + fontSize: 28, + margin: 20, + }, +}; + +export const ColoredBarcode: Story = { + args: { + value: 'PRODUCT001', + format: 'CODE128', + width: 2, + height: 100, + displayValue: true, + lineColor: '#0066CC', + background: '#F0F8FF', + fontSize: 20, + margin: 10, + }, +}; + +export const RedBarcode: Story = { + args: { + value: 'URGENT2024', + format: 'CODE128', + width: 3, + height: 120, + displayValue: true, + lineColor: '#CC0000', + background: '#FFEEEE', + fontSize: 22, + margin: 15, + }, +}; + +export const MinimalMargin: Story = { + args: { + value: '1234567890', + format: 'CODE128', + width: 2, + height: 80, + displayValue: true, + lineColor: '#000000', + background: '#ffffff', + fontSize: 16, + margin: 0, + }, +}; + +export const SmallFont: Story = { + args: { + value: '555123456789', + format: 'CODE128', + width: 2, + height: 100, + displayValue: true, + lineColor: '#000000', + background: '#ffffff', + fontSize: 12, + margin: 10, + }, +}; diff --git a/apps/isa-app/stories/ui/switch/ui-icon-switch.stories.ts b/apps/isa-app/stories/ui/switch/ui-icon-switch.stories.ts index 8a39598c8..933b96739 100644 --- a/apps/isa-app/stories/ui/switch/ui-icon-switch.stories.ts +++ b/apps/isa-app/stories/ui/switch/ui-icon-switch.stories.ts @@ -1,5 +1,5 @@ import { argsToTemplate, type Meta, type StoryObj } from '@storybook/angular'; -import { IconSwitchComponent, IconSwitchColor } from '@isa/ui/switch'; +import { SwitchComponent, IconSwitchColor } from '@isa/ui/switch'; import { provideIcons } from '@ng-icons/core'; import { isaFiliale, IsaIcons, isaNavigationDashboard } from '@isa/icons'; @@ -11,7 +11,7 @@ type IconSwitchComponentInputs = { }; const meta: Meta = { - component: IconSwitchComponent, + component: SwitchComponent, title: 'ui/switch/IconSwitch', decorators: [ (story) => ({ @@ -49,12 +49,12 @@ const meta: Meta = { }, render: (args) => ({ props: args, - template: ``, + template: ``, }), }; export default meta; -type Story = StoryObj; +type Story = StoryObj; export const Default: Story = { args: {}, diff --git a/docs/library-reference.md b/docs/library-reference.md index 09161b223..b09597a58 100644 --- a/docs/library-reference.md +++ b/docs/library-reference.md @@ -1,11 +1,11 @@ # Library Reference Guide -> **Last Updated:** 2025-01-10 -> **Angular Version:** 20.1.2 +> **Last Updated:** 2025-11-20 +> **Angular Version:** 20.3.6 > **Nx Version:** 21.3.2 -> **Total Libraries:** 63 +> **Total Libraries:** 72 -All 63 libraries in the monorepo have comprehensive README.md documentation located at `libs/[domain]/[layer]/[feature]/README.md`. +All 72 libraries in the monorepo have comprehensive README.md documentation located at `libs/[domain]/[layer]/[feature]/README.md`. **IMPORTANT: Always use the `docs-researcher` subagent** to retrieve and analyze library documentation. This keeps the main context clean and prevents pollution. @@ -23,7 +23,7 @@ A comprehensive product availability service for Angular applications supporting ## Catalogue Domain (1 library) ### `@isa/catalogue/data-access` -A comprehensive product catalogue search and availability service for Angular applications, providing catalog item search, loyalty program integration, and specialized availability validation for download and delivery order types. +A comprehensive product catalogue search service for Angular applications, providing catalog item search and loyalty program integration. **Location:** `libs/catalogue/data-access/` @@ -37,7 +37,7 @@ A comprehensive checkout and shopping cart management library for Angular applic **Location:** `libs/checkout/data-access/` ### `@isa/checkout/feature/reward-order-confirmation` -This library was generated with [Nx](https://nx.dev). +## Overview **Location:** `libs/checkout/feature/reward-order-confirmation/` @@ -57,8 +57,6 @@ A comprehensive loyalty rewards catalog feature for Angular applications support **Location:** `libs/checkout/feature/reward-catalog/` ### `@isa/checkout/shared/reward-selection-dialog` -Angular library for managing reward selection in shopping cart context. Allows users to toggle between regular purchase and reward redemption using bonus points. - **Location:** `libs/checkout/shared/reward-selection-dialog/` --- @@ -85,11 +83,9 @@ A comprehensive print management library for Angular applications providing prin ## Core Libraries (6 libraries) ### `@isa/core/auth` -Type-safe role-based authorization utilities with Angular signals integration for the ISA Frontend application. Provides Role enum, RoleService for programmatic checks, and IfRoleDirective for declarative template rendering with automatic JWT token parsing via OAuthService. +Type-safe role-based authorization utilities with Angular signals integration for the ISA Frontend application. **Location:** `libs/core/auth/` -**Testing:** Vitest (18 passing tests) -**Features:** Signal-based reactivity, type-safe Role enum, zero-configuration OAuth2 integration ### `@isa/core/config` A lightweight, type-safe configuration management system for Angular applications with runtime validation and nested object access. @@ -118,19 +114,37 @@ A sophisticated tab management system for Angular applications providing browser --- -## CRM Domain (1 library) +## CRM Domain (5 libraries) ### `@isa/crm/data-access` -A comprehensive Customer Relationship Management (CRM) data access library for Angular applications providing customer, shipping address, payer, and bonus card management with reactive data loading using Angular resources. - **Location:** `libs/crm/data-access/` +### `@isa/crm/feature/customer-bon-redemption` +This library was generated with [Nx](https://nx.dev). + +**Location:** `libs/crm/feature/customer-bon-redemption/` + +### `@isa/crm/feature/customer-booking` +This library was generated with [Nx](https://nx.dev). + +**Location:** `libs/crm/feature/customer-booking/` + +### `@isa/crm/feature/customer-card-transactions` +This library was generated with [Nx](https://nx.dev). + +**Location:** `libs/crm/feature/customer-card-transactions/` + +### `@isa/crm/feature/customer-loyalty-cards` +This library was generated with [Nx](https://nx.dev). + +**Location:** `libs/crm/feature/customer-loyalty-cards/` + --- ## Icons (1 library) ### `@isa/icons` -This library was generated with [Nx](https://nx.dev). +## Overview **Location:** `libs/icons/` @@ -187,16 +201,16 @@ A specialized Angular component library for displaying and managing return recei ## Remission Domain (8 libraries) -### `@isa/remission/feature/remission-list` -Feature module providing the main remission list view with filtering, searching, item selection, and remitting capabilities for department ("Abteilung") and mandatory ("Pflicht") return workflows. - -**Location:** `libs/remission/feature/remission-list/` - ### `@isa/remission/data-access` A comprehensive remission (returns) management system for Angular applications supporting mandatory returns (Pflichtremission) and department overflow returns (Abteilungsremission) in retail inventory operations. **Location:** `libs/remission/data-access/` +### `@isa/remission/feature/remission-list` +Feature module providing the main remission list view with filtering, searching, item selection, and remitting capabilities for department ("Abteilung") and mandatory ("Pflicht") return workflows. + +**Location:** `libs/remission/feature/remission-list/` + ### `@isa/remission/feature/remission-return-receipt-details` Feature component for displaying detailed view of a return receipt ("Warenbegleitschein") with items, actions, and completion workflows. @@ -207,35 +221,45 @@ Feature component providing a comprehensive list view of all return receipts wit **Location:** `libs/remission/feature/remission-return-receipt-list/` -### `@isa/remission/shared/return-receipt-actions` -Angular standalone components for managing return receipt actions including deletion, continuation, and completion workflows in the remission process. - -**Location:** `libs/remission/shared/return-receipt-actions/` - ### `@isa/remission/shared/product` A collection of Angular standalone components for displaying product information in remission workflows, including product details, stock information, and shelf metadata. **Location:** `libs/remission/shared/product/` -### `@isa/remission/shared/search-item-to-remit-dialog` -Angular dialog component for searching and adding items to remission lists that are not on the mandatory return list (Pflichtremission). - -**Location:** `libs/remission/shared/search-item-to-remit-dialog/` - ### `@isa/remission/shared/remission-start-dialog` Angular dialog component for initiating remission processes with two-step workflow: creating return receipts and assigning package numbers. **Location:** `libs/remission/shared/remission-start-dialog/` +### `@isa/remission/shared/return-receipt-actions` +Angular standalone components for managing return receipt actions including deletion, continuation, and completion workflows in the remission process. + +**Location:** `libs/remission/shared/return-receipt-actions/` + +### `@isa/remission/shared/search-item-to-remit-dialog` +Angular dialog component for searching and adding items to remission lists that are not on the mandatory return list (Pflichtremission). + +**Location:** `libs/remission/shared/search-item-to-remit-dialog/` + --- -## Shared Component Libraries (7 libraries) +## Shared Component Libraries (9 libraries) ### `@isa/shared/address` Comprehensive Angular components for displaying addresses in both multi-line and inline formats with automatic country name resolution and intelligent formatting. **Location:** `libs/shared/address/` +### `@isa/shared/barcode` +Angular library for generating Code 128 barcodes using [JsBarcode](https://github.com/lindell/JsBarcode). + +**Location:** `libs/shared/barcode/` + +### `@isa/shared/delivery` +This library was generated with [Nx](https://nx.dev). + +**Location:** `libs/shared/delivery/` + ### `@isa/shared/filter` A powerful and flexible filtering library for Angular applications that provides a complete solution for implementing filters, search functionality, and sorting capabilities. @@ -268,7 +292,7 @@ An accessible, feature-rich Angular quantity selector component with dropdown pr --- -## UI Component Libraries (16 libraries) +## UI Component Libraries (18 libraries) ### `@isa/ui/label` A flexible label component for displaying tags and notices with configurable priority levels across Angular applications. @@ -286,7 +310,7 @@ A comprehensive button component library for Angular applications providing five **Location:** `libs/ui/buttons/` ### `@isa/ui/carousel` -A horizontal scroll container component with left/right navigation arrows, responsive behavior, keyboard support, and auto-hide functionality for Angular applications. +A horizontal scroll container component with left/right navigation arrows, responsive behavior, keyboard support, and auto-hide functionality. **Location:** `libs/ui/carousel/` @@ -345,6 +369,11 @@ A lightweight Angular structural directive and component for displaying skeleton **Location:** `libs/ui/skeleton-loader/` +### `@isa/ui/switch` +This library provides a toggle switch component with an icon for Angular applications. + +**Location:** `libs/ui/switch/` + ### `@isa/ui/toolbar` A flexible toolbar container component for Angular applications with configurable sizing and content projection. @@ -357,13 +386,23 @@ A flexible tooltip library for Angular applications, built with Angular CDK over --- -## Utility Libraries (3 libraries) +## Utility Libraries (5 libraries) ### `@isa/utils/ean-validation` Lightweight Angular utility library for validating EAN (European Article Number) barcodes with reactive forms integration and standalone validation functions. **Location:** `libs/utils/ean-validation/` +### `@isa/utils/format-name` +This library was generated with [Nx](https://nx.dev). + +**Location:** `libs/utils/format-name/` + +### `@isa/utils/positive-integer-input` +This library was generated with [Nx](https://nx.dev). + +**Location:** `libs/utils/positive-integer-input/` + ### `@isa/utils/scroll-position` ## Overview diff --git a/libs/crm/feature/customer-loyalty-cards/src/lib/components/customer-card/customer-card.component.html b/libs/crm/feature/customer-loyalty-cards/src/lib/components/customer-card/customer-card.component.html index 6fcca2e40..47f65d600 100644 --- a/libs/crm/feature/customer-loyalty-cards/src/lib/components/customer-card/customer-card.component.html +++ b/libs/crm/feature/customer-loyalty-cards/src/lib/components/customer-card/customer-card.component.html @@ -22,19 +22,19 @@ - -
+ - -
- Barcode: {{ card().code }} -
-
+ class="rounded-[0.25rem] overflow-hidden" + /> diff --git a/libs/crm/feature/customer-loyalty-cards/src/lib/components/customer-card/customer-card.component.spec.ts b/libs/crm/feature/customer-loyalty-cards/src/lib/components/customer-card/customer-card.component.spec.ts index 05a51a7f7..596342913 100644 --- a/libs/crm/feature/customer-loyalty-cards/src/lib/components/customer-card/customer-card.component.spec.ts +++ b/libs/crm/feature/customer-loyalty-cards/src/lib/components/customer-card/customer-card.component.spec.ts @@ -14,8 +14,7 @@ describe('CustomerCardComponent', () => { isActive: true, isPrimary: true, totalPoints: 1500, - cardNumber: '1234-5678-9012-3456', - } as BonusCardInfo; + }; beforeEach(() => { TestBed.configureTestingModule({ @@ -84,7 +83,7 @@ describe('CustomerCardComponent', () => { fixture.componentRef.setInput('card', mockCard); fixture.detectChanges(); - expect(component.card().cardNumber).toBe('1234-5678-9012-3456'); + expect(component.card().code).toBe('CARD-1'); }); }); }); diff --git a/libs/crm/feature/customer-loyalty-cards/src/lib/components/customer-card/customer-card.component.ts b/libs/crm/feature/customer-loyalty-cards/src/lib/components/customer-card/customer-card.component.ts index 1b0630885..bc6a45edd 100644 --- a/libs/crm/feature/customer-loyalty-cards/src/lib/components/customer-card/customer-card.component.ts +++ b/libs/crm/feature/customer-loyalty-cards/src/lib/components/customer-card/customer-card.component.ts @@ -1,10 +1,6 @@ -import { - ChangeDetectionStrategy, - Component, - input, - output, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, input } from '@angular/core'; import { BonusCardInfo } from '@isa/crm/data-access'; +import { BarcodeComponent } from '@isa/shared/barcode'; import { LockCustomerCardComponent } from '../lock-customer-card/lock-customer-card.component'; /** @@ -31,11 +27,19 @@ import { LockCustomerCardComponent } from '../lock-customer-card/lock-customer-c templateUrl: './customer-card.component.html', styleUrl: './customer-card.component.css', changeDetection: ChangeDetectionStrategy.OnPush, - imports: [LockCustomerCardComponent], + imports: [BarcodeComponent, LockCustomerCardComponent], }) export class CustomerCardComponent { /** * Bonus card data to display. */ readonly card = input.required(); + + /** + * Barcode dimensions in rem, converted to pixels for JsBarcode. + * Base: 1rem = 16px + */ + protected readonly barcodeHeight = 4.5 * 16; // 4.5rem = 72px + protected readonly barcodeWidth = 0.125 * 16; // 0.125rem = 2px + protected readonly barcodeMargin = 0.5 * 16; // 0.5rem = 8px } diff --git a/libs/shared/barcode/README.md b/libs/shared/barcode/README.md new file mode 100644 index 000000000..a327a384b --- /dev/null +++ b/libs/shared/barcode/README.md @@ -0,0 +1,376 @@ +# @isa/shared/barcode + +Angular library for generating Code 128 barcodes using [JsBarcode](https://github.com/lindell/JsBarcode). + +Provides both a **component** (easier to use) and a **directive** (more flexible) for rendering barcodes. + +## Features + +- ✅ Standalone Angular component & directive +- ✅ Code 128 barcode format support +- ✅ Signal-based reactive inputs +- ✅ SVG-based rendering (vector graphics) +- ✅ Highly customizable (colors, size, margins, fonts) +- ✅ Automatic re-rendering on input changes +- ✅ Built-in logging with `@isa/core/logging` +- ✅ Comprehensive Vitest test coverage + +## Installation + +### Component (Recommended) + +The component provides the easiest way to add barcodes: + +```typescript +import { BarcodeComponent } from '@isa/shared/barcode'; + +@Component({ + selector: 'app-my-component', + standalone: true, + imports: [BarcodeComponent], + template: ` + + `, +}) +export class MyComponent { + productEan = '9783161484100'; +} +``` + +### Directive (Advanced) + +Use the directive when you need to apply the barcode to an existing SVG element: + +```typescript +import { BarcodeDirective } from '@isa/shared/barcode'; + +@Component({ + selector: 'app-my-component', + standalone: true, + imports: [BarcodeDirective], + template: ` + + `, +}) +export class MyComponent { + productEan = '9783161484100'; +} +``` + +## Basic Usage + +### Component - Minimal Example + +```html + +``` + +### Directive - Minimal Example + +```html + +``` + +### Component - With Custom Options + +```html + +``` + +### Directive - With Custom Options + +```html + +``` + +### TypeScript Example + +```typescript +import { Component, signal } from '@angular/core'; +import { BarcodeComponent } from '@isa/shared/barcode'; + +@Component({ + selector: 'app-product-label', + standalone: true, + imports: [BarcodeComponent], + template: ` +
+

Product Label

+ +
+ `, +}) +export class ProductLabelComponent { + ean = signal('4006381333634'); +} +``` + +## API Reference + +### Inputs + +| Input | Type | Default | Description | +|-------|------|---------|-------------| +| `value` | `string` | *(required)* | The barcode value to encode | +| `format` | `string` | `'CODE128'` | Barcode format (supports CODE128, CODE39, EAN13, etc.) | +| `width` | `number` | `2` | Width of a single bar in pixels | +| `height` | `number` | `100` | Height of the barcode in pixels | +| `displayValue` | `boolean` | `true` | Whether to display the human-readable text below the barcode | +| `lineColor` | `string` | `'#000000'` | Color of the barcode bars and text | +| `background` | `string` | `'#ffffff'` | Background color | +| `fontSize` | `number` | `20` | Font size for the human-readable text | +| `margin` | `number` | `10` | Margin around the barcode in pixels | + +### Selector + +```typescript +selector: 'svg[sharedBarcode]' +``` + +The directive must be applied to an `` element. + +## Examples + +### Component Examples + +#### Large Barcode + +```html + +``` + +#### Compact Barcode (No Text) + +```html + +``` + +#### Colored Barcode + +```html + +``` + +### Directive Examples + +#### Large Barcode + +```html + +``` + +#### Compact Barcode (No Text) + +```html + +``` + +#### Colored Barcode + +```html + +``` + +### Reactive Value Updates + +```typescript +@Component({ + selector: 'app-barcode-scanner', + standalone: true, + imports: [BarcodeDirective], + template: ` + + + `, +}) +export class BarcodeScannerComponent { + scannedValue = ''; +} +``` + +The barcode automatically re-renders when the value changes. + +### Multiple Barcodes + +```typescript +@Component({ + selector: 'app-product-list', + standalone: true, + imports: [BarcodeDirective, CommonModule], + template: ` + @for (product of products; track product.id) { +
+

{{ product.name }}

+ +
+ } + `, +}) +export class ProductListComponent { + products = [ + { id: 1, name: 'Product A', ean: '1234567890123' }, + { id: 2, name: 'Product B', ean: '9876543210987' }, + ]; +} +``` + +## Styling + +The directive renders an SVG element. You can apply CSS styles to the parent SVG element: + +```css +svg[sharedBarcode] { + border: 1px solid #ccc; + padding: 8px; + border-radius: 4px; + background: white; +} +``` + +## Error Handling + +The directive includes built-in error handling: + +- **Invalid values**: Logged as warnings (check browser console with logging enabled) +- **Empty values**: Handled gracefully without crashing +- **Rendering errors**: Caught and logged with `@isa/core/logging` + +## Testing + +The library uses **Vitest** with **Angular Testing Utilities** for testing. + +### Running Tests + +```bash +# Run tests +npx nx test shared-barcode + +# Run tests with coverage +npx nx test shared-barcode --coverage.enabled=true + +# Skip cache for fresh test run +npx nx test shared-barcode --skip-nx-cache +``` + +### Test Reports + +- **JUnit XML**: `testresults/junit-shared-barcode.xml` +- **Cobertura Coverage**: `coverage/libs/shared/barcode/cobertura-coverage.xml` + +### Example Test + +```typescript +import { Component } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { BarcodeDirective } from '@isa/shared/barcode'; + +@Component({ + standalone: true, + imports: [BarcodeDirective], + template: '', +}) +class TestComponent { + value = '123456789'; +} + +describe('BarcodeDirective', () => { + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [TestComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(TestComponent); + fixture.detectChanges(); + }); + + it('should render barcode', () => { + const svg = fixture.nativeElement.querySelector('svg'); + expect(svg).toBeTruthy(); + }); +}); +``` + +## Browser Support + +Supports all modern browsers with SVG rendering capabilities: +- Chrome/Edge (latest) +- Firefox (latest) +- Safari (latest) + +## Dependencies + +- **JsBarcode**: `^3.12.1` - Core barcode generation library +- **@types/jsbarcode**: TypeScript type definitions + +## Related Libraries + +- `@isa/shared/scanner`: Barcode scanning functionality +- `@isa/shared/product-image`: Product image display with CDN support + +## License + +Internal ISA-Frontend library. Not for external distribution. + +## Support + +For issues or feature requests, consult the ISA-Frontend development team. diff --git a/libs/shared/barcode/eslint.config.cjs b/libs/shared/barcode/eslint.config.cjs new file mode 100644 index 000000000..0f8b00db8 --- /dev/null +++ b/libs/shared/barcode/eslint.config.cjs @@ -0,0 +1,34 @@ +const nx = require('@nx/eslint-plugin'); +const baseConfig = require('../../../eslint.config.js'); + +module.exports = [ + ...baseConfig, + ...nx.configs['flat/angular'], + ...nx.configs['flat/angular-template'], + { + files: ['**/*.ts'], + rules: { + '@angular-eslint/directive-selector': [ + 'error', + { + type: 'attribute', + prefix: 'shared', + style: 'camelCase', + }, + ], + '@angular-eslint/component-selector': [ + 'error', + { + type: 'element', + prefix: 'shared', + style: 'kebab-case', + }, + ], + }, + }, + { + files: ['**/*.html'], + // Override or add rules here + rules: {}, + }, +]; diff --git a/libs/shared/barcode/project.json b/libs/shared/barcode/project.json new file mode 100644 index 000000000..74ab65f49 --- /dev/null +++ b/libs/shared/barcode/project.json @@ -0,0 +1,20 @@ +{ + "name": "shared-barcode", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/shared/barcode/src", + "prefix": "shared", + "projectType": "library", + "tags": [], + "targets": { + "test": { + "executor": "@nx/vite:test", + "outputs": ["{options.reportsDirectory}"], + "options": { + "reportsDirectory": "../../../coverage/libs/shared/barcode" + } + }, + "lint": { + "executor": "@nx/eslint:lint" + } + } +} diff --git a/libs/shared/barcode/src/index.ts b/libs/shared/barcode/src/index.ts new file mode 100644 index 000000000..e7a6aa2bc --- /dev/null +++ b/libs/shared/barcode/src/index.ts @@ -0,0 +1,2 @@ +export * from './lib/barcode.directive'; +export * from './lib/barcode.component'; diff --git a/libs/shared/barcode/src/lib/barcode.component.spec.ts b/libs/shared/barcode/src/lib/barcode.component.spec.ts new file mode 100644 index 000000000..20d40e19c --- /dev/null +++ b/libs/shared/barcode/src/lib/barcode.component.spec.ts @@ -0,0 +1,213 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { BarcodeComponent } from './barcode.component'; + +// Mock JsBarcode +vi.mock('jsbarcode', () => ({ + default: vi.fn((element, value, options) => { + // Simulate JsBarcode by adding a rect element to the SVG + if (element && element.tagName === 'svg') { + const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect'); + rect.setAttribute('data-value', value); + rect.setAttribute('data-format', options?.format || 'CODE128'); + element.appendChild(rect); + + // Call valid callback if provided + if (options?.valid) { + const isValid = value && value.length > 0; + options.valid(isValid); + } + } + }), +})); + +describe('BarcodeComponent', () => { + let component: BarcodeComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [BarcodeComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(BarcodeComponent); + component = fixture.componentInstance; + }); + + describe('Initialization', () => { + it('should create', () => { + fixture.componentRef.setInput('value', '123456789'); + fixture.detectChanges(); + + expect(component).toBeTruthy(); + }); + + it('should render an SVG element', () => { + fixture.componentRef.setInput('value', '123456789'); + fixture.detectChanges(); + + const svg = fixture.nativeElement.querySelector('svg'); + expect(svg).toBeTruthy(); + expect(svg.tagName).toBe('svg'); + }); + + it('should apply the barcode directive to the SVG', () => { + fixture.componentRef.setInput('value', '123456789'); + fixture.detectChanges(); + + const svg = fixture.nativeElement.querySelector('svg[sharedBarcode]'); + expect(svg).toBeTruthy(); + }); + }); + + describe('Input Bindings', () => { + it('should pass value input to directive', () => { + fixture.componentRef.setInput('value', 'TEST123'); + fixture.detectChanges(); + + expect(component.value()).toBe('TEST123'); + }); + + it('should pass format input to directive', () => { + fixture.componentRef.setInput('value', '123'); + fixture.componentRef.setInput('format', 'CODE39'); + fixture.detectChanges(); + + expect(component.format()).toBe('CODE39'); + }); + + it('should pass width input to directive', () => { + fixture.componentRef.setInput('value', '123'); + fixture.componentRef.setInput('width', 4); + fixture.detectChanges(); + + expect(component.width()).toBe(4); + }); + + it('should pass height input to directive', () => { + fixture.componentRef.setInput('value', '123'); + fixture.componentRef.setInput('height', 200); + fixture.detectChanges(); + + expect(component.height()).toBe(200); + }); + + it('should pass displayValue input to directive', () => { + fixture.componentRef.setInput('value', '123'); + fixture.componentRef.setInput('displayValue', false); + fixture.detectChanges(); + + expect(component.displayValue()).toBe(false); + }); + + it('should pass lineColor input to directive', () => { + fixture.componentRef.setInput('value', '123'); + fixture.componentRef.setInput('lineColor', '#FF0000'); + fixture.detectChanges(); + + expect(component.lineColor()).toBe('#FF0000'); + }); + + it('should pass background input to directive', () => { + fixture.componentRef.setInput('value', '123'); + fixture.componentRef.setInput('background', '#F0F0F0'); + fixture.detectChanges(); + + expect(component.background()).toBe('#F0F0F0'); + }); + + it('should pass fontSize input to directive', () => { + fixture.componentRef.setInput('value', '123'); + fixture.componentRef.setInput('fontSize', 24); + fixture.detectChanges(); + + expect(component.fontSize()).toBe(24); + }); + + it('should pass margin input to directive', () => { + fixture.componentRef.setInput('value', '123'); + fixture.componentRef.setInput('margin', 15); + fixture.detectChanges(); + + expect(component.margin()).toBe(15); + }); + }); + + describe('Default Values', () => { + it('should use default format CODE128', () => { + fixture.componentRef.setInput('value', '123'); + fixture.detectChanges(); + + expect(component.format()).toBe('CODE128'); + }); + + it('should use default width 2', () => { + fixture.componentRef.setInput('value', '123'); + fixture.detectChanges(); + + expect(component.width()).toBe(2); + }); + + it('should use default height 100', () => { + fixture.componentRef.setInput('value', '123'); + fixture.detectChanges(); + + expect(component.height()).toBe(100); + }); + + it('should use default displayValue true', () => { + fixture.componentRef.setInput('value', '123'); + fixture.detectChanges(); + + expect(component.displayValue()).toBe(true); + }); + + it('should use default lineColor #000000', () => { + fixture.componentRef.setInput('value', '123'); + fixture.detectChanges(); + + expect(component.lineColor()).toBe('#000000'); + }); + + it('should use default background #ffffff', () => { + fixture.componentRef.setInput('value', '123'); + fixture.detectChanges(); + + expect(component.background()).toBe('#ffffff'); + }); + + it('should use default fontSize 20', () => { + fixture.componentRef.setInput('value', '123'); + fixture.detectChanges(); + + expect(component.fontSize()).toBe(20); + }); + + it('should use default margin 10', () => { + fixture.componentRef.setInput('value', '123'); + fixture.detectChanges(); + + expect(component.margin()).toBe(10); + }); + }); + + describe('Barcode Rendering', () => { + it('should render barcode with custom value', () => { + fixture.componentRef.setInput('value', '987654321'); + fixture.detectChanges(); + + const svg = fixture.nativeElement.querySelector('svg'); + expect(svg.children.length).toBeGreaterThan(0); + }); + + it('should update when value changes', () => { + fixture.componentRef.setInput('value', 'INITIAL'); + fixture.detectChanges(); + + fixture.componentRef.setInput('value', 'UPDATED'); + fixture.detectChanges(); + + expect(component.value()).toBe('UPDATED'); + }); + }); +}); diff --git a/libs/shared/barcode/src/lib/barcode.component.ts b/libs/shared/barcode/src/lib/barcode.component.ts new file mode 100644 index 000000000..88967ae9d --- /dev/null +++ b/libs/shared/barcode/src/lib/barcode.component.ts @@ -0,0 +1,78 @@ +import { ChangeDetectionStrategy, Component, input } from '@angular/core'; +import { BarcodeDirective } from './barcode.directive'; + +/** + * Component wrapper for the barcode directive that provides an easier-to-use API. + * Renders a Code 128 barcode as an SVG element. + * + * @example + * ```html + * + * ``` + */ +@Component({ + selector: 'shared-barcode', + standalone: true, + imports: [BarcodeDirective], + template: ` + + `, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class BarcodeComponent { + /** + * The barcode value to encode (required) + */ + value = input.required(); + + /** + * Barcode format (default: CODE128) + */ + format = input('CODE128'); + + /** + * Width of a single bar in pixels (default: 2) + */ + width = input(2); + + /** + * Height of the barcode in pixels (default: 100) + */ + height = input(100); + + /** + * Whether to display the human-readable value below the barcode (default: true) + */ + displayValue = input(true); + + /** + * Color of the barcode bars and text (default: #000000) + */ + lineColor = input('#000000'); + + /** + * Background color (default: #ffffff) + */ + background = input('#ffffff'); + + /** + * Font size for the human-readable text (default: 20) + */ + fontSize = input(20); + + /** + * Margin around the barcode in pixels (default: 10) + */ + margin = input(10); +} diff --git a/libs/shared/barcode/src/lib/barcode.directive.spec.ts b/libs/shared/barcode/src/lib/barcode.directive.spec.ts new file mode 100644 index 000000000..386b7789b --- /dev/null +++ b/libs/shared/barcode/src/lib/barcode.directive.spec.ts @@ -0,0 +1,243 @@ +import { Component } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { BarcodeDirective } from './barcode.directive'; + +// Mock JsBarcode +vi.mock('jsbarcode', () => ({ + default: vi.fn((element, value, options) => { + // Simulate JsBarcode by adding a rect element to the SVG + if (element && element.tagName === 'svg') { + const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect'); + rect.setAttribute('data-value', value); + rect.setAttribute('data-format', options?.format || 'CODE128'); + element.appendChild(rect); + + // Call valid callback if provided + if (options?.valid) { + const isValid = value && value.length > 0; + options.valid(isValid); + } + } + }), +})); + +@Component({ + standalone: true, + imports: [BarcodeDirective], + template: ` + + `, +}) +class TestHostComponent { + barcodeValue = '123456789'; + format = 'CODE128'; + width = 2; + height = 100; + displayValue = true; +} + +describe('BarcodeDirective', () => { + let fixture: ComponentFixture; + let component: TestHostComponent; + let svgElement: SVGElement; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [TestHostComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(TestHostComponent); + component = fixture.componentInstance; + svgElement = fixture.nativeElement.querySelector('svg'); + fixture.detectChanges(); + }); + + describe('Initialization', () => { + it('should create the directive', () => { + expect(component).toBeTruthy(); + }); + + it('should apply to SVG element', () => { + expect(svgElement).toBeTruthy(); + expect(svgElement.tagName).toBe('svg'); + }); + + it('should render barcode on initialization', () => { + // JsBarcode adds child elements to the SVG + expect(svgElement.children.length).toBeGreaterThan(0); + }); + }); + + describe('Barcode Rendering', () => { + it('should render barcode with default CODE128 format', () => { + const rect = svgElement.querySelector('rect'); + expect(rect).toBeTruthy(); + }); + + it('should render barcode with custom value', () => { + component.barcodeValue = '987654321'; + fixture.detectChanges(); + + // SVG should be updated with new value + expect(svgElement.children.length).toBeGreaterThan(0); + }); + + it('should update barcode when value changes', () => { + component.barcodeValue = 'NEWVALUE123'; + fixture.detectChanges(); + + // Barcode should be re-rendered (may have different number of bars) + expect(svgElement.children.length).toBeGreaterThan(0); + }); + + it('should render with custom width', () => { + component.width = 4; + fixture.detectChanges(); + + expect(svgElement.children.length).toBeGreaterThan(0); + }); + + it('should render with custom height', () => { + component.height = 200; + fixture.detectChanges(); + + const rect = svgElement.querySelector('rect'); + expect(rect).toBeTruthy(); + }); + + it('should render without display value when disabled', () => { + component.displayValue = false; + fixture.detectChanges(); + + // Check that SVG is still rendered + expect(svgElement.children.length).toBeGreaterThan(0); + }); + }); + + describe('Input Changes', () => { + it('should re-render when format changes', () => { + component.format = 'CODE128'; + fixture.detectChanges(); + + expect(svgElement.children.length).toBeGreaterThan(0); + }); + + it('should handle multiple rapid value changes', () => { + component.barcodeValue = 'VALUE1'; + fixture.detectChanges(); + + component.barcodeValue = 'VALUE2'; + fixture.detectChanges(); + + component.barcodeValue = 'VALUE3'; + fixture.detectChanges(); + + expect(svgElement.children.length).toBeGreaterThan(0); + }); + }); + + describe('Error Handling', () => { + it('should handle empty value gracefully', () => { + // JsBarcode should handle this or log warning + component.barcodeValue = ''; + fixture.detectChanges(); + + // Directive should not crash + expect(component).toBeTruthy(); + }); + + it('should handle very long values', () => { + component.barcodeValue = '1'.repeat(100); + fixture.detectChanges(); + + expect(svgElement.children.length).toBeGreaterThan(0); + }); + }); + + describe('Customization Options', () => { + it('should render with custom line color', () => { + @Component({ + standalone: true, + imports: [BarcodeDirective], + template: ` + + `, + }) + class ColorTestComponent {} + + const colorFixture = TestBed.createComponent(ColorTestComponent); + colorFixture.detectChanges(); + + const svg = colorFixture.nativeElement.querySelector('svg'); + expect(svg).toBeTruthy(); + expect(svg.children.length).toBeGreaterThan(0); + }); + + it('should render with custom background', () => { + @Component({ + standalone: true, + imports: [BarcodeDirective], + template: ` + + `, + }) + class BackgroundTestComponent {} + + const bgFixture = TestBed.createComponent(BackgroundTestComponent); + bgFixture.detectChanges(); + + const svg = bgFixture.nativeElement.querySelector('svg'); + expect(svg).toBeTruthy(); + }); + + it('should render with custom font size', () => { + @Component({ + standalone: true, + imports: [BarcodeDirective], + template: ` + + `, + }) + class FontSizeTestComponent {} + + const fontFixture = TestBed.createComponent(FontSizeTestComponent); + fontFixture.detectChanges(); + + const svg = fontFixture.nativeElement.querySelector('svg'); + expect(svg).toBeTruthy(); + }); + + it('should render with custom margin', () => { + @Component({ + standalone: true, + imports: [BarcodeDirective], + template: ` + + `, + }) + class MarginTestComponent {} + + const marginFixture = TestBed.createComponent(MarginTestComponent); + marginFixture.detectChanges(); + + const svg = marginFixture.nativeElement.querySelector('svg'); + expect(svg).toBeTruthy(); + }); + }); +}); diff --git a/libs/shared/barcode/src/lib/barcode.directive.ts b/libs/shared/barcode/src/lib/barcode.directive.ts new file mode 100644 index 000000000..e458ca40c --- /dev/null +++ b/libs/shared/barcode/src/lib/barcode.directive.ts @@ -0,0 +1,125 @@ +import { + Directive, + ElementRef, + effect, + inject, + input, + type OnDestroy, +} from '@angular/core'; +import JsBarcode from 'jsbarcode'; +import { logger } from '@isa/core/logging'; + +/** + * Directive for rendering Code 128 barcodes on SVG elements using JsBarcode. + * + * @example + * ```html + * + * ``` + */ +@Directive({ + selector: 'svg[sharedBarcode]', + standalone: true, +}) +export class BarcodeDirective implements OnDestroy { + readonly #elementRef = inject(ElementRef); + readonly #logger = logger({ directive: 'BarcodeDirective' }); + + /** + * The barcode value to encode (required) + */ + value = input.required(); + + /** + * Barcode format (default: CODE128) + */ + format = input('CODE128'); + + /** + * Width of a single bar in pixels (default: 2) + */ + width = input(2); + + /** + * Height of the barcode in pixels (default: 100) + */ + height = input(100); + + /** + * Whether to display the human-readable value below the barcode (default: true) + */ + displayValue = input(true); + + /** + * Color of the barcode bars and text (default: #000000) + */ + lineColor = input('#000000'); + + /** + * Background color (default: #ffffff) + */ + background = input('#ffffff'); + + /** + * Font size for the human-readable text (default: 20) + */ + fontSize = input(20); + + /** + * Margin around the barcode in pixels (default: 10) + */ + margin = input(10); + + constructor() { + // Re-render barcode whenever any input changes + effect(() => { + this.#renderBarcode(); + }); + } + + ngOnDestroy(): void { + this.#logger.debug('Directive destroyed', () => ({ + value: this.value(), + })); + } + + #renderBarcode(): void { + const element = this.#elementRef.nativeElement; + const value = this.value(); + + try { + JsBarcode(element, value, { + format: this.format(), + width: this.width(), + height: this.height(), + displayValue: this.displayValue(), + lineColor: this.lineColor(), + background: this.background(), + fontSize: this.fontSize(), + margin: this.margin(), + valid: (isValid: boolean) => { + if (!isValid) { + this.#logger.warn('Invalid barcode data', () => ({ + value, + format: this.format(), + })); + } + }, + }); + + this.#logger.debug('Barcode rendered', () => ({ + value, + format: this.format(), + })); + } catch (error) { + this.#logger.error( + 'Failed to render barcode', + error as Error, + () => ({ + value, + format: this.format(), + }) + ); + } + } +} diff --git a/libs/shared/barcode/src/test-setup.ts b/libs/shared/barcode/src/test-setup.ts new file mode 100644 index 000000000..cebf5ae72 --- /dev/null +++ b/libs/shared/barcode/src/test-setup.ts @@ -0,0 +1,13 @@ +import '@angular/compiler'; +import '@analogjs/vitest-angular/setup-zone'; + +import { + BrowserTestingModule, + platformBrowserTesting, +} from '@angular/platform-browser/testing'; +import { getTestBed } from '@angular/core/testing'; + +getTestBed().initTestEnvironment( + BrowserTestingModule, + platformBrowserTesting(), +); diff --git a/libs/shared/barcode/tsconfig.json b/libs/shared/barcode/tsconfig.json new file mode 100644 index 000000000..3268ed4dc --- /dev/null +++ b/libs/shared/barcode/tsconfig.json @@ -0,0 +1,30 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "importHelpers": true, + "moduleResolution": "bundler", + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "module": "preserve" + }, + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "typeCheckHostBindings": true, + "strictTemplates": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/libs/shared/barcode/tsconfig.lib.json b/libs/shared/barcode/tsconfig.lib.json new file mode 100644 index 000000000..312ee86bb --- /dev/null +++ b/libs/shared/barcode/tsconfig.lib.json @@ -0,0 +1,27 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "declaration": true, + "declarationMap": true, + "inlineSources": true, + "types": [] + }, + "exclude": [ + "src/**/*.spec.ts", + "src/test-setup.ts", + "jest.config.ts", + "src/**/*.test.ts", + "vite.config.ts", + "vite.config.mts", + "vitest.config.ts", + "vitest.config.mts", + "src/**/*.test.tsx", + "src/**/*.spec.tsx", + "src/**/*.test.js", + "src/**/*.spec.js", + "src/**/*.test.jsx", + "src/**/*.spec.jsx" + ], + "include": ["src/**/*.ts"] +} diff --git a/libs/shared/barcode/tsconfig.spec.json b/libs/shared/barcode/tsconfig.spec.json new file mode 100644 index 000000000..5785a8a5f --- /dev/null +++ b/libs/shared/barcode/tsconfig.spec.json @@ -0,0 +1,29 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "types": [ + "vitest/globals", + "vitest/importMeta", + "vite/client", + "node", + "vitest" + ] + }, + "include": [ + "vite.config.ts", + "vite.config.mts", + "vitest.config.ts", + "vitest.config.mts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.test.tsx", + "src/**/*.spec.tsx", + "src/**/*.test.js", + "src/**/*.spec.js", + "src/**/*.test.jsx", + "src/**/*.spec.jsx", + "src/**/*.d.ts" + ], + "files": ["src/test-setup.ts"] +} diff --git a/libs/shared/barcode/vite.config.mts b/libs/shared/barcode/vite.config.mts new file mode 100644 index 000000000..1be84334e --- /dev/null +++ b/libs/shared/barcode/vite.config.mts @@ -0,0 +1,29 @@ +/// +import { defineConfig } from 'vite'; +import angular from '@analogjs/vite-plugin-angular'; +import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin'; +import { nxCopyAssetsPlugin } from '@nx/vite/plugins/nx-copy-assets.plugin'; + +export default +// @ts-expect-error - Vitest reporter tuple types have complex inference issues +defineConfig(() => ({ + root: __dirname, + cacheDir: '../../../node_modules/.vite/libs/shared/barcode', + plugins: [angular(), nxViteTsPaths(), nxCopyAssetsPlugin(['*.md'])], + test: { + watch: false, + globals: true, + environment: 'jsdom', + include: ['{src,tests}/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], + setupFiles: ['src/test-setup.ts'], + reporters: [ + 'default', + ['junit', { outputFile: '../../../testresults/junit-shared-barcode.xml' }], + ], + coverage: { + reportsDirectory: '../../../coverage/libs/shared/barcode', + provider: 'v8' as const, + reporter: ['text', 'cobertura'], + }, + }, +})); diff --git a/package-lock.json b/package-lock.json index b63785714..d02cf3349 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,6 +33,7 @@ "angular-oauth2-oidc": "^20.0.2", "angular-oauth2-oidc-jwks": "^20.0.0", "date-fns": "^4.1.0", + "jsbarcode": "^3.12.1", "lodash": "^4.17.21", "moment": "^2.30.1", "ng2-pdf-viewer": "^10.4.0", @@ -75,6 +76,7 @@ "@swc/core": "1.12.1", "@swc/helpers": "0.5.17", "@types/jest": "30.0.0", + "@types/jsbarcode": "^3.11.4", "@types/lodash": "^4.17.16", "@types/node": "18.16.9", "@types/uuid": "^10.0.0", @@ -993,18 +995,6 @@ } } }, - "node_modules/@angular/build/node_modules/@types/node": { - "version": "24.10.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", - "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "undici-types": "~7.16.0" - } - }, "node_modules/@angular/build/node_modules/vite": { "version": "7.1.11", "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.11.tgz", @@ -7518,16 +7508,15 @@ } }, "node_modules/@module-federation/node": { - "version": "2.7.23", - "resolved": "https://registry.npmjs.org/@module-federation/node/-/node-2.7.23.tgz", - "integrity": "sha512-2v9ks2caLDJl1wAqVeFLaxKK7oBT6jM1nHzJXsFqgTC0xN2GyRBUz+SHmcXIRcMW5FZKIBBzv8OZrte2/z5PWQ==", + "version": "2.7.25", + "resolved": "https://registry.npmjs.org/@module-federation/node/-/node-2.7.25.tgz", + "integrity": "sha512-/u4f+GYRZfHpSvdt5n40lMCS9Cmve7N3JlDreaFXz8xrWDNOp2wvMgiVGpndo5J4iQdtLjpavWStahGQ05B2cQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@module-federation/enhanced": "0.21.4", - "@module-federation/runtime": "0.21.4", - "@module-federation/sdk": "0.21.4", + "@module-federation/enhanced": "0.21.6", + "@module-federation/runtime": "0.21.6", + "@module-federation/sdk": "0.21.6", "btoa": "1.2.1", "encoding": "^0.1.13", "node-fetch": "2.7.0" @@ -7550,29 +7539,29 @@ } }, "node_modules/@module-federation/node/node_modules/@module-federation/bridge-react-webpack-plugin": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/@module-federation/bridge-react-webpack-plugin/-/bridge-react-webpack-plugin-0.21.4.tgz", - "integrity": "sha512-aVxpy5dI5da2Qxw5YUDrXnzB68G3tUM3hogaImBjUvEsXFOxg7Pc5DBio2I/FJ45jXnoP3Gaswa0vLz6xWiyiA==", + "version": "0.21.6", + "resolved": "https://registry.npmjs.org/@module-federation/bridge-react-webpack-plugin/-/bridge-react-webpack-plugin-0.21.6.tgz", + "integrity": "sha512-lJMmdhD4VKVkeg8RHb+Jwe6Ou9zKVgjtb1inEURDG/sSS2ksdZA8pVKLYbRPRbdmjr193Y8gJfqFbI2dqoyc/g==", "dev": true, "license": "MIT", "dependencies": { - "@module-federation/sdk": "0.21.4", + "@module-federation/sdk": "0.21.6", "@types/semver": "7.5.8", "semver": "7.6.3" } }, "node_modules/@module-federation/node/node_modules/@module-federation/cli": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/@module-federation/cli/-/cli-0.21.4.tgz", - "integrity": "sha512-WmNVpq9h6xFe5+NviLL8/n174nhS5pOVHs7JAW7e/0qpQ5qXXn4ZN57ewUNfd6+RR6WYuoP1Q3ZWkeK+/dv9gQ==", + "version": "0.21.6", + "resolved": "https://registry.npmjs.org/@module-federation/cli/-/cli-0.21.6.tgz", + "integrity": "sha512-qNojnlc8pTyKtK7ww3i/ujLrgWwgXqnD5DcDPsjADVIpu7STaoaVQ0G5GJ7WWS/ajXw6EyIAAGW/AMFh4XUxsQ==", "dev": true, "license": "MIT", "dependencies": { - "@modern-js/node-bundle-require": "2.68.2", - "@module-federation/dts-plugin": "0.21.4", - "@module-federation/sdk": "0.21.4", + "@module-federation/dts-plugin": "0.21.6", + "@module-federation/sdk": "0.21.6", "chalk": "3.0.0", - "commander": "11.1.0" + "commander": "11.1.0", + "jiti": "2.4.2" }, "bin": { "mf": "bin/mf.js" @@ -7582,14 +7571,14 @@ } }, "node_modules/@module-federation/node/node_modules/@module-federation/data-prefetch": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/@module-federation/data-prefetch/-/data-prefetch-0.21.4.tgz", - "integrity": "sha512-RKCacRLuh2kd9gtTkEqYlT0l2w9B0NDDthOVHCi+WlHWc5vXDxupZaEFFOAUimzARN8dPXok2iwlLLD2gs6AQw==", + "version": "0.21.6", + "resolved": "https://registry.npmjs.org/@module-federation/data-prefetch/-/data-prefetch-0.21.6.tgz", + "integrity": "sha512-8HD7ZhtWZ9vl6i3wA7M8cEeCRdtvxt09SbMTfqIPm+5eb/V4ijb8zGTYSRhNDb5RCB+BAixaPiZOWKXJ63/rVw==", "dev": true, "license": "MIT", "dependencies": { - "@module-federation/runtime": "0.21.4", - "@module-federation/sdk": "0.21.4", + "@module-federation/runtime": "0.21.6", + "@module-federation/sdk": "0.21.6", "fs-extra": "9.1.0" }, "peerDependencies": { @@ -7598,16 +7587,16 @@ } }, "node_modules/@module-federation/node/node_modules/@module-federation/dts-plugin": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/@module-federation/dts-plugin/-/dts-plugin-0.21.4.tgz", - "integrity": "sha512-dStZ+J90JByoa++p3TuB4xx3b+25tHb9EAsvBkv86ptXEM1QFYRljV/7fvrQrvvqgD6Jpmq1LESi5NvI5J9P+w==", + "version": "0.21.6", + "resolved": "https://registry.npmjs.org/@module-federation/dts-plugin/-/dts-plugin-0.21.6.tgz", + "integrity": "sha512-YIsDk8/7QZIWn0I1TAYULniMsbyi2LgKTi9OInzVmZkwMC6644x/ratTWBOUDbdY1Co+feNkoYeot1qIWv2L7w==", "dev": true, "license": "MIT", "dependencies": { - "@module-federation/error-codes": "0.21.4", - "@module-federation/managers": "0.21.4", - "@module-federation/sdk": "0.21.4", - "@module-federation/third-party-dts-extractor": "0.21.4", + "@module-federation/error-codes": "0.21.6", + "@module-federation/managers": "0.21.6", + "@module-federation/sdk": "0.21.6", + "@module-federation/third-party-dts-extractor": "0.21.6", "adm-zip": "^0.5.10", "ansi-colors": "^4.1.3", "axios": "^1.12.0", @@ -7632,23 +7621,23 @@ } }, "node_modules/@module-federation/node/node_modules/@module-federation/enhanced": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/@module-federation/enhanced/-/enhanced-0.21.4.tgz", - "integrity": "sha512-QX4nfL1E2dboPBCLIU/x1P87wa/fwj+AOLP7TPJ6CHwEahXoXbrnrux6Hjcf/6SfrN9RGZkEauYy2W/VvigJlw==", + "version": "0.21.6", + "resolved": "https://registry.npmjs.org/@module-federation/enhanced/-/enhanced-0.21.6.tgz", + "integrity": "sha512-8PFQxtmXc6ukBC4CqGIoc96M2Ly9WVwCPu4Ffvt+K/SB6rGbeFeZoYAwREV1zGNMJ5v5ly6+AHIEOBxNuSnzSg==", "dev": true, "license": "MIT", "dependencies": { - "@module-federation/bridge-react-webpack-plugin": "0.21.4", - "@module-federation/cli": "0.21.4", - "@module-federation/data-prefetch": "0.21.4", - "@module-federation/dts-plugin": "0.21.4", - "@module-federation/error-codes": "0.21.4", - "@module-federation/inject-external-runtime-core-plugin": "0.21.4", - "@module-federation/managers": "0.21.4", - "@module-federation/manifest": "0.21.4", - "@module-federation/rspack": "0.21.4", - "@module-federation/runtime-tools": "0.21.4", - "@module-federation/sdk": "0.21.4", + "@module-federation/bridge-react-webpack-plugin": "0.21.6", + "@module-federation/cli": "0.21.6", + "@module-federation/data-prefetch": "0.21.6", + "@module-federation/dts-plugin": "0.21.6", + "@module-federation/error-codes": "0.21.6", + "@module-federation/inject-external-runtime-core-plugin": "0.21.6", + "@module-federation/managers": "0.21.6", + "@module-federation/manifest": "0.21.6", + "@module-federation/rspack": "0.21.6", + "@module-federation/runtime-tools": "0.21.6", + "@module-federation/sdk": "0.21.6", "btoa": "^1.2.1", "schema-utils": "^4.3.0", "upath": "2.0.1" @@ -7674,62 +7663,62 @@ } }, "node_modules/@module-federation/node/node_modules/@module-federation/error-codes": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/@module-federation/error-codes/-/error-codes-0.21.4.tgz", - "integrity": "sha512-ClpL5MereWNXh+EgDjz7w4RrC1JlisQTvXDa1gLxpviHafzNDfdViVmuhi9xXVuj+EYo8KU70Y999KHhk9424Q==", + "version": "0.21.6", + "resolved": "https://registry.npmjs.org/@module-federation/error-codes/-/error-codes-0.21.6.tgz", + "integrity": "sha512-MLJUCQ05KnoVl8xd6xs9a5g2/8U+eWmVxg7xiBMeR0+7OjdWUbHwcwgVFatRIwSZvFgKHfWEiI7wsU1q1XbTRQ==", "dev": true, "license": "MIT" }, "node_modules/@module-federation/node/node_modules/@module-federation/inject-external-runtime-core-plugin": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/@module-federation/inject-external-runtime-core-plugin/-/inject-external-runtime-core-plugin-0.21.4.tgz", - "integrity": "sha512-lOy+qPEA56AdkSIN2hO5zsKvnbplCJHUR5B6BKjo5+q752BrE3C1O0vAXYBRgmdQIBn+JAssdkbJKtfwl8oReQ==", + "version": "0.21.6", + "resolved": "https://registry.npmjs.org/@module-federation/inject-external-runtime-core-plugin/-/inject-external-runtime-core-plugin-0.21.6.tgz", + "integrity": "sha512-DJQne7NQ988AVi3QB8byn12FkNb+C2lBeU1NRf8/WbL0gmHsr6kW8hiEJCm8LYaURwtsQqtsEV7i+8+51qjSmQ==", "dev": true, "license": "MIT", "peerDependencies": { - "@module-federation/runtime-tools": "0.21.4" + "@module-federation/runtime-tools": "0.21.6" } }, "node_modules/@module-federation/node/node_modules/@module-federation/managers": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/@module-federation/managers/-/managers-0.21.4.tgz", - "integrity": "sha512-z8KZJdT56lv73GKh0g7IO4CLxCtgV44qnTCn7GZ/R1cdR0JhdDvrqlYL8rrVGPw1y2BqudO0OxlRw0LjAGGj7g==", + "version": "0.21.6", + "resolved": "https://registry.npmjs.org/@module-federation/managers/-/managers-0.21.6.tgz", + "integrity": "sha512-BeV6m2/7kF5MDVz9JJI5T8h8lMosnXkH2bOxxFewcra7ZjvDOgQu7WIio0mgk5l1zjNPvnEVKhnhrenEdcCiWg==", "dev": true, "license": "MIT", "dependencies": { - "@module-federation/sdk": "0.21.4", + "@module-federation/sdk": "0.21.6", "find-pkg": "2.0.0", "fs-extra": "9.1.0" } }, "node_modules/@module-federation/node/node_modules/@module-federation/manifest": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/@module-federation/manifest/-/manifest-0.21.4.tgz", - "integrity": "sha512-sW6eYTpqeNjPszC2FMUyT21IaUkqueDPlmPffyV9XVUSjOZgNa5VbDiD3qyW86v/bHC0nhrQ0/TWKn8EPOszLQ==", + "version": "0.21.6", + "resolved": "https://registry.npmjs.org/@module-federation/manifest/-/manifest-0.21.6.tgz", + "integrity": "sha512-yg93+I1qjRs5B5hOSvjbjmIoI2z3th8/yst9sfwvx4UDOG1acsE3HHMyPN0GdoIGwplC/KAnU5NmUz4tREUTGQ==", "dev": true, "license": "MIT", "dependencies": { - "@module-federation/dts-plugin": "0.21.4", - "@module-federation/managers": "0.21.4", - "@module-federation/sdk": "0.21.4", + "@module-federation/dts-plugin": "0.21.6", + "@module-federation/managers": "0.21.6", + "@module-federation/sdk": "0.21.6", "chalk": "3.0.0", "find-pkg": "2.0.0" } }, "node_modules/@module-federation/node/node_modules/@module-federation/rspack": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/@module-federation/rspack/-/rspack-0.21.4.tgz", - "integrity": "sha512-/FG6CjAg8M5EUwTMOMxceC+oLggHwYMGaTp7jCXhCXQJpnJ0C/OvcgKp0lif+ELIRxerAAldrFO06/4n9gLEIg==", + "version": "0.21.6", + "resolved": "https://registry.npmjs.org/@module-federation/rspack/-/rspack-0.21.6.tgz", + "integrity": "sha512-SB+z1P+Bqe3R6geZje9dp0xpspX6uash+zO77nodmUy8PTTBlkL7800Cq2FMLKUdoTZHJTBVXf0K6CqQWSlItg==", "dev": true, "license": "MIT", "dependencies": { - "@module-federation/bridge-react-webpack-plugin": "0.21.4", - "@module-federation/dts-plugin": "0.21.4", - "@module-federation/inject-external-runtime-core-plugin": "0.21.4", - "@module-federation/managers": "0.21.4", - "@module-federation/manifest": "0.21.4", - "@module-federation/runtime-tools": "0.21.4", - "@module-federation/sdk": "0.21.4", + "@module-federation/bridge-react-webpack-plugin": "0.21.6", + "@module-federation/dts-plugin": "0.21.6", + "@module-federation/inject-external-runtime-core-plugin": "0.21.6", + "@module-federation/managers": "0.21.6", + "@module-federation/manifest": "0.21.6", + "@module-federation/runtime-tools": "0.21.6", + "@module-federation/sdk": "0.21.6", "btoa": "1.2.1" }, "peerDependencies": { @@ -7747,51 +7736,51 @@ } }, "node_modules/@module-federation/node/node_modules/@module-federation/runtime": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/@module-federation/runtime/-/runtime-0.21.4.tgz", - "integrity": "sha512-wgvGqryurVEvkicufJmTG0ZehynCeNLklv8kIk5BLIsWYSddZAE+xe4xov1kgH5fIJQAoQNkRauFFjVNlHoAkA==", + "version": "0.21.6", + "resolved": "https://registry.npmjs.org/@module-federation/runtime/-/runtime-0.21.6.tgz", + "integrity": "sha512-+caXwaQqwTNh+CQqyb4mZmXq7iEemRDrTZQGD+zyeH454JAYnJ3s/3oDFizdH6245pk+NiqDyOOkHzzFQorKhQ==", "dev": true, "license": "MIT", "dependencies": { - "@module-federation/error-codes": "0.21.4", - "@module-federation/runtime-core": "0.21.4", - "@module-federation/sdk": "0.21.4" + "@module-federation/error-codes": "0.21.6", + "@module-federation/runtime-core": "0.21.6", + "@module-federation/sdk": "0.21.6" } }, "node_modules/@module-federation/node/node_modules/@module-federation/runtime-core": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/@module-federation/runtime-core/-/runtime-core-0.21.4.tgz", - "integrity": "sha512-SGpmoOLGNxZofpTOk6Lxb2ewaoz5wMi93AFYuuJB04HTVcngEK+baNeUZ2D/xewrqNIJoMY6f5maUjVfIIBPUA==", + "version": "0.21.6", + "resolved": "https://registry.npmjs.org/@module-federation/runtime-core/-/runtime-core-0.21.6.tgz", + "integrity": "sha512-5Hd1Y5qp5lU/aTiK66lidMlM/4ji2gr3EXAtJdreJzkY+bKcI5+21GRcliZ4RAkICmvdxQU5PHPL71XmNc7Lsw==", "dev": true, "license": "MIT", "dependencies": { - "@module-federation/error-codes": "0.21.4", - "@module-federation/sdk": "0.21.4" + "@module-federation/error-codes": "0.21.6", + "@module-federation/sdk": "0.21.6" } }, "node_modules/@module-federation/node/node_modules/@module-federation/runtime-tools": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/@module-federation/runtime-tools/-/runtime-tools-0.21.4.tgz", - "integrity": "sha512-RzFKaL0DIjSmkn76KZRfzfB6dD07cvID84950jlNQgdyoQFUGkqD80L6rIpVCJTY/R7LzR3aQjHnoqmq4JPo3w==", + "version": "0.21.6", + "resolved": "https://registry.npmjs.org/@module-federation/runtime-tools/-/runtime-tools-0.21.6.tgz", + "integrity": "sha512-fnP+ZOZTFeBGiTAnxve+axGmiYn2D60h86nUISXjXClK3LUY1krUfPgf6MaD4YDJ4i51OGXZWPekeMe16pkd8Q==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@module-federation/runtime": "0.21.4", - "@module-federation/webpack-bundler-runtime": "0.21.4" + "@module-federation/runtime": "0.21.6", + "@module-federation/webpack-bundler-runtime": "0.21.6" } }, "node_modules/@module-federation/node/node_modules/@module-federation/sdk": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-0.21.4.tgz", - "integrity": "sha512-tzvhOh/oAfX++6zCDDxuvioHY4Jurf8vcfoCbKFxusjmyKr32GPbwFDazUP+OPhYCc3dvaa9oWU6X/qpUBLfJw==", + "version": "0.21.6", + "resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-0.21.6.tgz", + "integrity": "sha512-x6hARETb8iqHVhEsQBysuWpznNZViUh84qV2yE7AD+g7uIzHKiYdoWqj10posbo5XKf/147qgWDzKZoKoEP2dw==", "dev": true, "license": "MIT" }, "node_modules/@module-federation/node/node_modules/@module-federation/third-party-dts-extractor": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/@module-federation/third-party-dts-extractor/-/third-party-dts-extractor-0.21.4.tgz", - "integrity": "sha512-zKaKpABSbpZhKbTUGkN6VKqApa+PcawwXAv+L8co3vhErRna82svSIicgLy27n4QzAFJ09coB4WgnPQLjXdU+A==", + "version": "0.21.6", + "resolved": "https://registry.npmjs.org/@module-federation/third-party-dts-extractor/-/third-party-dts-extractor-0.21.6.tgz", + "integrity": "sha512-Il6x4hLsvCgZNk1DFwuMBNeoxD1BsZ5AW2BI/nUgu0k5FiAvfcz1OFawRFEHtaM/kVrCsymMOW7pCao90DaX3A==", "dev": true, "license": "MIT", "dependencies": { @@ -7801,14 +7790,14 @@ } }, "node_modules/@module-federation/node/node_modules/@module-federation/webpack-bundler-runtime": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/@module-federation/webpack-bundler-runtime/-/webpack-bundler-runtime-0.21.4.tgz", - "integrity": "sha512-dusmR3uPnQh9u9ChQo3M+GLOuGFthfvnh7WitF/a1eoeTfRmXqnMFsXtZCUK+f/uXf+64874Zj/bhAgbBcVHZA==", + "version": "0.21.6", + "resolved": "https://registry.npmjs.org/@module-federation/webpack-bundler-runtime/-/webpack-bundler-runtime-0.21.6.tgz", + "integrity": "sha512-7zIp3LrcWbhGuFDTUMLJ2FJvcwjlddqhWGxi/MW3ur1a+HaO8v5tF2nl+vElKmbG1DFLU/52l3PElVcWf/YcsQ==", "dev": true, "license": "MIT", "dependencies": { - "@module-federation/runtime": "0.21.4", - "@module-federation/sdk": "0.21.4" + "@module-federation/runtime": "0.21.6", + "@module-federation/sdk": "0.21.6" } }, "node_modules/@module-federation/node/node_modules/accepts": { @@ -9628,6 +9617,23 @@ "node": ">= 0.6" } }, + "node_modules/@nx/module-federation/node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/@nx/module-federation/node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -10211,6 +10217,23 @@ "node": ">= 0.6" } }, + "node_modules/@nx/rspack/node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/@nx/rspack/node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -13633,6 +13656,16 @@ "integrity": "sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA==", "license": "MIT" }, + "node_modules/@types/jsbarcode": { + "version": "3.11.4", + "resolved": "https://registry.npmjs.org/@types/jsbarcode/-/jsbarcode-3.11.4.tgz", + "integrity": "sha512-VBcpTAnEMH0Gbh8JpV14CgOtJjCYjsvR2FoDRyoYPE0gUxtApf8N4c+HKEOyz/iiIZkMzqrzBA3XX7+KgKxxsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/jsdom": { "version": "20.0.1", "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz", @@ -16333,24 +16366,6 @@ ], "license": "CC-BY-4.0" }, - "node_modules/canvas": { - "version": "2.11.2", - "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.11.2.tgz", - "integrity": "sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@mapbox/node-pre-gyp": "^1.0.0", - "nan": "^2.17.0", - "simple-get": "^3.0.3" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/case-sensitive-paths-webpack-plugin": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz", @@ -18950,9 +18965,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.258", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.258.tgz", - "integrity": "sha512-rHUggNV5jKQ0sSdWwlaRDkFc3/rRJIVnOSe9yR4zrR07m3ZxhP4N27Hlg8VeJGGYgFTxK5NqDmWI4DSH72vIJg==", + "version": "1.5.259", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.259.tgz", + "integrity": "sha512-I+oLXgpEJzD6Cwuwt1gYjxsDmu/S/Kd41mmLA3O+/uH2pFRO/DvOjUyGozL8j3KeLV6WyZ7ssPwELMsXCcsJAQ==", "license": "ISC" }, "node_modules/emittery": { @@ -21568,30 +21583,24 @@ "license": "MIT" }, "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "dev": true, "license": "MIT", "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" }, "engines": { "node": ">= 0.8" - } - }, - "node_modules/http-errors/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/http-parser-js": { @@ -27307,7 +27316,6 @@ "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", "dev": true, "license": "MIT", - "peer": true, "bin": { "jiti": "lib/jiti-cli.mjs" } @@ -27362,6 +27370,12 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/jsbarcode": { + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/jsbarcode/-/jsbarcode-3.12.1.tgz", + "integrity": "sha512-QZQSqIknC2Rr/YOUyOkCBqsoiBAOTYK+7yNN3JsqfoUtJtkazxNw1dmPpxuv7VVvqW13kA3/mKiLq+s/e3o9hQ==", + "license": "MIT" + }, "node_modules/jsdom": { "version": "22.1.0", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-22.1.0.tgz", @@ -28110,6 +28124,7 @@ "integrity": "sha512-SL0JY3DaxylDuo/MecFeiC+7pedM0zia33zl0vcjgwcq1q1FWWF1To9EIauPbl8GbMCU0R2e0uJ8bZunhYKD2g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "cli-truncate": "^4.0.0", "colorette": "^2.0.20", @@ -32442,6 +32457,33 @@ "node": ">= 0.10" } }, + "node_modules/raw-body/node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -37071,6 +37113,98 @@ } } }, + "node_modules/vitest/node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/vitest/node_modules/vite": { + "version": "7.2.4", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.4.tgz", + "integrity": "sha512-NL8jTlbo0Tn4dUEXEsUg8KeyG/Lkmc4Fnzb8JXN/Ykm9G4HNImjtABMJgkQoVjOBN/j2WAwDTRytdqJbZsah7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, "node_modules/w3c-xmlserializer": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", @@ -37280,6 +37414,7 @@ "integrity": "sha512-QcQ72gh8a+7JO63TAx/6XZf/CWhgMzu5m0QirvPfGvptOusAxG12w2+aua1Jkjr7hzaWDnJ2n6JFeexMHI+Zjg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/bonjour": "^3.5.13", "@types/connect-history-api-fallback": "^1.5.4", @@ -37532,6 +37667,23 @@ "node": ">= 6" } }, + "node_modules/webpack-dev-server/node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/webpack-dev-server/node_modules/http-proxy-middleware": { "version": "2.0.9", "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz", diff --git a/package.json b/package.json index 6cb118076..c59f5a2ba 100644 --- a/package.json +++ b/package.json @@ -1,131 +1,133 @@ -{ - "name": "hima", - "version": "0.0.0", - "scripts": { - "ng": "ng", - "start": "nx serve isa-app --ssl", - "pretest": "npx trash-cli testresults", - "test": "npx nx run-many --tuiAutoExit true -t test --exclude isa-app", - "ci": "npx nx run-many -t test --exclude tag:skip:ci -c ci --tuiAutoExit true", - "build": "nx build isa-app --configuration=development", - "build-prod": "nx build isa-app --configuration=production", - "lint": "nx lint", - "e2e": "nx e2e", - "generate:swagger": "nx run-many -t generate -p tag:generated,swagger", - "fix:files:swagger": "node ./tools/fix-files.js generated/swagger", - "prettier": "prettier --write .", - "pretty-quick": "pretty-quick --staged", - "prepare": "husky", - "storybook": "npx nx run isa-app:storybook", - "docs:generate": "node tools/generate-library-reference.js" - }, - "private": true, - "dependencies": { - "@angular-architects/ngrx-toolkit": "^20.4.0", - "@angular/animations": "20.3.6", - "@angular/cdk": "20.2.9", - "@angular/common": "20.3.6", - "@angular/compiler": "20.3.6", - "@angular/core": "20.3.6", - "@angular/forms": "20.3.6", - "@angular/localize": "20.3.6", - "@angular/platform-browser": "20.3.6", - "@angular/platform-browser-dynamic": "20.3.6", - "@angular/router": "20.3.6", - "@angular/service-worker": "20.3.6", - "@microsoft/signalr": "^8.0.7", - "@ng-icons/core": "32.2.0", - "@ng-icons/material-icons": "32.2.0", - "@ngrx/component-store": "^20.0.0", - "@ngrx/effects": "^20.0.0", - "@ngrx/entity": "^20.0.0", - "@ngrx/operators": "^20.0.0", - "@ngrx/signals": "^20.0.0", - "@ngrx/store": "^20.0.0", - "@ngrx/store-devtools": "^20.0.0", - "angular-oauth2-oidc": "^20.0.2", - "angular-oauth2-oidc-jwks": "^20.0.0", - "date-fns": "^4.1.0", - "lodash": "^4.17.21", - "moment": "^2.30.1", - "ng2-pdf-viewer": "^10.4.0", - "ngx-matomo-client": "^8.0.0", - "parse-duration": "^2.1.3", - "rxjs": "~7.8.2", - "scandit-web-datacapture-barcode": "^6.28.1", - "scandit-web-datacapture-core": "^6.28.1", - "tslib": "^2.3.0", - "uuid": "^8.3.2", - "zod": "^3.24.2", - "zone.js": "~0.15.0" - }, - "devDependencies": { - "@analogjs/vite-plugin-angular": "1.21.3", - "@analogjs/vitest-angular": "1.21.3", - "@angular-devkit/build-angular": "^20.3.6", - "@angular-devkit/core": "20.3.6", - "@angular-devkit/schematics": "20.3.6", - "@angular/build": "^20.3.6", - "@angular/cli": "^20.3.6", - "@angular/compiler-cli": "20.3.6", - "@angular/language-service": "20.3.6", - "@angular/pwa": "20.3.6", - "@eslint/js": "^9.8.0", - "@ngneat/spectator": "22.0.0", - "@nx/angular": "21.3.2", - "@nx/eslint": "21.3.2", - "@nx/eslint-plugin": "21.3.2", - "@nx/jest": "21.3.2", - "@nx/js": "21.3.2", - "@nx/storybook": "21.3.2", - "@nx/vite": "21.3.2", - "@nx/web": "21.3.2", - "@nx/workspace": "21.3.2", - "@schematics/angular": "20.3.6", - "@storybook/addon-docs": "^9.0.11", - "@storybook/angular": "^9.0.5", - "@swc-node/register": "1.10.10", - "@swc/core": "1.12.1", - "@swc/helpers": "0.5.17", - "@types/jest": "30.0.0", - "@types/lodash": "^4.17.16", - "@types/node": "18.16.9", - "@types/uuid": "^10.0.0", - "@typescript-eslint/utils": "^8.33.1", - "@vitest/coverage-v8": "^3.1.1", - "@vitest/ui": "^3.1.1", - "angular-eslint": "20.4.0", - "autoprefixer": "^10.4.20", - "eslint": "^9.28.0", - "eslint-config-prettier": "^10.1.5", - "husky": "^9.1.7", - "jest": "^29.7.0", - "jest-environment-jsdom": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-junit": "^16.0.0", - "jest-preset-angular": "14.6.0", - "jiti": "2.4.2", - "jsdom": "~22.1.0", - "jsonc-eslint-parser": "^2.1.0", - "ng-mocks": "14.14.0", - "ng-packagr": "20.3.0", - "ng-swagger-gen": "^2.3.1", - "nx": "21.3.2", - "postcss": "^8.5.3", - "postcss-url": "~10.1.3", - "prettier": "^3.5.2", - "pretty-quick": "~4.0.0", - "storybook": "^9.0.5", - "tailwindcss": "^3.4.14", - "ts-jest": "^29.1.0", - "ts-node": "10.9.1", - "typescript": "5.8.3", - "typescript-eslint": "^8.33.1", - "vite": "6.3.5", - "vitest": "^3.1.1" - }, - "engines": { - "node": ">=22.12.0 <23.0.0", - "npm": ">=11.6.0 <11.7.0" - } -} +{ + "name": "hima", + "version": "0.0.0", + "scripts": { + "ng": "ng", + "start": "nx serve isa-app --ssl", + "pretest": "npx trash-cli testresults", + "test": "npx nx run-many --tuiAutoExit true -t test --exclude isa-app", + "ci": "npx nx run-many -t test --exclude tag:skip:ci -c ci --tuiAutoExit true", + "build": "nx build isa-app --configuration=development", + "build-prod": "nx build isa-app --configuration=production", + "lint": "nx lint", + "e2e": "nx e2e", + "generate:swagger": "nx run-many -t generate -p tag:generated,swagger", + "fix:files:swagger": "node ./tools/fix-files.js generated/swagger", + "prettier": "prettier --write .", + "pretty-quick": "pretty-quick --staged", + "prepare": "husky", + "storybook": "npx nx run isa-app:storybook", + "docs:generate": "node tools/generate-library-reference.js" + }, + "private": true, + "dependencies": { + "@angular-architects/ngrx-toolkit": "^20.4.0", + "@angular/animations": "20.3.6", + "@angular/cdk": "20.2.9", + "@angular/common": "20.3.6", + "@angular/compiler": "20.3.6", + "@angular/core": "20.3.6", + "@angular/forms": "20.3.6", + "@angular/localize": "20.3.6", + "@angular/platform-browser": "20.3.6", + "@angular/platform-browser-dynamic": "20.3.6", + "@angular/router": "20.3.6", + "@angular/service-worker": "20.3.6", + "@microsoft/signalr": "^8.0.7", + "@ng-icons/core": "32.2.0", + "@ng-icons/material-icons": "32.2.0", + "@ngrx/component-store": "^20.0.0", + "@ngrx/effects": "^20.0.0", + "@ngrx/entity": "^20.0.0", + "@ngrx/operators": "^20.0.0", + "@ngrx/signals": "^20.0.0", + "@ngrx/store": "^20.0.0", + "@ngrx/store-devtools": "^20.0.0", + "angular-oauth2-oidc": "^20.0.2", + "angular-oauth2-oidc-jwks": "^20.0.0", + "date-fns": "^4.1.0", + "jsbarcode": "^3.12.1", + "lodash": "^4.17.21", + "moment": "^2.30.1", + "ng2-pdf-viewer": "^10.4.0", + "ngx-matomo-client": "^8.0.0", + "parse-duration": "^2.1.3", + "rxjs": "~7.8.2", + "scandit-web-datacapture-barcode": "^6.28.1", + "scandit-web-datacapture-core": "^6.28.1", + "tslib": "^2.3.0", + "uuid": "^8.3.2", + "zod": "^3.24.2", + "zone.js": "~0.15.0" + }, + "devDependencies": { + "@analogjs/vite-plugin-angular": "1.21.3", + "@analogjs/vitest-angular": "1.21.3", + "@angular-devkit/build-angular": "^20.3.6", + "@angular-devkit/core": "20.3.6", + "@angular-devkit/schematics": "20.3.6", + "@angular/build": "^20.3.6", + "@angular/cli": "^20.3.6", + "@angular/compiler-cli": "20.3.6", + "@angular/language-service": "20.3.6", + "@angular/pwa": "20.3.6", + "@eslint/js": "^9.8.0", + "@ngneat/spectator": "22.0.0", + "@nx/angular": "21.3.2", + "@nx/eslint": "21.3.2", + "@nx/eslint-plugin": "21.3.2", + "@nx/jest": "21.3.2", + "@nx/js": "21.3.2", + "@nx/storybook": "21.3.2", + "@nx/vite": "21.3.2", + "@nx/web": "21.3.2", + "@nx/workspace": "21.3.2", + "@schematics/angular": "20.3.6", + "@storybook/addon-docs": "^9.0.11", + "@storybook/angular": "^9.0.5", + "@swc-node/register": "1.10.10", + "@swc/core": "1.12.1", + "@swc/helpers": "0.5.17", + "@types/jest": "30.0.0", + "@types/jsbarcode": "^3.11.4", + "@types/lodash": "^4.17.16", + "@types/node": "18.16.9", + "@types/uuid": "^10.0.0", + "@typescript-eslint/utils": "^8.33.1", + "@vitest/coverage-v8": "^3.1.1", + "@vitest/ui": "^3.1.1", + "angular-eslint": "20.4.0", + "autoprefixer": "^10.4.20", + "eslint": "^9.28.0", + "eslint-config-prettier": "^10.1.5", + "husky": "^9.1.7", + "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-junit": "^16.0.0", + "jest-preset-angular": "14.6.0", + "jiti": "2.4.2", + "jsdom": "~22.1.0", + "jsonc-eslint-parser": "^2.1.0", + "ng-mocks": "14.14.0", + "ng-packagr": "20.3.0", + "ng-swagger-gen": "^2.3.1", + "nx": "21.3.2", + "postcss": "^8.5.3", + "postcss-url": "~10.1.3", + "prettier": "^3.5.2", + "pretty-quick": "~4.0.0", + "storybook": "^9.0.5", + "tailwindcss": "^3.4.14", + "ts-jest": "^29.1.0", + "ts-node": "10.9.1", + "typescript": "5.8.3", + "typescript-eslint": "^8.33.1", + "vite": "6.3.5", + "vitest": "^3.1.1" + }, + "engines": { + "node": ">=22.12.0 <23.0.0", + "npm": ">=11.6.0 <11.7.0" + } +} diff --git a/tsconfig.base.json b/tsconfig.base.json index 9020fab4f..3c4cc8d89 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -126,6 +126,7 @@ "libs/remission/shared/search-item-to-remit-dialog/src/index.ts" ], "@isa/shared/address": ["libs/shared/address/src/index.ts"], + "@isa/shared/barcode": ["libs/shared/barcode/src/index.ts"], "@isa/shared/delivery": ["libs/shared/delivery/src/index.ts"], "@isa/shared/filter": ["libs/shared/filter/src/index.ts"], "@isa/shared/product-format": ["libs/shared/product-format/src/index.ts"],