Merged PR 1954: feat: Enhance product info components and add redemption points feature...

Related work items: #5346
This commit is contained in:
Lorenz Hilpert
2025-09-18 13:10:07 +00:00
committed by Nino Righi
parent 6c86dfbbad
commit 6ab839a529
26 changed files with 33351 additions and 31889 deletions

456
CLAUDE.md
View File

@@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## Project Overview
This is an Angular monorepo managed by Nx. The main application is `isa-app`, which appears to be an inventory and returns management system for retail/e-commerce.
This is a sophisticated Angular 20.1.2 monorepo managed by Nx 21.3.2. The main application is `isa-app`, a comprehensive inventory and returns management system for retail/e-commerce operations. The system handles complex workflows including order management (OMS), returns processing (remission), customer relationship management (CRM), product cataloging, and checkout/reward systems.
## Architecture
@@ -23,34 +23,43 @@ This is an Angular monorepo managed by Nx. The main application is `isa-app`, wh
- **generated/swagger/**: Auto-generated API client code from OpenAPI specs
### Key Architectural Patterns
- **Standalone Components**: Project uses Angular standalone components
- **Feature Libraries**: Domain features organized as separate libraries (e.g., `oms-feature-return-search`)
- **Data Access Layer**: Separate data-access libraries for each domain (e.g., `oms-data-access`, `remission-data-access`)
- **Shared UI Components**: Reusable UI components in `libs/ui/`
- **Generated API Clients**: Swagger/OpenAPI clients auto-generated in `generated/swagger/`
- **Domain-Driven Design**: Clear domain boundaries with dedicated modules (OMS, remission, CRM, catalogue, checkout)
- **Layered Architecture**: Strict dependency hierarchy (Feature → Shared/UI → Data Access → Infrastructure)
- **Standalone Components**: All new components use Angular standalone architecture with explicit imports
- **Feature Libraries**: Domain features organized as separate libraries (e.g., `oms-feature-return-search`, `remission-feature-remission-list`)
- **Data Access Layer**: Separate data-access libraries for each domain with NgRx Signals stores
- **Shared UI Components**: 15 dedicated UI component libraries with design system integration
- **Generated API Clients**: 10 auto-generated Swagger/OpenAPI clients with post-processing pipeline
- **Path Aliases**: Comprehensive TypeScript path mapping (`@isa/domain/layer/feature`)
- **Component Prefixes**: Domain-specific prefixes (OMS: `oms-feature-*`, Remission: `remi-*`, UI: `ui-*`)
- **Modern State Management**: NgRx Signals with entities, session persistence, and reactive patterns
## Common Development Commands
### Build Commands
```bash
# Build the main application (development)
npx nx build isa-app --configuration=development
# Build the main application (development) - default configuration
npm run build
# Or: npx nx build isa-app --configuration=development
# Build for production
npx nx build isa-app --configuration=production
npm run build-prod
# Or: npx nx build isa-app --configuration=production
# Serve the application with SSL
npx nx serve isa-app --ssl
# Serve the application with SSL (development server)
npm start
# Or: npx nx serve isa-app --ssl
```
### Testing Commands
```bash
# Run tests for a specific library (always use --skip-cache)
npx nx run <project-name>:test --skip-cache
# Example: npx nx run remission-data-access:test --skip-cache
# Run tests for all libraries except the main app (default command)
npm test
# Or: npx nx run-many -t test --exclude isa-app --skip-cache
# Run tests for all libraries except the main app
npx nx run-many -t test --exclude isa-app --skip-cache
# Run tests for a specific library (always use --skip-cache for fresh results)
npx nx run <project-name>:test --skip-cache
# Example: npx nx run oms-data-access:test --skip-cache
# Run a single test file
npx nx run <project-name>:test --testFile=<path-to-test-file> --skip-cache
@@ -60,6 +69,10 @@ npx nx run <project-name>:test --code-coverage --skip-cache
# Run tests in watch mode
npx nx run <project-name>:test --watch
# Run CI tests with coverage (for CI/CD)
npm run ci
# Or: npx nx run-many -t test --exclude isa-app -c ci
```
### Linting Commands
@@ -74,19 +87,24 @@ npx nx run-many -t lint
### Other Useful Commands
```bash
# Generate Swagger API clients
# Generate Swagger API clients (regenerates all API clients)
npm run generate:swagger
# Or: npx nx run-many -t generate -p tag:generated,swagger
# Start Storybook
npx nx run isa-app:storybook
# Start Storybook for UI component development
npm run storybook
# Or: npx nx run isa-app:storybook
# Format code with Prettier
npm run prettier
# Format staged files only (used in pre-commit hooks)
npm run pretty-quick
# List all projects in the workspace
npx nx list
# Show project dependencies graph
# Show project dependencies graph (visual)
npx nx graph
# Run affected tests (based on git changes)
@@ -95,54 +113,380 @@ npx nx affected:test
## Testing Framework
### Current Setup
- **Jest**: Primary test runner for existing libraries
- **Vitest**: Being adopted for new libraries (migration in progress)
- **Testing Utilities**:
- **Angular Testing Utilities** (TestBed, ComponentFixture): Use for new tests
- **Spectator**: Legacy testing utility for existing tests
- **ng-mocks**: For advanced mocking scenarios
### Current Setup (Migration in Progress)
- **Jest**: Used by 40 libraries (76% of codebase) - existing/legacy libraries
- **Vitest**: Used by 13 libraries (24% of codebase) - newer libraries and migrations
- **Testing Utilities Migration**:
- **Angular Testing Utilities** (TestBed, ComponentFixture): Preferred for new tests (25 test files)
- **Spectator**: Legacy testing utility for existing tests (56 test files)
- **ng-mocks**: For advanced mocking scenarios and child component mocking
### Migration Status by Domain
- **Migrated to Vitest**: `crm-data-access`, `checkout-*`, several `ui/*` components, `remission/*` libraries
- **Still on Jest**: Core `oms/*` libraries, main application, most legacy libraries
- **New Libraries**: Should use Vitest + Angular Testing Utilities from the start
### Test File Requirements
- Test files must end with `.spec.ts`
- Use AAA pattern (Arrange-Act-Assert)
- Include E2E testing attributes (`data-what`, `data-which`) in HTML templates
- Mock external dependencies and child components
- Use AAA pattern (Arrange-Act-Assert) consistently across both frameworks
- Include E2E testing attributes (`data-what`, `data-which`, dynamic `data-*` attributes) in HTML templates
- Mock external dependencies and child components using appropriate framework tools
- Verify E2E attributes are correctly applied in component unit tests
## State Management
- **NgRx**: Store, Effects, Entity, Component Store, Signals
- **RxJS**: For reactive programming patterns
- **NgRx Signals**: Primary state management with modern functional approach using `signalStore()`
- **Entity Management**: Uses `withEntities()` for normalized data storage
- **Session Persistence**: State persistence with `withStorage()` using SessionStorageProvider
- **Reactive Methods**: `rxMethod()` with `takeUntilKeydownEscape()` for user-cancellable operations
- **Custom RxJS Operators**: Specialized operators like `takeUntilAborted()`, `takeUntilKeydown()`
- **Error Handling**: `tapResponse()` for handling success/error states in stores
- **Lifecycle Hooks**: `withHooks()` for cleanup and initialization (e.g., orphaned entity cleanup)
## Styling
- **Tailwind CSS**: Primary styling framework with custom configuration
- **SCSS**: For component-specific styles
- **Custom Tailwind plugins**: For buttons, inputs, menus, typography
## Styling and Design System
- **Tailwind CSS**: Primary styling framework with extensive ISA-specific customization
- **Custom Breakpoints**: `isa-desktop` (1024px), `isa-desktop-l` (1440px), `isa-desktop-xl` (1920px)
- **Brand Color System**: Comprehensive `isa-*` color palette with semantic naming
- **Design Tokens**: Consistent spacing, typography, shadows, and border-radius values
- **Custom Tailwind Plugins** (7 plugins): `button`, `typography`, `menu`, `label`, `input`, `section`, `select-bullet`
- **SCSS Integration**: Component-scoped SCSS with BEM-like naming conventions
- **CSS Custom Properties**: Extensive use of CSS variables for theming and component variants
- **Typography System**: 14 custom text utilities (`.isa-text-heading-1-bold`, `.isa-text-body-2-regular`)
- **UI Component Libraries**: 15 specialized UI libraries with consistent API patterns
- **Storybook Integration**: Component documentation and development environment
## API Integration
- **Generated Swagger Clients**: Auto-generated TypeScript clients from OpenAPI specs
- **Available APIs**: availability, cat-search, checkout, crm, eis, inventory, isa, oms, print, wws
## API Integration and Data Access
- **Generated Swagger Clients**: 10 auto-generated TypeScript clients from OpenAPI specs in `generated/swagger/`
- **Available APIs**: availability-api, cat-search-api, checkout-api, crm-api, eis-api, inventory-api, isa-api, oms-api, print-api, wws-api
- **Generation Tool**: `ng-swagger-gen` with custom configurations per API
- **Post-processing**: Automatic Unicode character cleanup via `tools/fix-files.js`
- **Data Access Architecture**:
- **Business Logic Services**: Domain-specific services that wrap generated API clients
- **Type Safety**: Full TypeScript integration with Zod schema validation
- **Error Handling**: Global HTTP interceptor with custom error classes and automatic re-authentication
- **Configuration Management**: Dynamic API endpoint configuration through dependency injection
- **Request Cancellation**: Built-in support via AbortSignal and custom operators
- **Service Patterns**:
- **Modern Injection**: Uses `inject()` function with private field pattern
- **Logging Integration**: Comprehensive logging throughout data access layer
- **Consistent API**: Standardized service interfaces across all domains
## Build Configuration
- **Angular 20.1.2**: Latest Angular version
- **TypeScript 5.8.3**: For type safety
- **Node.js >= 22.0.0**: Required Node version
- **npm >= 10.0.0**: Required npm version
- **Node.js >= 22.0.0**: Required Node version (specified in package.json engines)
- **npm >= 10.0.0**: Required npm version (specified in package.json engines)
- **Nx 21.3.2**: Monorepo build system and development tools
- **Vite 6.3.5**: Build tool for faster development and testing (used with Vitest)
## Important Conventions
- **Component Prefix**: Each library has its own prefix (e.g., `remi` for remission, `oms` for OMS)
- **Standalone Components**: All new components should be standalone
- **Path Aliases**: Use TypeScript path aliases defined in `tsconfig.base.json` (e.g., `@isa/core/config`)
- **Project Names**: Can be found in each library's `project.json` file
## Important Conventions and Patterns
## Development Workflow Tips
- Always use `npx nx run` pattern for executing tasks
### Library Organization
- **Naming Pattern**: `[domain]-[layer]-[feature]` (e.g., `oms-feature-return-search`, `ui-buttons`)
- **Path Aliases**: `@isa/[domain]/[layer]/[feature]` (e.g., `@isa/oms/data-access`, `@isa/ui/buttons`)
- **Project Names**: Found in each library's `project.json` file, following consistent naming
### Component Architecture
- **Standalone Components**: All new components must be standalone with explicit imports
- **Component Prefixes**: Domain-specific prefixes for clear identification
- OMS features: `oms-feature-*` (e.g., `oms-feature-return-search-main`)
- Remission features: `remi-*`
- UI components: `ui-*`
- Core utilities: `core-*`
- **Signal-based Inputs**: Use Angular signals (`input()`, `computed()`) for reactive properties
- **Host Binding**: Dynamic CSS classes via Angular host properties
### Dependency Rules
- **Unidirectional Dependencies**: Feature → Shared/UI → Data Access → Infrastructure
- **Import Boundaries**: Use path aliases, avoid relative imports across domain boundaries
- **Generated API Imports**: Import from `@generated/swagger/[api-name]` for API clients
### Code Quality
- **Modern Angular Patterns**: Prefer `inject()` over constructor injection
- **Type Safety**: Use Zod schemas for runtime validation alongside TypeScript
- **Error Handling**: Custom error classes with specific error codes
- **E2E Testing**: Always include `data-what` and `data-which` attributes in templates
## Development Workflow and Best Practices
### Essential Commands
- **Development**: `npm start` (serves with SSL enabled by default)
- **Testing**: `npm test` (runs all library tests, skips main app)
- **Building**: `npm run build` (development), `npm run build-prod` (production)
- **Code Generation**: `npm run generate:swagger` (regenerates all API clients)
- **Code Quality**: `npm run prettier` (formats all files), `npm run pretty-quick` (staged files only)
### Nx Workflow Optimization
- Always use `npx nx run` pattern for executing specific tasks
- Include `--skip-cache` flag when running tests to ensure fresh results
- Use Nx's affected commands to optimize CI/CD pipelines
- Project graph visualization helps understand dependencies: `npx nx graph`
- Use affected commands for CI/CD optimization: `npx nx affected:test`
- Visualize project dependencies: `npx nx graph`
- The default git branch is `develop` (not `main`)
## Development Notes
- Use start target to start the application. Only one project can be started: isa-app
- Make sure to have a look at @docs/guidelines/testing.md before writing tests
- Make sure to add e2e attributes to the html. Those are important for my colleagues writen e2e tests
- Guide for the e2e testing attributes can be found in the testing.md
- When reviewing code follow the instructions @.github/review-instructions.md
### Testing Best Practices
- **New Libraries**: Use Vitest + Angular Testing Utilities
- **Legacy Libraries**: Continue using Jest + Spectator until migrated
- **Migration Priority**: UI components and new domains migrate first
- **E2E Attributes**: Verify `data-what`, `data-which`, and dynamic `data-*` attributes in tests
- **Mock Strategy**: Use appropriate framework tools (Spectator's `mocks` array vs TestBed providers)
### Code Quality Guidelines
- **Linting**: ESLint flat config with Nx dependency checks
- **Formatting**: Prettier with pre-commit hooks via Husky and lint-staged
- **Type Safety**: Zod validation for API boundaries, TypeScript strict mode
- **Bundle Optimization**: Monitor bundle sizes (2MB warning, 5MB error for main bundle)
- **Performance**: Use NgRx Signals for reactive state management with session persistence
## Development Notes and Guidelines
### Getting Started
- **Application Startup**: Only `isa-app` can be started - it's the main application entry point
- **SSL Development**: The development server runs with SSL by default (`npm start`), which is crucial for production-like authentication flows
- **Node Requirements**: Ensure Node.js ≥22.0.0 and npm ≥10.0.0 before starting development
- **First-Time Setup**: After cloning, run `npm install` then `npm start` to verify everything works
### Essential Documentation References
- **Testing Guidelines**: Review `docs/guidelines/testing.md` before writing any tests - it covers the Jest→Vitest migration, Spectator→Angular Testing Utilities transition, and E2E attribute requirements
- **Code Review Standards**: Follow the structured review process in `.github/review-instructions.md` with categorized feedback (🚨 Critical, ❗ Minor, ⚠️ Warnings, ✅ Good Practices)
- **E2E Testing Requirements**: Always include `data-what`, `data-which`, and dynamic `data-*` attributes in HTML templates - these are essential for automated testing by QA colleagues
### Library Development Patterns
- **New Library Creation**: Use Nx generators with domain-specific naming (`[domain]-[layer]-[feature]`)
- **Standalone Components**: All new components must be standalone with explicit imports - no more NgModules
- **Testing Framework Selection**:
- **New libraries**: Use Vitest + Angular Testing Utilities
- **Existing libraries**: Continue with Jest + Spectator until migrated
- **Path Aliases**: Always use `@isa/[domain]/[layer]/[feature]` - avoid relative imports across domain boundaries
### API Integration Workflow
- **Swagger Generation**: Run `npm run generate:swagger` to regenerate all 10 API clients when backend changes
- **Data Services**: Wrap generated API clients in domain-specific data-access services with proper error handling and Zod validation
- **State Management**: Use NgRx Signals with `signalStore()`, entity management, and session persistence for complex state
### Performance and Quality Considerations
- **Bundle Monitoring**: Watch bundle sizes (2MB warning, 5MB error for main bundle)
- **Testing Cache**: Always use `--skip-cache` flag when running tests to ensure reliable results
- **Code Quality**: Pre-commit hooks enforce Prettier formatting and ESLint rules automatically
- **Memory Management**: Clean up subscriptions and use OnPush change detection for optimal performance
### Common Troubleshooting
- **Build Issues**: Check Node version and run `npm install` if encountering module resolution errors
- **Test Failures**: Use `--skip-cache` flag and ensure test isolation (no shared state between tests)
- **SSL Certificates**: Development server uses SSL - accept certificate warnings in browser for localhost
- **Import Errors**: Verify path aliases in `tsconfig.base.json` and use absolute imports for cross-library dependencies
### Domain-Specific Conventions
- **Component Prefixes**: Use `oms-feature-*` for OMS, `remi-*` for remission, `ui-*` for shared components
- **Git Workflow**: Default branch is `develop` (not main), use conventional commits without co-author tags
- **Design System**: Use ISA-specific Tailwind utilities (`isa-accent-*`, `isa-text-*`) and custom breakpoints (`isa-desktop-*`)
- **Logging**: Use centralized logging service (`@isa/core/logging`) with contextual information for debugging
## Claude Code Workflow Definition
Based on comprehensive analysis of development patterns and available tools/MCP servers, here's the formal workflow for handling requests using the Task tool with specialized subagents:
### Phase 1: Request Analysis and Routing
#### 1.1 Initial Analysis (Always Use Subagents)
**Trigger**: Any user request
**Subagents**: `general-purpose` (for research) + domain specialists as needed
```
1. Parse request intent and complexity level
2. Classify request type:
- Feature Development (new functionality)
- Bug Fix/Maintenance (corrections)
- Code Quality/Refactoring (improvements)
- Testing/QA (validation)
- Architecture/Design (structure)
- Research/Investigation (exploration)
3. Assess information completeness and ambiguity
4. Determine required domains and expertise
```
#### 1.2 Clarification Decision Matrix
```
IF (high ambiguity + high impact) → REQUEST clarification
IF (medium ambiguity + architectural changes) → REQUEST optional clarification
IF (low ambiguity OR simple changes) → PROCEED with documented assumptions
```
### Phase 2: Task Decomposition and Agent Assignment
#### 2.1 Complexity-Based Routing
**Simple Tasks (Single file, isolated change)**:
- Route directly to specialist (`frontend-developer`, `typescript-pro`, etc.)
- Single quality gate at completion
**Moderate Tasks (Multiple related files, limited scope)**:
- Decompose into 2-4 parallel subagent tasks
- Example: `angular-mcp` + `test-automator` + `docs-architect`
**Complex Tasks (Multiple domains, significant scope)**:
- Hierarchical task tree with dependencies
- Example: `general-purpose` (analysis) → `nx-mcp` (structure) → `frontend-developer` + `test-automator` (parallel) → `code-reviewer` (validation)
**Architectural Tasks (System-wide changes)**:
- Multi-phase with user approval gates
- Example: `architect-review` → user approval → `frontend-developer` + `backend-architect` + `database-optimizer``performance-engineer` (validation)
#### 2.2 Optimal Agent Combinations
**Feature Development Pattern**:
```
context7 (fetch latest APIs)
→ frontend-developer + test-automator (parallel TDD)
→ ui-ux-designer (if UI changes)
→ code-reviewer (quality validation)
```
**Bug Fix Pattern**:
```
error-detective (analyze issue)
→ debugger (reproduce and fix)
→ test-automator (regression tests)
→ performance-engineer (if performance-related)
```
**Refactoring Pattern**:
```
general-purpose (understand current state)
→ legacy-modernizer (plan modernization)
→ test-automator (safety tests)
→ frontend-developer (implement changes)
→ architect-review (validate architecture)
```
### Phase 3: Execution with Progressive Quality Gates
#### 3.1 Standard Execution Flow
```
1. Each subagent receives atomic task with clear requirements
2. Subagent performs implementation with embedded quality checks
3. Results validated against project standards (E2E attributes, naming conventions)
4. Integration compatibility verified
5. Progress reported to user with next steps
```
#### 3.2 Quality Gates by Task Type
- **Code Changes**: `test-automator``code-reviewer``performance-engineer`
- **UI Changes**: `ui-visual-validator``frontend-security-coder``code-reviewer`
- **Architecture Changes**: `architect-review` → user approval → implementation → `performance-engineer`
- **API Changes**: `api-documenter``backend-security-coder` → integration tests
### Phase 4: Integration and Validation
#### 4.1 Continuous Validation
```
- Every file change triggers appropriate quality subagent
- Integration points validated by architect-review
- Performance impacts assessed by performance-engineer
- Security changes reviewed by security-auditor
```
#### 4.2 User Communication Strategy
```
- Real-time: Critical decisions and blockers
- Milestone-based: Major phase completions
- Exception-driven: Alternative approaches or issues
- Summary-based: Long-running workflow progress
```
### Specialized Subagent Usage Patterns
#### **Always Use These Subagents Proactively**:
- `general-purpose`: For codebase analysis, research, and complex multi-step tasks
- Available specialist agents based on task requirements (see full list below)
#### **Available MCP Server Integration**:
- **nx-mcp**: `nx_workspace`, `nx_project_details`, `nx_run_generator`, `nx_visualize_graph`, etc.
- **angular-mcp**: `list_projects`, `search_documentation`
- **context7**: `resolve-library-id`, `get-library-docs` for latest package documentation
#### **Project-Relevant Subagent Types**:
**Tier 1 - Essential (Most Frequently Needed)**:
- `frontend-developer`: Angular 20.1.2 feature development, standalone components, domain library implementation
- `typescript-pro`: Advanced TypeScript 5.8.3 patterns, type system optimization, interface design
- `test-automator`: Jest→Vitest migration, Spectator→Angular Testing Utilities, unit testing, E2E attributes
- `ui-ux-designer`: Tailwind CSS design system, UI component libraries, responsive design
**Tier 2 - Important (Regularly Needed)**:
- `code-reviewer`: ESLint compliance, Angular best practices, code quality validation
- `performance-engineer`: Angular optimization, bundle analysis, NgRx performance patterns
- `api-documenter`: Swagger/OpenAPI integration, API client documentation
- `docs-architect`: Technical documentation, architecture guides, CLAUDE.md maintenance
**Tier 3 - Specialized (Domain-Specific)**:
- `architect-review`: Nx monorepo architecture, domain-driven design validation
- `frontend-security-coder`: Angular security patterns, XSS prevention, authentication flows
- `deployment-engineer`: Angular build pipelines, Nx CI/CD, npm script automation
- `debugger`: Error analysis, RxJS debugging, state management troubleshooting
**Cross-Cutting Support**:
- `general-purpose`: Complex analysis, codebase research, multi-domain investigations
- `error-detective`: Production issue investigation, log analysis, performance bottlenecks
### Escalation and Error Handling
#### Immediate Escalation Triggers:
- Subagent cannot access required files or resources
- Conflicting requirements between parallel tasks
- Security vulnerabilities discovered
- Breaking changes with no compatibility path
#### Recovery Hierarchy:
1. Subagent self-resolution (retry with different approach)
2. Alternative subagent assignment (different specialist)
3. Task redecomposition (break down further)
4. User consultation (clarification or decision needed)
### Example Workflow: "Add Dark Mode Feature"
```
1. ANALYSIS PHASE:
Task(general-purpose, "Research Angular/Tailwind dark mode patterns and analyze ISA theme system")
2. PLANNING PHASE:
Task(ui-ux-designer, "Design dark mode integration with ISA color system and Tailwind classes")
Task(frontend-developer, "Plan component modifications and theme service architecture")
3. IMPLEMENTATION PHASE (can run in parallel):
Task(frontend-developer, "Implement Angular theme service and component modifications")
Task(typescript-pro, "Create TypeScript interfaces and type-safe theme system")
Task(test-automator, "Create Jest/Vitest tests with E2E attributes for theme functionality")
4. VALIDATION PHASE:
Task(code-reviewer, "Review Angular patterns, NgRx integration, and code quality")
Task(performance-engineer, "Assess bundle size impact and runtime performance")
5. INTEGRATION PHASE:
Task(test-automator, "Run full Nx test suite and validate E2E attributes")
Task(docs-architect, "Update Storybook documentation and component usage guides")
```
### MCP Server Integration Workflow: "Integrate New Angular Library"
```
1. RESEARCH PHASE:
- Use mcp__context7__resolve-library-id to find the library
- Use mcp__context7__get-library-docs to understand APIs
- Use mcp__angular-mcp__search_documentation for Angular integration patterns
2. PROJECT SETUP:
- Use mcp__nx-mcp__nx_generators to find appropriate generators
- Use mcp__nx-mcp__nx_run_generator to scaffold integration components
- Use mcp__nx-mcp__nx_project_details to understand impact scope
3. IMPLEMENTATION WITH SUBAGENTS:
Task(typescript-pro, "Create TypeScript interfaces for library integration")
Task(frontend-developer, "Implement Angular service wrapping the library")
Task(test-automator, "Create unit and integration tests")
4. VALIDATION:
- Use mcp__nx-mcp__nx_visualize_graph to verify dependency relationships
- Use mcp__nx-mcp__nx_current_running_tasks_details to monitor builds
Task(code-reviewer, "Review integration patterns and code quality")
```
This comprehensive workflow leverages ALL available MCP servers and specialized subagents for maximum efficiency, with each agent handling both analysis AND implementation tasks according to their expertise.

View File

@@ -0,0 +1,60 @@
import {
type Meta,
type StoryObj,
applicationConfig,
argsToTemplate,
moduleMetadata,
} from '@storybook/angular';
import { ProductInfoRedemptionComponent } from '@isa/checkout/shared/product-info';
import { provideProductImageUrl } from '@isa/shared/product-image';
import { provideProductRouterLinkBuilder } from '@isa/shared/product-router-link';
import { provideRouter } from '@angular/router';
const meta: Meta<ProductInfoRedemptionComponent> = {
title: 'checkout/shared/product-info/ProductInfoRedemption',
component: ProductInfoRedemptionComponent,
decorators: [
applicationConfig({
providers: [
provideRouter([
{ path: ':ean', component: ProductInfoRedemptionComponent },
]),
],
}),
moduleMetadata({
providers: [
provideProductImageUrl('https://produktbilder-test.paragon-data.net'),
provideProductRouterLinkBuilder((ean: string) => ean),
],
}),
],
};
export default meta;
type Story = StoryObj<ProductInfoRedemptionComponent>;
export const Primary: Story = {
args: {
item: {
product: {
ean: '9783498007706',
name: 'Die Assistentin',
contributors: 'Wahl, Caroline',
format: 'TB',
formatDetail: 'Taschenbuch (Kartoniert)',
manufacturer: 'Test Manufacturer',
publicationDate: '2023-01-01',
},
redemptionPoints: 100,
},
orientation: 'vertical',
},
argTypes: {
item: { control: 'object' },
orientation: {
control: { type: 'radio' },
options: ['horizontal', 'vertical'],
},
},
};

View File

@@ -0,0 +1,59 @@
import {
type Meta,
type StoryObj,
applicationConfig,
argsToTemplate,
moduleMetadata,
} from '@storybook/angular';
import { StockInfoComponent } from '@isa/checkout/shared/product-info';
import { RemissionStockService } from '@isa/remission/data-access';
import { StockInfoDTO } from '@generated/swagger/inventory-api';
const meta: Meta<StockInfoComponent> = {
title: 'checkout/shared/product-info/StockInfoComponent',
component: StockInfoComponent,
decorators: [
applicationConfig({
providers: [],
}),
moduleMetadata({
imports: [],
providers: [
{
provide: RemissionStockService,
useValue: {
fetchStock: async (
params: { itemIds: number[]; assignedStockId?: number },
abortSignal?: AbortSignal,
) => {
const result: StockInfoDTO = {
itemId: params.itemIds[0],
stockId: params.assignedStockId,
inStock: 14,
};
await new Promise((resolve) => setTimeout(resolve, 1000));
return [result];
},
},
},
],
}),
],
};
export default meta;
type Story = StoryObj<StockInfoComponent>;
export const Primary: Story = {
args: {
item: {
id: 123456,
catalogAvailability: {
ssc: '999',
sscText: 'Lieferbar in 1-3 Werktagen',
},
},
},
};

View File

@@ -1,48 +1,57 @@
import { argsToTemplate, Meta } from '@storybook/angular';
import { ProductFormatIconGroup } from '@isa/icons';
import { ProductFormatComponent } from '@isa/shared/product-foramt';
type ProductFormatInputs = {
format: string;
formatDetail: string;
};
const options = Object.keys(ProductFormatIconGroup).map((key) =>
key.toUpperCase(),
);
const meta: Meta<ProductFormatInputs> = {
title: 'shared/product-format/ProductFormat',
component: ProductFormatComponent,
argTypes: {
format: {
control: {
type: 'select',
},
options,
description: 'The product format to display the icon for.',
defaultValue: options[0],
},
formatDetail: {
control: {
type: 'text',
},
description: 'The detail text for the product format.',
defaultValue: 'Default Format Detail',
},
},
args: {
format: options[0], // Default value for the product format
formatDetail: 'Default Format Detail', // Default value for the format detail
},
render: (args) => ({
props: args,
template: `<shared-product-format ${argsToTemplate(args)}></shared-product-format>`,
}),
};
export default meta;
type Story = typeof meta;
export const Default: Story = {};
import { argsToTemplate, Meta } from '@storybook/angular';
import { ProductFormatIconGroup } from '@isa/icons';
import { ProductFormatComponent } from '@isa/shared/product-foramt';
type ProductFormatInputs = {
format: string;
formatDetail: string;
formatDetailsBold: boolean;
};
const options = Object.keys(ProductFormatIconGroup).map((key) =>
key.toUpperCase(),
);
const meta: Meta<ProductFormatInputs> = {
title: 'shared/product-format/ProductFormat',
component: ProductFormatComponent,
argTypes: {
format: {
control: {
type: 'select',
},
options,
description: 'The product format to display the icon for.',
defaultValue: options[0],
},
formatDetail: {
control: {
type: 'text',
},
description: 'The detail text for the product format.',
defaultValue: 'Default Format Detail',
},
formatDetailsBold: {
control: {
type: 'boolean',
},
description: 'Whether the format detail text should be bold.',
defaultValue: false,
},
},
args: {
format: options[0], // Default value for the product format
formatDetail: 'Default Format Detail', // Default value for the format detail
formatDetailsBold: false, // Default value for the format details bold
},
render: (args) => ({
props: args,
template: `<shared-product-format ${argsToTemplate(args)}></shared-product-format>`,
}),
};
export default meta;
type Story = typeof meta;
export const Default: Story = {};

View File

@@ -1,9 +1,10 @@
import { ItemDTO } from '@generated/swagger/cat-search-api';
import { Product } from './product';
import { Availability } from './availability';
export interface Item extends ItemDTO {
id: number;
product: Product;
catalogAvailability: Availability;
}
import { ItemDTO } from '@generated/swagger/cat-search-api';
import { Product } from './product';
import { Availability } from './availability';
export interface Item extends ItemDTO {
id: number;
product: Product;
catalogAvailability: Availability;
redemptionPoints?: number;
}

View File

@@ -0,0 +1,7 @@
# checkout-shared-product-info
This library was generated with [Nx](https://nx.dev).
## Running unit tests
Run `nx test checkout-shared-product-info` to execute the unit tests.

View File

@@ -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: 'checkout',
style: 'camelCase',
},
],
'@angular-eslint/component-selector': [
'error',
{
type: 'element',
prefix: 'checkout',
style: 'kebab-case',
},
],
},
},
{
files: ['**/*.html'],
// Override or add rules here
rules: {},
},
];

