Merged PR 1950: 5343-Filter-NumberRange

Related work items: #5343
This commit is contained in:
Lorenz Hilpert
2025-09-16 09:54:29 +00:00
committed by Nino Righi
parent 707802ce0d
commit c5d057e3a7
27 changed files with 1117 additions and 361 deletions

185
.github/prompts/plan.prompt.md vendored Normal file
View File

@@ -0,0 +1,185 @@
---
mode: agent
tools: [ 'edit', 'search', 'usages', 'vscodeAPI', 'problems', 'changes', 'fetch', 'githubRepo', 'Nx Mcp Server', 'context7' ]
description: Plan Mode - Research and create a detailed implementation plan before making any changes.
---
# Plan Mode
You are now operating in **Plan Mode** - a research and planning phase that ensures thorough analysis before implementation. Plan mode is **ALWAYS ACTIVE** when using this prompt. You must follow these strict guidelines for every request:
## Phase 1: Research & Analysis (MANDATORY)
### ALLOWED Operations:
- ✅ Read files using Read, Glob, Grep tools
- ✅ Search documentation and codebases
- ✅ Analyze existing patterns and structures
- ✅ Use WebFetch for documentation research
- ✅ List and explore project structure
- ✅ Use Nx/Angular/Context7 MCP tools for workspace analysis
- ✅ Review dependencies and configurations
### FORBIDDEN Operations:
-**NEVER** create, edit, or modify any files
-**NEVER** run commands that change system state
-**NEVER** make commits or push changes
-**NEVER** install packages or modify configurations
-**NEVER** run build/test commands during planning
## Phase 2: Plan Presentation (REQUIRED FORMAT)
After thorough research, present your plan using this exact structure:
```markdown
## 📋 Implementation Plan
### 🎯 Objective
[Clear statement of what will be accomplished]
### 🔍 Research Summary
- **Current State**: [What exists now]
- **Requirements**: [What needs to be built/changed]
- **Constraints**: [Limitations and considerations]
### 📁 Files to be Modified/Created
1. **File**: `path/to/file.ts`
- **Action**: Create/Modify/Delete
- **Purpose**: [Why this file needs changes]
- **Key Changes**: [Specific modifications planned]
2. **File**: `path/to/another-file.ts`
- **Action**: Create/Modify/Delete
- **Purpose**: [Why this file needs changes]
- **Key Changes**: [Specific modifications planned]
### 🏗️ Implementation Steps
1. **Step 1**: [Detailed description]
- Files affected: `file1.ts`, `file2.ts`
- Rationale: [Why this step is necessary]
2. **Step 2**: [Detailed description]
- Files affected: `file3.ts`
- Rationale: [Why this step is necessary]
3. **Step N**: [Continue numbering...]
### ⚠️ Risks & Considerations
- **Risk 1**: [Potential issue and mitigation]
- **Risk 2**: [Potential issue and mitigation]
### 🧪 Testing Strategy
- [How the changes will be tested]
- [Specific test files or approaches]
### 📚 Architecture Decisions
- **Pattern Used**: [Which architectural pattern will be followed]
- **Libraries/Dependencies**: [What will be used and why]
- **Integration Points**: [How this fits with existing code]
### ✅ Success Criteria
- [ ] Criterion 1
- [ ] Criterion 2
- [ ] All tests pass
- [ ] No lint errors
```
## Phase 3: Await Approval
After presenting the plan:
1. **STOP** all implementation activities
2. **WAIT** for explicit user approval
3. **DO NOT** proceed with any file changes
4. **RESPOND** to questions or plan modifications
5. **EXIT PLAN MODE** only when user explicitly says "execute", "implement", "go ahead", "approved", or similar approval language
## Phase 4: Implementation (After Exiting Plan Mode)
Once the user explicitly approves and you exit plan mode:
1. **PLAN MODE IS NOW DISABLED** - you can proceed with normal implementation
2. Use TodoWrite to create implementation todos
3. Follow the plan step-by-step
4. Update todos as you progress
5. Run tests and lint checks as specified
6. Provide progress updates
## Key Behavioral Rules
### Research Thoroughly
- Spend significant time understanding the codebase
- Look for existing patterns to follow
- Identify all dependencies and integration points
- Consider edge cases and error scenarios
### Be Comprehensive
- Plans should be detailed enough for another developer to implement
- Include all necessary file changes
- Consider testing, documentation, and deployment
- Address potential conflicts or breaking changes
### Show Your Work
- Explain reasoning behind architectural decisions
- Reference existing code patterns when applicable
- Cite documentation or best practices
- Provide alternatives when multiple approaches exist
### Safety First
- Never make changes during planning phase
- Always wait for explicit approval
- Flag potentially risky changes
- Suggest incremental implementation when complex
## Example Interactions
### Good Plan Mode Behavior:
```
User: "Add a dark mode toggle to the settings page"
Assistant: I'll research the current theming system and create a comprehensive plan for implementing dark mode.
[Extensive research using Read, Grep, Glob tools]
## 📋 Implementation Plan
[Follows complete format above]
Ready to proceed? Please approve this plan before I begin implementation.
```
### What NOT to do:
```
User: "Add a dark mode toggle"
Assistant: I'll add that right away!
[Immediately starts editing files - WRONG!]
```
## Integration with Existing Copilot Instructions
This plan mode respects all existing project patterns:
- Follows Angular + Nx workspace conventions
- Uses existing import path aliases
- Respects testing strategy (Jest/Vitest)
- Follows NgRx Signals patterns
- Adheres to logging and configuration patterns
- Maintains library conventions and file naming
Remember: **RESEARCH FIRST, PLAN THOROUGHLY, WAIT FOR APPROVAL, THEN IMPLEMENT**