View File

@@ -0,0 +1,20 @@
{
"name": "checkout-shared-product-info",
"$schema": "../../../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "libs/checkout/shared/product-info/src",
"prefix": "checkout",
"projectType": "library",
"tags": [],
"targets": {
"test": {
"executor": "@nx/vite:test",
"outputs": ["{options.reportsDirectory}"],
"options": {
"reportsDirectory": "../../../../coverage/libs/checkout/shared/product-info"
}
},
"lint": {
"executor": "@nx/eslint:lint"
}
}
}

View File

@@ -0,0 +1,2 @@
export * from './lib/product-info/product-info-redemption.component';
export * from './lib/stock-info/stock-info.component';

View File

@@ -0,0 +1,15 @@
:host {
@apply grid grid-flow-col gap-6 text-neutral-900;
}
:host.vertical {
@apply grid-flow-row;
}
shared-product-format span {
@apply isa-text-body-2-bold text-isa-secondary-900;
}
.lh-1 {
line-height: 1;
}

View File

@@ -0,0 +1,44 @@
@let prd = item().product;
@let rPoints = item().redemptionPoints;
<div class="grid grid-cols-[auto,1fr] gap-6">
<div>
<img
sharedProductRouterLink
sharedProductImage
[ean]="prd.ean"
[alt]="prd.name"
class="checkout-product-info-redemption__image w-14"
data-what="product-image"
/>
</div>
<div class="flex flex-1 flex-col justify-between gap-1">
<div class="isa-text-body-2-bold">{{ prd.contributors }}</div>
<div
[class.isa-text-body-2-regular]="orientation() === 'horizontal'"
[class.isa-text-subtitle-1-regular]="orientation() === 'vertical'"
>
{{ prd.name }}
</div>
<div class="isa-text-body-2-regular">
<span class="isa-text-body-2-bold">{{ rPoints }}</span>
Lesepunkte
</div>
</div>
</div>
<div
class="flex flex-1 flex-col justify-between gap-1"
[class.ml-20]="orientation() === 'vertical'"
>
<shared-product-format
[format]="prd.format"
[formatDetail]="prd.formatDetail"
[formatDetailsBold]="true"
></shared-product-format>
<div class="isa-text-body-2-regular text-neutral-600">
{{ prd.manufacturer }} | {{ prd.ean }}
</div>
<div class="isa-text-body-2-regular text-neutral-600">
{{ prd.publicationDate | date: 'dd. MMMM yyyy' }}
</div>
</div>

View File

@@ -0,0 +1,42 @@
import { ChangeDetectionStrategy, Component, input } from '@angular/core';
import { Product } from '@isa/catalogue/data-access';
import { ProductImageDirective } from '@isa/shared/product-image';
import { ProductRouterLinkDirective } from '@isa/shared/product-router-link';
import { ProductFormatComponent } from '@isa/shared/product-foramt';
import { DatePipe } from '@angular/common';
export type ProductInfoItem = {
product: Pick<
Product,
| 'ean'
| 'name'
| 'contributors'
| 'format'
| 'formatDetail'
| 'manufacturer'
| 'publicationDate'
>;
redemptionPoints: number;
};
@Component({
selector: 'checkout-product-info-redemption',
templateUrl: './product-info-redemption.component.html',
styleUrls: ['./product-info-redemption.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [
ProductImageDirective,
ProductRouterLinkDirective,
ProductFormatComponent,
DatePipe,
],
host: {
'[class]': '[orientation()]',
},
})
export class ProductInfoRedemptionComponent {
item = input.required<ProductInfoItem>();
orientation = input<'horizontal' | 'vertical'>('vertical');
}

View File

@@ -0,0 +1,3 @@
:host {
@apply grid grid-flow-row gap-2;
}

View File

@@ -0,0 +1,16 @@
@let itm = item();
<div
class="grid grid-cols-[auto,1fr] gap-2 items-center text-isa-secondary-900"
>
<ng-icon size="1.5rem" name="isaFiliale"></ng-icon>
<div class="isa-text-body-2-bold">
@if (loading()) {
<ui-skeleton-loader class="w-8 h-5 inline-block"></ui-skeleton-loader>
} @else {
{{ inStock() }}x
}
</div>
</div>
<div class="text-isa-neutral-600 isa-text-body-2-regular">
{{ itm.catalogAvailability.ssc }} - {{ itm.catalogAvailability.sscText }}
</div>