148
CLAUDE.md Normal file
View File

@@ -0,0 +1,148 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## 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.
## Architecture
### Monorepo Structure
- **apps/isa-app**: Main Angular application
- **libs/**: Reusable libraries organized by domain and type
- **core/**: Core utilities (config, logging, storage, tabs)
- **common/**: Shared utilities (data-access, decorators, print)
- **ui/**: UI component libraries (buttons, dialogs, inputs, etc.)
- **shared/**: Shared domain components (filter, scanner, product components)
- **oms/**: Order Management System features and utilities
- **remission/**: Remission/returns management features
- **catalogue/**: Product catalogue functionality
- **utils/**: General utilities (validation, scroll position, parsing)
- **icons/**: Icon library
- **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/`
## Common Development Commands
### Build Commands
```bash
# Build the main application (development)
npx nx build isa-app --configuration=development
# Build for production
npx nx build isa-app --configuration=production
# Serve the application with SSL
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
npx nx run-many -t test --exclude isa-app --skip-cache
# Run a single test file
npx nx run <project-name>:test --testFile=<path-to-test-file> --skip-cache
# Run tests with coverage
npx nx run <project-name>:test --code-coverage --skip-cache
# Run tests in watch mode
npx nx run <project-name>:test --watch
```
### Linting Commands
```bash
# Lint a specific project
npx nx lint <project-name>
# Example: npx nx lint remission-data-access
# Run linting for all projects
npx nx run-many -t lint
```
### Other Useful Commands
```bash
# Generate Swagger API clients
npm run generate:swagger
# Start Storybook
npx nx run isa-app:storybook
# Format code with Prettier
npm run prettier
# List all projects in the workspace
npx nx list
# Show project dependencies graph
npx nx graph
# Run affected tests (based on git changes)
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
### 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
## State Management
- **NgRx**: Store, Effects, Entity, Component Store, Signals
- **RxJS**: For reactive programming patterns
## Styling
- **Tailwind CSS**: Primary styling framework with custom configuration
- **SCSS**: For component-specific styles
- **Custom Tailwind plugins**: For buttons, inputs, menus, typography
## 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
## 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
## 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
## Development Workflow Tips
- Always use `npx nx run` pattern for executing 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`
## 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

View File

@@ -0,0 +1,59 @@
import {
argsToTemplate,
moduleMetadata,
type Meta,
type StoryObj,
} from '@storybook/angular';
import {
InlineInputComponent,
InputControlDirective,
} from '@isa/ui/input-controls';
const meta: Meta<InlineInputComponent> = {
component: InlineInputComponent,
title: 'ui/input-controls/InlineInput',
argTypes: {
size: { control: 'select', options: ['small', 'medium'] },
},
args: {
size: 'medium',
},
decorators: [
moduleMetadata({
imports: [InputControlDirective],
}),
],
render: (args) => ({
props: args,
template: `
<ui-inline-input ${argsToTemplate(args)}>
<input type="text" placeholder="Enter inline text" />
</ui-inline-input>
`,
}),
};
export default meta;
type Story = StoryObj<InlineInputComponent>;
export const Primary: Story = {
args: {
size: 'medium',
},
};
export const WithLabel: Story = {
args: {
size: 'medium',
},
render: (args) => ({
props: args,
template: `
<ui-inline-input ${argsToTemplate(args)}>
<label>Label</label>
<input type="text" placeholder="Enter inline text" />
</ui-inline-input>
`,
}),
};

View File

@@ -1,5 +1,5 @@
{
"name": "data-access",
"name": "checkout-data-access",
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "libs/checkout/data-access/src",
"prefix": "lib",

View File

@@ -1,70 +1,70 @@
<reward-start-card></reward-start-card>
<!-- <filter-controls-panel (triggerSearch)="search($event)"></filter-controls-panel> -->
<span
class="text-isa-neutral-900 isa-text-body-2-regular self-start"
data-what="result-count"
>
<!-- {{ hits() }} Einträge -->
XXX Einträge
</span>
<div class="flex flex-col gap-4 w-full items-center justify-center mb-24">
<!-- @if (hits()) {
<!-- @for (item of items(); track item.id) {
@defer (on viewport) {
<reward-catalog-item
#listElement
[item]="item"
[stock]="getStockForItem(item)"
[stockFetching]="inStockFetching()"
[productGroupValue]="getProductGroupValueForItem(item)"
(inProgressChange)="onListItemActionInProgress($event)"
></reward-catalog-item>
} @placeholder {
<div class="h-[7.75rem] w-full flex items-center justify-center">
<ui-icon-button
[pending]="true"
[color]="'tertiary'"
data-what="load-spinner"
data-which="item-placeholder"
></ui-icon-button>
</div>
}
}
} @else {
<ui-empty-state
class="w-full justify-self-center"
title="Keine Suchergebnisse"
description="Bitte prüfen Sie die Schreibweise oder ändern Sie die Filtereinstellungen."
>
</ui-empty-state>
} -->
<ui-empty-state
class="w-full justify-self-center"
title="Keine Suchergebnisse"
description="Bitte prüfen Sie die Schreibweise oder ändern Sie die Filtereinstellungen."
>
</ui-empty-state>
</div>
<!--
<ui-stateful-button
class="fixed right-6 bottom-6"
(clicked)="remitItems()"
(action)="remitItems()"
[(state)]="remitItemsState"
defaultContent="Prämie auswählen"
defaultWidth="13rem"
[errorContent]="remitItemsError()"
errorWidth="32rem"
errorAction="Erneut versuchen"
successContent="Hinzugefügt"
successWidth="20rem"
size="large"
color="brand"
[pending]="remitItemsInProgress()"
[disabled]="!hasSelectedItems() || listItemActionInProgress()"
>
</ui-stateful-button>} -->
<reward-start-card></reward-start-card>
<!-- <filter-controls-panel (triggerSearch)="search($event)"></filter-controls-panel> -->
<span
class="text-isa-neutral-900 isa-text-body-2-regular self-start"
data-what="result-count"
>
<!-- {{ hits() }} Einträge -->
XXX Einträge
</span>
<div class="flex flex-col gap-4 w-full items-center justify-center mb-24">
<!-- @if (hits()) {
<!-- @for (item of items(); track item.id) {
@defer (on viewport) {
<reward-catalog-item
#listElement
[item]="item"
[stock]="getStockForItem(item)"
[stockFetching]="inStockFetching()"
[productGroupValue]="getProductGroupValueForItem(item)"
(inProgressChange)="onListItemActionInProgress($event)"
></reward-catalog-item>
} @placeholder {
<div class="h-[7.75rem] w-full flex items-center justify-center">
<ui-icon-button
[pending]="true"
[color]="'tertiary'"
data-what="load-spinner"
data-which="item-placeholder"
></ui-icon-button>
</div>
}
}
} @else {
<ui-empty-state
class="w-full justify-self-center"
title="Keine Suchergebnisse"
description="Bitte prüfen Sie die Schreibweise oder ändern Sie die Filtereinstellungen."
>
</ui-empty-state>
} -->
<ui-empty-state
class="w-full justify-self-center"
title="Keine Suchergebnisse"
description="Bitte prüfen Sie die Schreibweise oder ändern Sie die Filtereinstellungen."
>
</ui-empty-state>
</div>
<!--
<ui-stateful-button
class="fixed right-6 bottom-6"
(clicked)="remitItems()"
(action)="remitItems()"
[(state)]="remitItemsState"
defaultContent="Prämie auswählen"
defaultWidth="13rem"
[errorContent]="remitItemsError()"
errorWidth="32rem"
errorAction="Erneut versuchen"
successContent="Hinzugefügt"
successWidth="20rem"
size="large"
color="brand"
[pending]="remitItemsInProgress()"
[disabled]="!hasSelectedItems() || listItemActionInProgress()"
>
</ui-stateful-button>} -->

View File

@@ -193,19 +193,18 @@ export class FilterService {
return input;
}
const isParent =
const hasChildren =
Array.isArray(checkboxOption.values) &&
checkboxOption.values.length > 0;
let keys: string[] = [];
if (isParent) {
// If the option has children, we need to include all child keys
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
keys = checkboxOption.values!.map((v) =>
checkboxOptionKeysHelper(v).join('.'),
// Collect the keys we want to add/remove. When the option has children we only include the
// children's keys; otherwise we include the option's own key. Avoids non-null assertion.
let keys: string[];
if (hasChildren) {
const children = checkboxOption.values as CheckboxFilterInputOption[]; // safe due to hasChildren
keys = children.map((child) =>
checkboxOptionKeysHelper(child).join('.'),
);
} else {
// If no children, just use the current option key
keys = [optionKeys.join('.')];
}
@@ -361,6 +360,45 @@ export class FilterService {
}
}
/**
* Sets the number range values for an input with the specified key.
*
* This updates the current minimum (min) and maximum (max) values for a
* NumberRange filter input. Passing `undefined` for either side will clear that
* boundary while leaving the other intact.
*
* @param key - The key of the number range input to update
* @param min - The minimum numeric value (inclusive) or undefined to clear
* @param max - The maximum numeric value (inclusive) or undefined to clear
* @param options - Optional parameters
* @param options.commit - If true, commits the changes immediately
*/
setInputNumberRangeValue(
key: string,
min?: number,
max?: number,
options?: { commit: boolean },
): void {
const inputs = this.#state.inputs().map((input) => {
if (input.key !== key) {
return input;
}
if (input.type === InputType.NumberRange) {
return { ...input, min, max };
}
this.logUnsupportedInputType(input, 'setInputNumberRangeValue');
return input;
});
patchState(this.#state, { inputs });
if (options?.commit) {
this.commit();
}
}
/**
* Helper method to consistently log unsupported input type warnings
* @private
@@ -809,6 +847,17 @@ export class FilterService {
result[input.key] = '';
}
break;
case InputType.NumberRange: {
const { min: minVal, max: maxVal } = input;
if (minVal != null && maxVal != null) {
result[input.key] = `"${minVal}-${maxVal}"`;
} else if (minVal != null) {
result[input.key] = `"${minVal}-"`;
} else if (maxVal != null) {
result[input.key] = `"-${maxVal}"`;
}
break;
}
}
}
@@ -927,6 +976,18 @@ export class FilterService {
this.setInputDateRangeValue(key, start, stop);
break;
}
case InputType.NumberRange: {
const decoded = decodeURIComponent(params[key]);
const [minRaw, maxRaw] = decoded.split('-"');
const min = minRaw
? Number(minRaw.replace(/"-/g, '').replace(/"/g, ''))
: undefined;
const max = maxRaw
? Number(maxRaw.replace(/"-/g, '').replace(/"/g, ''))
: undefined;
this.setInputNumberRangeValue(key, min, max);
break;
}
default:
this.logUnsupportedInputType(input, 'parseQueryParams');
break;

View File

@@ -50,6 +50,15 @@ export function mapFilterInputToRecord(
if (start || stop) {
acc[input.key] = `${start}-${stop}`;
}
} else if (input.type === InputType.NumberRange) {
const { min, max } = input;
if (min && max) {
acc[input.key] = `"${min}-${max}"`;
} else if (min) {
acc[input.key] = `"${min}-"`;
} else if (max) {
acc[input.key] = `"-${max}"`;
}
}
return acc;
}, {});

View File

@@ -119,22 +119,4 @@ describe('filterInputMapping', () => {
mapped: 'dateRange',
});
});
it('should throw error for unknown input type', () => {
// Arrange
const group = 'testGroup';
const input: Input = {
key: 'unknownInput',
label: 'Unknown Input',
type: 999 as unknown as InputType, // Invalid input type
};
// Act & Assert
expect(() => filterInputMapping(group, input)).toThrow(
'Unknown input type: 999',
);
expect(mockTextFilterInputMapping).not.toHaveBeenCalled();
expect(mockCheckboxFilterInputMapping).not.toHaveBeenCalled();
expect(mockDateRangeFilterInputMapping).not.toHaveBeenCalled();
});
});

View File

@@ -1,29 +1,32 @@
import { Input, InputType } from '../../types';
import { FilterInput } from '../schemas';
import { checkboxFilterInputMapping } from './checkbox-filter-input.mapping';
import { dateRangeFilterInputMapping } from './date-range-filter-input.mapping';
import { textFilterInputMapping } from './text-filter-input.mapping';
/**
* Maps an Input object to the appropriate FilterInput type based on its input type
*
* This function serves as a router that delegates to the specific mapping function
* based on the input type (Text, Checkbox, DateRange). It ensures that each input
* is converted to its corresponding strongly-typed filter input object.
*
* @param group - The group identifier that this input belongs to
* @param input - The source input object to map
* @returns A validated FilterInput object of the appropriate subtype
* @throws Error if the input type is not supported
*/
export function filterInputMapping(group: string, input: Input): FilterInput {
switch (input.type) {
case InputType.Text:
return textFilterInputMapping(group, input);
case InputType.Checkbox:
return checkboxFilterInputMapping(group, input);
case InputType.DateRange:
return dateRangeFilterInputMapping(group, input);
}
throw new Error(`Unknown input type: ${input.type}`);
}
import { Input, InputType } from '../../types';
import { FilterInput } from '../schemas';
import { checkboxFilterInputMapping } from './checkbox-filter-input.mapping';
import { dateRangeFilterInputMapping } from './date-range-filter-input.mapping';
import { numberRangeFilterInputMapping } from './number-range-filter-input.mapping';
import { textFilterInputMapping } from './text-filter-input.mapping';
/**
* Maps an Input object to the appropriate FilterInput type based on its input type
*
* This function serves as a router that delegates to the specific mapping function
* based on the input type (Text, Checkbox, DateRange). It ensures that each input
* is converted to its corresponding strongly-typed filter input object.
*
* @param group - The group identifier that this input belongs to
* @param input - The source input object to map
* @returns A validated FilterInput object of the appropriate subtype
* @throws Error if the input type is not supported
*/
export function filterInputMapping(group: string, input: Input): FilterInput {
switch (input.type) {
case InputType.Text:
return textFilterInputMapping(group, input);
case InputType.Checkbox:
return checkboxFilterInputMapping(group, input);
case InputType.DateRange:
return dateRangeFilterInputMapping(group, input);
case InputType.NumberRange:
return numberRangeFilterInputMapping(group, input);
}
console.warn('filterInputMapping: Unknown input type', input);
}