View File

@@ -0,0 +1,57 @@
import {
ChangeDetectionStrategy,
Component,
computed,
inject,
input,
resource,
} from '@angular/core';
import { RemissionStockService } from '@isa/remission/data-access';
import { NgIconComponent, provideIcons } from '@ng-icons/core';
import { isaFiliale } from '@isa/icons';
import { Item } from '@isa/catalogue/data-access';
import { SkeletonLoaderComponent } from '@isa/ui/skeleton-loader';
export type StockInfoItem = {
id: Item['id'];
catalogAvailability: Required<
Pick<Item['catalogAvailability'], 'ssc' | 'sscText'>
>;
};
@Component({
selector: 'checkout-stock-info',
templateUrl: './stock-info.component.html',
styleUrls: ['./stock-info.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [NgIconComponent, SkeletonLoaderComponent],
providers: [provideIcons({ isaFiliale })],
})
export class StockInfoComponent {
#stockService = inject(RemissionStockService);
item = input.required<StockInfoItem>();
stockResource = resource({
params: () => this.item().id,
loader: ({ params, abortSignal }) =>
this.#stockService.fetchStock(
{
itemIds: [params],
},
abortSignal,
),
});
inStock = computed(() => {
if (this.stockResource.hasValue()) {
const stock = this.stockResource.value();
return stock[0]?.inStock ?? 0;
}
return 0;
});
loading = computed(() => this.stockResource.isLoading() && !this.inStock());
}

View File

@@ -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(),
);

View File

@@ -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"
}
]
}

View File

@@ -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"]
}

View File

@@ -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"]
}

View File

@@ -0,0 +1,28 @@
/// <reference types='vitest' />
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 defineConfig(() => ({
root: __dirname,
cacheDir: '../../../../node_modules/.vite/libs/checkout/shared/product-info',
plugins: [angular(), nxViteTsPaths(), nxCopyAssetsPlugin(['*.md'])],
// Uncomment this if you are using workers.
// worker: {
// plugins: [ nxViteTsPaths() ],
// },
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'],
coverage: {
reportsDirectory:
'../../../../coverage/libs/checkout/shared/product-info',
provider: 'v8' as const,
},
},
}));

View File

@@ -1,8 +1,8 @@
import { z } from 'zod';
export const FetchStockInStockSchema = z.object({
assignedStockId: z.number(),
itemIds: z.array(z.number()),
});
export type FetchStockInStock = z.infer<typeof FetchStockInStockSchema>;
import { z } from 'zod';
export const FetchStockInStockSchema = z.object({
assignedStockId: z.number().optional(),
itemIds: z.array(z.number()),
});
export type FetchStockInStock = z.infer<typeof FetchStockInStockSchema>;

View File

@@ -110,13 +110,23 @@ export class RemissionStockService {
this.#logger.debug('Fetching stock info', () => ({ params }));
const parsed = FetchStockInStockSchema.parse(params);
let assignedStockId: number;
if (parsed.assignedStockId) {
assignedStockId = parsed.assignedStockId;
} else {
assignedStockId = await this.fetchAssignedStock(abortSignal).then(
(s) => s.id,
);
}
this.#logger.info('Fetching stock info from API', () => ({
stockId: parsed.assignedStockId,
itemCount: parsed.itemIds.length,
}));
let req$ = this.#stockService.StockInStock({
stockId: parsed.assignedStockId,
stockId: assignedStockId,
articleIds: parsed.itemIds,
});