View File

@@ -31,8 +31,14 @@ export function filterMapping(settings: QuerySettings): Filter {
for (const group of groups) {
filter.groups.push(filterGroupMapping(group));
for (const input of group.input) {
filter.inputs.push(filterInputMapping(group.group, input));
if (Array.isArray(group.input)) {
for (const input of group.input) {
const inputs = filterInputMapping(group.group, input);
if (inputs === undefined) {
continue;
}
filter.inputs.push(inputs);
}
}
}

View File

@@ -0,0 +1,32 @@
import { Input, InputType } from '../../types';
import {
NumberRangeFilterInput,
NumberRangeFilterInputSchema,
} from '../schemas';
export function numberRangeFilterInputMapping(
group: string,
input: Input,
): NumberRangeFilterInput {
return NumberRangeFilterInputSchema.parse({
group,
key: input.key,
label: input.label,
description: input.description,
type: InputType.NumberRange,
min: input.options?.values?.[0]?.value
? Number(input.options?.values?.[0]?.value)
: undefined,
max: input.options?.values?.[1]?.value
? Number(input.options?.values?.[1]?.value)
: undefined,
minValue: input.options?.values?.[0]?.minValue
? Number(input.options?.values?.[0]?.minValue.replace('-', ''))
: undefined,
maxValue: input.options?.values?.[0]?.maxValue
? Number(input.options?.values?.[0]?.maxValue.replace('-', ''))
: undefined,
minLabel: input.options?.values?.[0]?.label || 'Min',
maxLabel: input.options?.values?.[1]?.label || 'Max',
});
}

View File

@@ -1,21 +1,23 @@
import { z } from 'zod';
import { CheckboxFilterInputSchema } from './checkbox-filter-input.schema';
import { DateRangeFilterInputSchema } from './date-range-filter-input.schema';
import { TextFilterInputSchema } from './text-filter-input.schema';
/**
* A union schema representing all possible filter input types in the system.
* This schema allows for type discrimination based on the `type` property.
*
* Supported filter input types:
* - TextFilterInput: Simple text input fields
* - CheckboxFilterInput: Multiple-choice checkbox selections
* - DateRangeFilterInput: Date range selectors for time-based filtering
*/
export const FilterInputSchema = z.union([
TextFilterInputSchema,
CheckboxFilterInputSchema,
DateRangeFilterInputSchema,
]);
export type FilterInput = z.infer<typeof FilterInputSchema>;
import { z } from 'zod';
import { CheckboxFilterInputSchema } from './checkbox-filter-input.schema';
import { DateRangeFilterInputSchema } from './date-range-filter-input.schema';
import { TextFilterInputSchema } from './text-filter-input.schema';
import { NumberRangeFilterInputSchema } from './number-range-filter-input.schema';
/**
* A union schema representing all possible filter input types in the system.
* This schema allows for type discrimination based on the `type` property.
*
* Supported filter input types:
* - TextFilterInput: Simple text input fields
* - CheckboxFilterInput: Multiple-choice checkbox selections
* - DateRangeFilterInput: Date range selectors for time-based filtering
*/
export const FilterInputSchema = z.union([
TextFilterInputSchema,
CheckboxFilterInputSchema,
DateRangeFilterInputSchema,
NumberRangeFilterInputSchema,
]);
export type FilterInput = z.infer<typeof FilterInputSchema>;

View File

@@ -1,12 +1,13 @@
export * from './base-filter-input.schema';
export * from './checkbox-filter-input-option.schema';
export * from './checkbox-filter-input.schema';
export * from './date-range-filter-input.schema';
export * from './filter-group.schema';
export * from './filter-input.schema';
export * from './filter.schema';
export * from './order-by-direction.schema';
export * from './order-by-option.schema';
export * from './query-order.schema';
export * from './query.schema';
export * from './text-filter-input.schema';
export * from './base-filter-input.schema';
export * from './checkbox-filter-input-option.schema';
export * from './checkbox-filter-input.schema';
export * from './date-range-filter-input.schema';
export * from './filter-group.schema';
export * from './filter-input.schema';
export * from './filter.schema';
export * from './number-range-filter-input.schema';
export * from './order-by-direction.schema';
export * from './order-by-option.schema';
export * from './query-order.schema';
export * from './query.schema';
export * from './text-filter-input.schema';

View File

@@ -0,0 +1,31 @@
import { z } from 'zod';
import { BaseFilterInputSchema } from './base-filter-input.schema';
import { InputType } from '../../types';
export const NumberRangeFilterInputSchema = BaseFilterInputSchema.extend({
type: z
.literal(InputType.NumberRange)
.describe(
'Specifies this as a number range input type. Must be InputType.NumberRange.',
),
min: z.number().optional().describe('Current minimum value of the range.'),
max: z.number().optional().describe('Current maximum value of the range.'),
minValue: z
.number()
.optional()
.describe(
'Minimum value of the number range. Optional if only a maximum is needed.',
),
maxValue: z
.number()
.optional()
.describe(
'Maximum value of the number range. Optional if only a minimum is needed.',
),
minLabel: z.string().describe('Label for the minimum input field.'),
maxLabel: z.string().describe('Label for the maximum input field.'),
}).describe('NumberRangeFilterInput');
export type NumberRangeFilterInput = z.infer<
typeof NumberRangeFilterInputSchema
>;

View File

@@ -1,15 +1,19 @@
@switch (filterInput().type) {
@case (InputType.Checkbox) {
<filter-checkbox-input [inputKey]="filterInput().key">
</filter-checkbox-input>
}
@case (InputType.DateRange) {
<filter-datepicker-range-input [inputKey]="filterInput().key">
</filter-datepicker-range-input>
}
@default {
<div class="text-isa-accent-red isa-text-body-1-bold">
Fehler: Kein Template für diesen Typ gefunden! {{ filterInput().type }}
</div>
}
}
@switch (filterInput().type) {
@case (InputType.Checkbox) {
<filter-checkbox-input [inputKey]="filterInput().key">
</filter-checkbox-input>
}
@case (InputType.DateRange) {
<filter-datepicker-range-input [inputKey]="filterInput().key">
</filter-datepicker-range-input>
}
@case (InputType.NumberRange) {
<filter-number-range-input [inputKey]="filterInput().key">
</filter-number-range-input>
}
@default {
<div class="text-isa-accent-red isa-text-body-1-bold">
Fehler: Kein Template für diesen Typ gefunden! {{ filterInput().type }}
</div>
}
}

View File

@@ -1,27 +1,32 @@
import {
ChangeDetectionStrategy,
Component,
input,
ViewEncapsulation,
} from '@angular/core';
import { CheckboxInputComponent } from '../checkbox-input';
import { DatepickerRangeInputComponent } from '../datepicker-range-input';
import { FilterInput } from '../../core';
import { InputType } from '../../types';
@Component({
selector: 'filter-input-renderer',
templateUrl: 'input-renderer.component.html',
styleUrls: ['./input-renderer.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
standalone: true,
imports: [CheckboxInputComponent, DatepickerRangeInputComponent],
host: {
'[class]': "['filter-input-renderer']",
},
})
export class InputRendererComponent {
filterInput = input.required<FilterInput>();
InputType = InputType;
}
import {
ChangeDetectionStrategy,
Component,
input,
ViewEncapsulation,
} from '@angular/core';
import { CheckboxInputComponent } from '../checkbox-input';
import { DatepickerRangeInputComponent } from '../datepicker-range-input';
import { NumberRangeInputComponent } from '../number-range-input';
import { FilterInput } from '../../core';
import { InputType } from '../../types';
@Component({
selector: 'filter-input-renderer',
templateUrl: 'input-renderer.component.html',
styleUrls: ['./input-renderer.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
standalone: true,
imports: [
CheckboxInputComponent,
DatepickerRangeInputComponent,
NumberRangeInputComponent,
],
host: {
'[class]': "['filter-input-renderer']",
},
})
export class InputRendererComponent {
filterInput = input.required<FilterInput>();
InputType = InputType;
}

View File

@@ -0,0 +1 @@
export * from './number-range-input.component';

View File

@@ -0,0 +1,43 @@
@let inp = input();
<ui-inline-input>
<label>{{ inp.minLabel }}</label>
<input
type="number"
uiInputControl
[(ngModel)]="minValue"
[min]="minInputLowerLimit()"
[max]="minInputUpperLimit()"
#minModel="ngModel"
/>
@if (minModel.invalid && minModel.touched) {
<div class="text-isa-accent-red isa-text-caption-regular">
@if (minModel.errors?.['min']) {
Mindestens {{ minInputLowerLimit() }}.
}
@if (minModel.errors?.['max']) {
Maximal {{ minInputUpperLimit() }}.
}
</div>
}
</ui-inline-input>
<ui-inline-input>
<label>{{ inp.maxLabel }}</label>
<input
type="number"
uiInputControl
[(ngModel)]="maxValue"
[min]="maxInputLowerLimit()"
[max]="maxInputUpperLimit()"
#maxModel="ngModel"
/>
@if (minModel.invalid && minModel.touched) {
<div class="text-isa-accent-red isa-text-caption-regular">
@if (minModel.errors?.['min']) {
Mindestens {{ minInputLowerLimit() }}.
}
@if (minModel.errors?.['max']) {
Maximal {{ minInputUpperLimit() }}.
}
</div>
}
</ui-inline-input>

View File

@@ -0,0 +1,7 @@
:host {
@apply grid grid-cols-2 gap-2 p-5;
}
.ui-input-control {
@apply w-full;
}

View File

@@ -0,0 +1,87 @@
import {
ChangeDetectionStrategy,
Component,
computed,
effect,
inject,
input,
linkedSignal,
} from '@angular/core';
import { FilterService, NumberRangeFilterInput } from '../../core';
import { InputType } from '../../types';
import {
InputControlDirective,
InlineInputComponent,
} from '@isa/ui/input-controls';
import { logger } from '@isa/core/logging';
import { FormsModule } from '@angular/forms';
@Component({
selector: 'filter-number-range-input',
templateUrl: './number-range-input.component.html',
styleUrls: ['./number-range-input.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [InlineInputComponent, InputControlDirective, FormsModule],
host: { '[class]': "['filter-number-range-input']" },
})
export class NumberRangeInputComponent {
#logger = logger(() => ({ component: 'NumberRangeInputComponent' }));
#filterService = inject(FilterService);
inputKey = input.required<string>();
input = computed<NumberRangeFilterInput>(() => {
const inputs = this.#filterService.inputs();
const input = inputs.find(
(input) =>
input.key === this.inputKey() && input.type === InputType.NumberRange,
) as NumberRangeFilterInput;
if (!input) {
const err = new Error(`Input not found for key: ${this.inputKey()}`);
this.#logger.error('Input not found', { error: err });
}
return input;
});
minValue = linkedSignal(() => this.input().min);
// Overall domain bounds coming from the filter input definition
overallMinLimit = computed(() => this.input().minValue ?? 0);
overallMaxLimit = computed(() => this.input().maxValue ?? Infinity);
// Allowed bounds for the minimum value input
minInputLowerLimit = computed(() => this.overallMinLimit());
minInputUpperLimit = computed(() =>
Math.min(this.maxValue() ?? Infinity, this.overallMaxLimit()),
);
maxValue = linkedSignal(() => this.input().max);
// Allowed bounds for the maximum value input
maxInputLowerLimit = computed(() =>
Math.max(this.minValue() ?? 0, this.overallMinLimit()),
);
maxInputUpperLimit = computed(() => this.overallMaxLimit());
constructor() {
effect(() => {
const minValue = this.minValue();
const maxValue = this.maxValue();
const currentMin = this.input().min;
const currentMax = this.input().max;
if (minValue === currentMin && maxValue === currentMax) {
return;
}
this.#filterService.setInputNumberRangeValue(
this.inputKey(),
minValue,
maxValue,
);
});
}
}

View File

@@ -2,13 +2,14 @@ export enum InputType {
Text = 1,
Checkbox = 2,
DateRange = 128,
NumberRange = 4096,
}
/**
* Enumeration of search trigger types that indicate how a search was initiated.
* Used throughout the filter system to track user interaction patterns and
* optimize search behavior based on the trigger source.
*
*
* @example
* ```typescript
* // Handle different search triggers

View File

@@ -1,16 +1,17 @@
export * from './lib/checkbox/checkbox-label.directive';
export * from './lib/checkbox/checkbox.component';
export * from './lib/checkbox/checklist-value.directive';
export * from './lib/checkbox/checklist.component';
export * from './lib/core/input-control.directive';
export * from './lib/dropdown/dropdown.component';
export * from './lib/dropdown/dropdown.types';
export * from './lib/listbox/listbox-item.directive';
export * from './lib/listbox/listbox.directive';
export * from './lib/text-field/textarea.component';
export * from './lib/text-field/text-field.component';
export * from './lib/text-field/text-field-clear.component';
export * from './lib/text-field/text-field-container.component';
export * from './lib/text-field/text-field-errors.component';
export * from './lib/chips/chips.component';
export * from './lib/chips/chip-option.component';
export * from './lib/checkbox/checkbox-label.directive';
export * from './lib/checkbox/checkbox.component';
export * from './lib/checkbox/checklist-value.directive';
export * from './lib/checkbox/checklist.component';
export * from './lib/core/input-control.directive';
export * from './lib/dropdown/dropdown.component';
export * from './lib/dropdown/dropdown.types';
export * from './lib/listbox/listbox-item.directive';
export * from './lib/listbox/listbox.directive';
export * from './lib/inline-input/inline-input.component';
export * from './lib/text-field/textarea.component';
export * from './lib/text-field/text-field.component';
export * from './lib/text-field/text-field-clear.component';
export * from './lib/text-field/text-field-container.component';
export * from './lib/text-field/text-field-errors.component';
export * from './lib/chips/chips.component';
export * from './lib/chips/chip-option.component';

View File

@@ -2,6 +2,7 @@
@use "./lib/checkbox/checklist";
@use "./lib/chips/chips";
@use "./lib/dropdown/dropdown";
@use "./lib/inline-input/inline-input";
@use "./lib/listbox/listbox";
@use "./lib/text-field/text-field";
@use "./lib/text-field/text-field-clear";

View File

@@ -0,0 +1,47 @@
.ui-inline-input {
display: inline-flex;
padding: 0.5rem 0;
flex-direction: column;
justify-content: center;
align-items: flex-start;
gap: 0.25rem;
label {
@apply text-isa-neutral-900;
font-family: "Open Sans";
font-size: 0.5rem;
font-style: normal;
font-weight: 700;
line-height: 0.75rem; /* 150% */
text-transform: uppercase;
}
&.ui-inline-input--small input {
@apply isa-text-body-2-bold;
}
&.ui-inline-input--medium input {
@apply isa-text-body-1-bold;
}
input {
@apply outline-none font-bold text-isa-neutral-900;
&::placeholder {
@apply text-isa-neutral-500 font-normal;
&:hover {
@apply font-normal;
}
&:focus {
@apply text-isa-neutral-600 font-bold;
}
}
&.ng-invalid {
@apply text-isa-accent-red;
}
}
}

View File

@@ -0,0 +1 @@
<ng-content></ng-content>

View File

@@ -0,0 +1,39 @@
import {
ChangeDetectionStrategy,
Component,
computed,
input,
contentChild,
} from '@angular/core';
import { InputControlDirective } from '../core/input-control.directive';
export const InlineInputSize = {
Small: 'small',
Medium: 'medium',
} as const;
export type InlineInputSize =
(typeof InlineInputSize)[keyof typeof InlineInputSize];
@Component({
selector: 'ui-inline-input',
templateUrl: './inline-input.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
host: { '[class]': "['ui-inline-input', sizeClass()]" },
})
export class InlineInputComponent {
size = input<InlineInputSize>(InlineInputSize.Medium);
sizeClass = computed(() => {
const size = this.size();
switch (size) {
case InlineInputSize.Small:
return 'ui-inline-input--small';
case InlineInputSize.Medium:
default:
return 'ui-inline-input--medium';
}
});
inputControl = contentChild.required(InputControlDirective);
}

View File

@@ -1,140 +1,140 @@
{
"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/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
}
}