View File

@@ -1,24 +1,30 @@
import { ChangeDetectionStrategy, Component, input } from '@angular/core';
import { ProductFormatIconComponent } from './product-format-icon.component';
@Component({
selector: 'shared-product-format',
template: `<shared-product-format-icon
[format]="format()"
></shared-product-format-icon>
<span class="isa-text-body-2-regular text-isa-secondary-900">
{{ formatDetail() }}
</span>`,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [ProductFormatIconComponent],
host: {
'[class]': '["flex", "items-center", "gap-2"]',
'data-what': 'product-format',
'data-which': 'shared-product-format',
},
})
export class ProductFormatComponent {
format = input.required<string>();
formatDetail = input.required<string>();
}
import { ChangeDetectionStrategy, Component, input } from '@angular/core';
import { ProductFormatIconComponent } from './product-format-icon.component';
@Component({
selector: 'shared-product-format',
template: `<shared-product-format-icon
[format]="format()"
></shared-product-format-icon>
<span
[class.isa-text-body-2-regular]="!formatDetailsBold()"
[class.isa-text-body-2-bold]="formatDetailsBold()"
class="text-isa-secondary-900"
>
{{ formatDetail() }}
</span>`,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [ProductFormatIconComponent],
host: {
'[class]': '["flex", "items-center", "gap-2"]',
'data-what': 'product-format',
'data-which': 'shared-product-format',
},
})
export class ProductFormatComponent {
format = input.required<string>();
formatDetail = input.required<string>();
formatDetailsBold = input<boolean>(false);
}

63533
package-lock.json generated
View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,138 +1,138 @@
{
"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 isa-app -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"
},
"private": true,
"dependencies": {
"@angular-architects/ngrx-toolkit": "^20.4.0",
"@angular/animations": "20.1.2",
"@angular/cdk": "20.1.2",
"@angular/common": "20.1.2",
"@angular/compiler": "20.1.2",
"@angular/core": "20.1.2",
"@angular/forms": "20.1.2",
"@angular/localize": "20.1.2",
"@angular/platform-browser": "20.1.2",
"@angular/platform-browser-dynamic": "20.1.2",
"@angular/router": "20.1.2",
"@angular/service-worker": "20.1.2",
"@microsoft/signalr": "^8.0.7",
"@ng-icons/core": "32.0.0",
"@ng-icons/material-icons": "32.0.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.0",
"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.19.1",
"@analogjs/vitest-angular": "1.19.1",
"@angular-devkit/build-angular": "20.1.1",
"@angular-devkit/core": "20.1.1",
"@angular-devkit/schematics": "20.1.1",
"@angular/build": "20.1.1",
"@angular/cli": "~20.1.0",
"@angular/compiler-cli": "20.1.2",
"@angular/language-service": "20.1.2",
"@angular/pwa": "20.1.1",
"@eslint/js": "^9.8.0",
"@ngneat/spectator": "19.6.2",
"@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.1.1",
"@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.1.1",
"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.13.5",
"ng-packagr": "20.1.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"
},
"optionalDependencies": {
"@esbuild/linux-x64": "^0.25.5"
},
"engines": {
"node": ">=22.00.0",
"npm": ">=10.0.0"
},
"overrides": {
"jest-environment-jsdom": {
"jsdom": "26.0.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 isa-app -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"
},
"private": true,
"dependencies": {
"@angular-architects/ngrx-toolkit": "^20.4.0",
"@angular/animations": "20.1.2",
"@angular/cdk": "20.1.2",
"@angular/common": "20.1.2",
"@angular/compiler": "20.1.2",
"@angular/core": "20.1.2",
"@angular/forms": "20.1.2",
"@angular/localize": "20.1.2",
"@angular/platform-browser": "20.1.2",
"@angular/platform-browser-dynamic": "20.1.2",
"@angular/router": "20.1.2",
"@angular/service-worker": "20.1.2",
"@microsoft/signalr": "^8.0.7",
"@ng-icons/core": "32.0.0",
"@ng-icons/material-icons": "32.0.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.0",
"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.19.1",
"@analogjs/vitest-angular": "1.19.1",
"@angular-devkit/build-angular": "20.1.1",
"@angular-devkit/core": "20.1.1",
"@angular-devkit/schematics": "20.1.1",
"@angular/build": "20.1.1",
"@angular/cli": "~20.1.0",
"@angular/compiler-cli": "20.1.2",
"@angular/language-service": "20.1.2",
"@angular/pwa": "20.1.1",
"@eslint/js": "^9.8.0",
"@ngneat/spectator": "19.6.2",
"@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.1.1",
"@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.1.1",
"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.13.5",
"ng-packagr": "20.1.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"
},
"optionalDependencies": {
"@esbuild/linux-x64": "^0.25.5"
},
"engines": {
"node": ">=22.00.0",
"npm": ">=10.0.0"
},
"overrides": {
"jest-environment-jsdom": {
"jsdom": "26.0.0"
}
}
}

View File

@@ -1,140 +1,143 @@
{
"compileOnSave": false,
"compilerOptions": {
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"importHelpers": false,
"target": "ES2022",
"module": "es2020",
"lib": ["es2020", "dom"],
"baseUrl": "./",
"paths": {
"@adapter/*": ["apps/isa-app/src/adapter/*/index.ts"],
"@cdn/*": ["apps/isa-app/src/cdn/*/index.ts"],
"@core/*": ["apps/isa-app/src/core/*/index.ts"],
"@core/config": ["libs/core/config/src/index.ts"],
"@domain/*": ["apps/isa-app/src/domain/*/index.ts"],
"@external/*": ["apps/isa-app/src/external/*/index.ts"],
"@generated/swagger/availability-api": [
"generated/swagger/availability-api/src/index.ts"
],
"@generated/swagger/cat-search-api": [
"generated/swagger/cat-search-api/src/index.ts"
],
"@generated/swagger/checkout-api": [
"generated/swagger/checkout-api/src/index.ts"
],
"@generated/swagger/crm-api": ["generated/swagger/crm-api/src/index.ts"],
"@generated/swagger/eis-api": ["generated/swagger/eis-api/src/index.ts"],
"@generated/swagger/inventory-api": [
"generated/swagger/inventory-api/src/index.ts"
],
"@generated/swagger/isa-api": ["generated/swagger/isa-api/src/index.ts"],
"@generated/swagger/oms-api": ["generated/swagger/oms-api/src/index.ts"],
"@generated/swagger/print-api": [
"generated/swagger/print-api/src/index.ts"
],
"@generated/swagger/wws-api": ["generated/swagger/wws-api/src/index.ts"],
"@hub/*": ["apps/isa-app/src/hub/*/index.ts"],
"@isa/catalogue/data-access": ["libs/catalogue/data-access/src/index.ts"],
"@isa/checkout/data-access": ["libs/checkout/data-access/src/index.ts"],
"@isa/checkout/feature/reward-catalog": [
"libs/checkout/feature/reward-catalog/src/index.ts"
],
"@isa/common/data-access": ["libs/common/data-access/src/index.ts"],
"@isa/common/decorators": ["libs/common/decorators/src/index.ts"],
"@isa/common/print": ["libs/common/print/src/index.ts"],
"@isa/core/config": ["libs/core/config/src/index.ts"],
"@isa/core/logging": ["libs/core/logging/src/index.ts"],
"@isa/core/notifications": ["libs/core/notifications/src/index.ts"],
"@isa/core/storage": ["libs/core/storage/src/index.ts"],
"@isa/core/tabs": ["libs/core/tabs/src/index.ts"],
"@isa/crm/data-access": ["libs/crm/data-access/src/index.ts"],
"@isa/icons": ["libs/icons/src/index.ts"],
"@isa/oms/data-access": ["libs/oms/data-access/src/index.ts"],
"@isa/oms/feature/return-details": [
"libs/oms/feature/return-details/src/index.ts"
],
"@isa/oms/feature/return-process": [
"libs/oms/feature/return-process/src/index.ts"
],
"@isa/oms/feature/return-review": [
"libs/oms/feature/return-review/src/index.ts"
],
"@isa/oms/feature/return-search": [
"libs/oms/feature/return-search/src/index.ts"
],
"@isa/oms/feature/return-summary": [
"libs/oms/feature/return-summary/src/index.ts"
],
"@isa/oms/shared/product-info": [
"libs/oms/shared/product-info/src/index.ts"
],
"@isa/oms/shared/task-list": ["libs/oms/shared/task-list/src/index.ts"],
"@isa/oms/utils/translation": ["libs/oms/utils/translation/src/index.ts"],
"@isa/remission/data-access": ["libs/remission/data-access/src/index.ts"],
"@isa/remission/feature/remission-list": [
"libs/remission/feature/remission-list/src/index.ts"
],
"@isa/remission/feature/remission-return-receipt-details": [
"libs/remission/feature/remission-return-receipt-details/src/index.ts"
],
"@isa/remission/feature/remission-return-receipt-list": [
"libs/remission/feature/remission-return-receipt-list/src/index.ts"
],
"@isa/remission/shared/product": [
"libs/remission/shared/product/src/index.ts"
],
"@isa/remission/shared/remission-start-dialog": [
"libs/remission/shared/remission-start-dialog/src/index.ts"
],
"@isa/remission/shared/return-receipt-actions": [
"libs/remission/shared/return-receipt-actions/src/index.ts"
],
"@isa/remission/shared/search-item-to-remit-dialog": [
"libs/remission/shared/search-item-to-remit-dialog/src/index.ts"
],
"@isa/shared/filter": ["libs/shared/filter/src/index.ts"],
"@isa/shared/product-foramt": ["libs/shared/product-format/src/index.ts"],
"@isa/shared/product-image": ["libs/shared/product-image/src/index.ts"],
"@isa/shared/product-router-link": [
"libs/shared/product-router-link/src/index.ts"
],
"@isa/shared/scanner": ["libs/shared/scanner/src/index.ts"],
"@isa/ui/bullet-list": ["libs/ui/bullet-list/src/index.ts"],
"@isa/ui/buttons": ["libs/ui/buttons/src/index.ts"],
"@isa/ui/datepicker": ["libs/ui/datepicker/src/index.ts"],
"@isa/ui/dialog": ["libs/ui/dialog/src/index.ts"],
"@isa/ui/empty-state": ["libs/ui/empty-state/src/index.ts"],
"@isa/ui/expandable": ["libs/ui/expandable/src/index.ts"],
"@isa/ui/input-controls": ["libs/ui/input-controls/src/index.ts"],
"@isa/ui/item-rows": ["libs/ui/item-rows/src/index.ts"],
"@isa/ui/label": ["libs/ui/label/src/index.ts"],
"@isa/ui/layout": ["libs/ui/layout/src/index.ts"],
"@isa/ui/menu": ["libs/ui/menu/src/index.ts"],
"@isa/ui/progress-bar": ["libs/ui/progress-bar/src/index.ts"],
"@isa/ui/search-bar": ["libs/ui/search-bar/src/index.ts"],
"@isa/ui/skeleton-loader": ["libs/ui/skeleton-loader/src/index.ts"],
"@isa/ui/toolbar": ["libs/ui/toolbar/src/index.ts"],
"@isa/ui/tooltip": ["libs/ui/tooltip/src/index.ts"],
"@isa/utils/ean-validation": ["libs/utils/ean-validation/src/index.ts"],
"@isa/utils/scroll-position": ["libs/utils/scroll-position/src/index.ts"],
"@isa/utils/z-safe-parse": ["libs/utils/z-safe-parse/src/index.ts"],
"@modal/*": ["apps/isa-app/src/modal/*/index.ts"],
"@page/*": ["apps/isa-app/src/page/*/index.ts"],
"@shared/*": ["apps/isa-app/src/shared/*/index.ts"],
"@shared/components/*": ["apps/isa-app/src/shared/components/*/index.ts"],
"@shared/directives/*": ["apps/isa-app/src/shared/directives/*/index.ts"],
"@shared/pipes/*": ["apps/isa-app/src/shared/pipes/*/index.ts"],
"@shared/services/*": ["apps/isa-app/src/shared/services/*/index.ts"],
"@swagger/*": ["apps/isa-app/src/swagger/*/index.ts"],
"@ui/*": ["apps/isa-app/src/ui/*/index.ts"],
"@utils/*": ["apps/isa-app/src/utils/*/index.ts"],
"packageJson": ["package.json"]
},
"skipLibCheck": true
}
}
{
"compileOnSave": false,
"compilerOptions": {
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"importHelpers": false,
"target": "ES2022",
"module": "es2020",
"lib": ["es2020", "dom"],
"baseUrl": "./",
"paths": {
"@adapter/*": ["apps/isa-app/src/adapter/*/index.ts"],
"@cdn/*": ["apps/isa-app/src/cdn/*/index.ts"],
"@core/*": ["apps/isa-app/src/core/*/index.ts"],
"@core/config": ["libs/core/config/src/index.ts"],
"@domain/*": ["apps/isa-app/src/domain/*/index.ts"],
"@external/*": ["apps/isa-app/src/external/*/index.ts"],
"@generated/swagger/availability-api": [
"generated/swagger/availability-api/src/index.ts"
],
"@generated/swagger/cat-search-api": [
"generated/swagger/cat-search-api/src/index.ts"
],
"@generated/swagger/checkout-api": [
"generated/swagger/checkout-api/src/index.ts"
],
"@generated/swagger/crm-api": ["generated/swagger/crm-api/src/index.ts"],
"@generated/swagger/eis-api": ["generated/swagger/eis-api/src/index.ts"],
"@generated/swagger/inventory-api": [
"generated/swagger/inventory-api/src/index.ts"
],
"@generated/swagger/isa-api": ["generated/swagger/isa-api/src/index.ts"],
"@generated/swagger/oms-api": ["generated/swagger/oms-api/src/index.ts"],
"@generated/swagger/print-api": [
"generated/swagger/print-api/src/index.ts"
],
"@generated/swagger/wws-api": ["generated/swagger/wws-api/src/index.ts"],
"@hub/*": ["apps/isa-app/src/hub/*/index.ts"],
"@isa/catalogue/data-access": ["libs/catalogue/data-access/src/index.ts"],
"@isa/checkout/data-access": ["libs/checkout/data-access/src/index.ts"],
"@isa/checkout/feature/reward-catalog": [
"libs/checkout/feature/reward-catalog/src/index.ts"
],
"@isa/checkout/shared/product-info": [
"libs/checkout/shared/product-info/src/index.ts"
],
"@isa/common/data-access": ["libs/common/data-access/src/index.ts"],
"@isa/common/decorators": ["libs/common/decorators/src/index.ts"],
"@isa/common/print": ["libs/common/print/src/index.ts"],
"@isa/core/config": ["libs/core/config/src/index.ts"],
"@isa/core/logging": ["libs/core/logging/src/index.ts"],
"@isa/core/notifications": ["libs/core/notifications/src/index.ts"],
"@isa/core/storage": ["libs/core/storage/src/index.ts"],
"@isa/core/tabs": ["libs/core/tabs/src/index.ts"],
"@isa/crm/data-access": ["libs/crm/data-access/src/index.ts"],
"@isa/icons": ["libs/icons/src/index.ts"],
"@isa/oms/data-access": ["libs/oms/data-access/src/index.ts"],
"@isa/oms/feature/return-details": [
"libs/oms/feature/return-details/src/index.ts"
],
"@isa/oms/feature/return-process": [
"libs/oms/feature/return-process/src/index.ts"
],
"@isa/oms/feature/return-review": [
"libs/oms/feature/return-review/src/index.ts"
],
"@isa/oms/feature/return-search": [
"libs/oms/feature/return-search/src/index.ts"
],
"@isa/oms/feature/return-summary": [
"libs/oms/feature/return-summary/src/index.ts"
],
"@isa/oms/shared/product-info": [
"libs/oms/shared/product-info/src/index.ts"
],
"@isa/oms/shared/task-list": ["libs/oms/shared/task-list/src/index.ts"],
"@isa/oms/utils/translation": ["libs/oms/utils/translation/src/index.ts"],
"@isa/remission/data-access": ["libs/remission/data-access/src/index.ts"],
"@isa/remission/feature/remission-list": [
"libs/remission/feature/remission-list/src/index.ts"
],
"@isa/remission/feature/remission-return-receipt-details": [
"libs/remission/feature/remission-return-receipt-details/src/index.ts"
],
"@isa/remission/feature/remission-return-receipt-list": [
"libs/remission/feature/remission-return-receipt-list/src/index.ts"
],
"@isa/remission/shared/product": [
"libs/remission/shared/product/src/index.ts"
],
"@isa/remission/shared/remission-start-dialog": [
"libs/remission/shared/remission-start-dialog/src/index.ts"
],
"@isa/remission/shared/return-receipt-actions": [
"libs/remission/shared/return-receipt-actions/src/index.ts"
],
"@isa/remission/shared/search-item-to-remit-dialog": [
"libs/remission/shared/search-item-to-remit-dialog/src/index.ts"
],
"@isa/shared/filter": ["libs/shared/filter/src/index.ts"],
"@isa/shared/product-foramt": ["libs/shared/product-format/src/index.ts"],
"@isa/shared/product-image": ["libs/shared/product-image/src/index.ts"],
"@isa/shared/product-router-link": [
"libs/shared/product-router-link/src/index.ts"
],
"@isa/shared/scanner": ["libs/shared/scanner/src/index.ts"],
"@isa/ui/bullet-list": ["libs/ui/bullet-list/src/index.ts"],
"@isa/ui/buttons": ["libs/ui/buttons/src/index.ts"],
"@isa/ui/datepicker": ["libs/ui/datepicker/src/index.ts"],
"@isa/ui/dialog": ["libs/ui/dialog/src/index.ts"],
"@isa/ui/empty-state": ["libs/ui/empty-state/src/index.ts"],
"@isa/ui/expandable": ["libs/ui/expandable/src/index.ts"],
"@isa/ui/input-controls": ["libs/ui/input-controls/src/index.ts"],
"@isa/ui/item-rows": ["libs/ui/item-rows/src/index.ts"],
"@isa/ui/label": ["libs/ui/label/src/index.ts"],
"@isa/ui/layout": ["libs/ui/layout/src/index.ts"],
"@isa/ui/menu": ["libs/ui/menu/src/index.ts"],
"@isa/ui/progress-bar": ["libs/ui/progress-bar/src/index.ts"],
"@isa/ui/search-bar": ["libs/ui/search-bar/src/index.ts"],
"@isa/ui/skeleton-loader": ["libs/ui/skeleton-loader/src/index.ts"],
"@isa/ui/toolbar": ["libs/ui/toolbar/src/index.ts"],
"@isa/ui/tooltip": ["libs/ui/tooltip/src/index.ts"],
"@isa/utils/ean-validation": ["libs/utils/ean-validation/src/index.ts"],
"@isa/utils/scroll-position": ["libs/utils/scroll-position/src/index.ts"],
"@isa/utils/z-safe-parse": ["libs/utils/z-safe-parse/src/index.ts"],
"@modal/*": ["apps/isa-app/src/modal/*/index.ts"],
"@page/*": ["apps/isa-app/src/page/*/index.ts"],
"@shared/*": ["apps/isa-app/src/shared/*/index.ts"],
"@shared/components/*": ["apps/isa-app/src/shared/components/*/index.ts"],
"@shared/directives/*": ["apps/isa-app/src/shared/directives/*/index.ts"],
"@shared/pipes/*": ["apps/isa-app/src/shared/pipes/*/index.ts"],
"@shared/services/*": ["apps/isa-app/src/shared/services/*/index.ts"],
"@swagger/*": ["apps/isa-app/src/swagger/*/index.ts"],
"@ui/*": ["apps/isa-app/src/ui/*/index.ts"],
"@utils/*": ["apps/isa-app/src/utils/*/index.ts"],
"packageJson": ["package.json"]
},
"skipLibCheck": true
}
}