Compare commits

..

1 Commits

Author SHA1 Message Date
Lorenz Hilpert
445817c22f hotfix(checkout): Chaning the Quantity in the cart used a wrink branch for "Rücklage"
- When chaning the quantity, the current destination branch will be used to update the quantity in the cart
2025-04-09 12:05:15 +02:00
3216 changed files with 15749 additions and 65137 deletions

View File

@@ -1,86 +0,0 @@
# Commit Message Instructions (Conventional Commits)
Commit messages should follow the [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/). This provides a standardized format for commit messages, making it easier to understand changes, automate changelog generation, and trigger build/publish processes.
## Format
The commit message structure is as follows:
```
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
```
---
### Components
1. **Type**: Indicates the kind of change introduced by the commit. Must be one of the allowed types (see below).
2. **Scope (Optional)**: A noun describing the section of the codebase affected by the change (e.g., `auth`, `ui`, `build`). Enclosed in parentheses.
3. **Description**: A concise summary of the change in the imperative, present tense (e.g., "add login feature", not "added login feature" or "adds login feature"). Starts with a lowercase letter and should not end with a period. Max 72 characters recommended for the entire header line (`<type>[optional scope]: <description>`).
4. **Body (Optional)**: A more detailed explanation of the changes. Use the imperative, present tense. Explain the _what_ and _why_ vs. _how_. Separate from the description by a blank line. Wrap lines at 72 characters.
5. **Footer(s) (Optional)**: Contains additional metadata. Common footers include:
- `BREAKING CHANGE:` followed by a description of the breaking change. A `!` can also be appended to the type/scope (`feat!:`) to indicate a breaking change.
- Issue references (e.g., `Refs: #123`, `Closes: #456`). Separate from the body by a blank line.
---
### Allowed Types
- **feat**: A new feature for the user.
- **fix**: A bug fix for the user.
- **build**: Changes that affect the build system or external dependencies (e.g., gulp, broccoli, npm).
- **chore**: Other changes that don't modify src or test files (e.g., updating dependencies, build tasks).
- **ci**: Changes to CI configuration files and scripts (e.g., Travis, Circle, BrowserStack, SauceLabs).
- **docs**: Documentation only changes.
- **perf**: A code change that improves performance.
- **refactor**: A code change that neither fixes a bug nor adds a feature.
- **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc).
- **test**: Adding missing tests or correcting existing tests.
---
### Examples
**Commit with description only:**
```
fix: correct minor typos in code
```
**Commit with scope:**
```
feat(lang): add polish language
```
**Commit with body and breaking change footer:**
```
refactor: drop support for Node 6
The new implementation relies on async/await and other features
introduced in Node 8+.
BREAKING CHANGE: refactor to use JavaScript features not available in Node 6.
```
**Commit with scope, body, and issue footer:**
```
docs(readme): improve installation instructions
Provide clearer steps for setting up the development environment.
Add links to prerequisite tools.
Closes: #12
```
**Commit with `!` for breaking change:**
```
feat(api)!: send an email to the customer when a product is shipped
```

View File

@@ -1,21 +0,0 @@
# Mentor Instructions
## Introduction
You are Mentor, an AI assistant focused on ensuring code quality, strict adherence to best practices, and development efficiency. **Your core function is to enforce the coding standards and guidelines established in this workspace.** Your goal is to help me produce professional, maintainable, and high-performing code.
## Tone and Personality
Maintain a professional, objective, and direct tone consistently:
- **Guideline Enforcement & Error Correction:** When code deviates from guidelines or contains errors, provide precise, technical feedback. Clearly state the issue, cite the relevant guideline or principle, and explain the required correction for optimal, maintainable results.
- **Technical Consultation:** In discussions about architecture, best practices, or complex coding inquiries, remain formal and analytical. Provide clear, well-reasoned explanations and recommendations grounded in industry standards and the project's specific guidelines.
## Behavioral Guidelines
- **Actionable Feedback:** Prioritize constructive, actionable feedback aimed at improving code quality, maintainability, and adherence to standards. Avoid rewriting code; focus on explaining the necessary changes and their rationale based on guidelines.
- **Strict Guideline Adherence:** Base _all_ feedback, suggestions, and explanations rigorously on the guidelines documented within this workspace. Cite specific rules and principles consistently.
- **Demand Clarity:** If a query or code snippet lacks sufficient detail for a thorough, professional analysis, request clarification.
- **Professional Framing:** Frame all feedback objectively, focusing on the technical aspects and the importance of meeting project standards for long-term success.
- **Context-Specific Expertise:** Provide specific, context-aware advice tailored to the code or problem, always within the framework of our established guidelines.
- **Enforce Standards:** Actively enforce project preferences for Type safety, Clean Code principles, and thorough documentation, as mandated by the workspace guidelines.

View File

@@ -1,182 +0,0 @@
# Code Review Instructions
## Summary
When conducting a code review, follow these steps to ensure a thorough and constructive process.
**Ensure that all review guidelines are followed. If any guideline is not adhered to, make it explicitly clear which guideline needs to be followed.**
## Review Process
1. 🎯 **Key Issues**
Identify critical issues in the code such as bugs, security vulnerabilities, or violations of the project's coding standards.
_Include specific links to files and line numbers (e.g., file.js#L10) where applicable._
2. 💡 **Suggestions for Improvement**
Highlight areas where the code can be enhanced in terms of readability, performance, maintainability, or adherence to best practices.
_Clarify what constitutes a "Critical" versus a "Minor" issue to avoid ambiguity._
3.**Code Examples**
Provide specific, concise code snippets that illustrate your suggestions.
_Include both a "Before" (problematic code) and an "After" (improved version) example where beneficial._
4. 📚 **Relevant Documentation Links**
Attach links to useful resources or official documentation to support the suggested changes.
_For example, link to ESLint, Jest, or Angular Style Guide pages when relevant._
## Tone and Feedback
- Be constructive and supportive.
Frame suggestions as opportunities for growth rather than criticism.
- Use the following emojis to categorize your feedback:
- 🚨 **Critical issues**
-**Minor Issues**
- ⚠️ **Warnings**
- 💡 **Suggestions**
-**Good practices**
## Additional Informations
- Missing tests and JSDocs are minor issues
- Missing unit test are minor issues
- Missing End-to-End (E2E) Testing Attributes (`data-what`, `data-which`) are warnings
### Review Template
````markdown
# Code Review
## Summary
A brief overview of the codes overall quality, highlighting key strengths and areas needing attention. This sets the stage for the detailed feedback below.
---
## 🚨 Critical Issues
High-priority issues that must be addressed immediately due to their potential to severely impact functionality, performance, or security.
### 1. High Priority: [Issue Title]
#### 🚨 Issue
Describe the issue clearly, including links to specific files and lines (e.g., file.js#L10). Explain why its critical—highlight crashes, security risks, or significant performance issues.
#### 💡 Suggestions for Improvement
Provide specific steps or alternative approaches to resolve the issue.
#### ✨ Code Example
**Current**: [file](file.js#L10) Problematic code with path to the file and line of the code
```typescript
// Code...
```
**Improvement**: Improved version
```typescript
// Code...
```
#### 📚 Relevant Documentation
Include URLs for further research (e.g., [Jest Documentation](https://jestjs.io/docs/getting-started)).
---
## ❗ Minor Issues
Issues that can improve code quality, maintainability, or adherence to best practices when resolved.
### 1. Medium Priority: [Issue Title]
#### ❗ Issue
Describe the issue clearly, including file and line references (e.g., file.js#L10). Explain the impact on the project.
#### 💡 Suggestions for Improvement
Offer concrete steps or alternative approaches to mitigate the issue.
#### ✨ Code Example
**Current**: [file](file.js#L10) Problematic code with path to the file and line of the code
```typescript
// Code...
```
**Improvement**: Improved version
```typescript
// Code...
```
#### 📚 Relevant Documentation
Provide links to further resources.
---
## ⚠️ Warnings
Low-priority issues or suggestions that could help prevent future problems or improve the code quality over time.
### 1. Low Priority: [Issue Title]
#### ⚠️ Issue
Describe the issue clearly with references (e.g., file.js#L10). Explain the potential impact if left unaddressed.
#### 💡 Suggestions for Improvement
Provide suggestions or alternative implementations to mitigate the issue.
#### ✨ Code Example
**Current**: [file](file.js#L10) Problematic code with path to the file and line of the code
```typescript
// Code...
```
**Improvement**: Improved version
```typescript
// Code...
```
#### 📚 Relevant Documentation
Include relevant resources for more information.
---
## 🛑 Bad Practices
Highlight up to five bad aspects of the code to reinforce improvements and encourage good practices. Use different funny emoji at the beginning of each bad practice.
- Emoji **Bad Practice 1**:
Describe a specific weakness (e.g., clear code structure) with an example reference (e.g., file.js#L20). Explain why its bad.
- Emoji **Bad Practice 2**:
Outline another negative feature (e.g., effective error handling) with a snippet reference.
---
## ✅ Good Practices
Highlight up to five positive aspects of the code to reinforce well-implemented patterns and encourage good practices. Use different funny emoji at the beginning of each good practice.
- Emoji **Good Practice 1**:
Describe a specific strength (e.g., clear code structure) with an example reference (e.g., file.js#L20). Explain why its commendable.
- Emoji **Good Practice 2**:
Outline another positive feature (e.g., effective error handling) with a snippet reference.
---
## 📓 Additional Notes
- **General Feedback**: Optional thoughts regarding the overall quality or potential areas for future improvement.
- **Next Steps**: Outline follow-up actions or further examination areas as needed.
````

View File

@@ -1,66 +0,0 @@
# Testing Instructions
## Framework and Tools
- Use **Jest** as the testing framework.
- For unit tests, utilize **Spectator** to simplify Angular component testing.
## Guidelines
1. **Error Case Testing**: Ensure all edge cases and error scenarios are thoroughly tested.
2. **Arrange-Act-Assert Pattern**: Follow the Arrange-Act-Assert pattern for structuring your tests:
- **Arrange**: Set up the testing environment and initialize required variables.
- **Act**: Execute the functionality being tested.
- **Assert**: Verify the expected outcomes.
## Best Practices
- Write clear and descriptive test names.
- Ensure tests are isolated and do not depend on each other.
- Mock external dependencies to avoid side effects.
- Aim for high code coverage without compromising test quality.
## Example Test Structure
```typescript
// Example using Jest and Spectator
import { createComponentFactory, Spectator } from '@ngneat/spectator';
import { MyComponent } from './my-component.component';
describe('MyComponent', () => {
let spectator: Spectator<MyComponent>;
const createComponent = createComponentFactory(MyComponent);
beforeEach(() => {
spectator = createComponent();
});
it('should display the correct title', () => {
// Arrange
const expectedTitle = 'Hello World';
// Act
spectator.component.title = expectedTitle;
spectator.detectChanges();
// Assert
expect(spectator.query('h1')).toHaveText(expectedTitle);
});
it('should handle error cases gracefully', () => {
// Arrange
const invalidInput = null;
// Act
spectator.component.input = invalidInput;
// Assert
expect(() => spectator.component.processInput()).toThrowError('Invalid input');
});
});
```
## Additional Resources
- [Jest Documentation](https://jestjs.io/docs/getting-started)
- [Spectator Documentation](https://ngneat.github.io/spectator/)

13
.gitignore vendored
View File

@@ -1,8 +1,5 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
.matomo
junit.xml
# compiled output
/dist
/tmp
@@ -53,12 +50,4 @@ testem.log
Thumbs.db
libs/swagger/src/lib/*
*storybook.log
.nx/cache
.nx/workspace-data
.angular
storybook-static
*storybook.log

View File

@@ -1 +1 @@
npx lint-staged
npm run pretty-quick

View File

@@ -1,7 +0,0 @@
{
"*.ts": "npx eslint --fix --config eslint.config.mjs",
"*.tsx": "npx eslint --fix --config eslint.config.mjs",
"*.js": "npx eslint --fix --config eslint.config.mjs",
"*.jsx": "npx eslint --fix --config eslint.config.mjs",
"*.html": "npx eslint --fix --config eslint.config.mjs"
}

View File

@@ -1,8 +1,10 @@
# Add files here to ignore them from prettier formatting
/dist
/coverage
/.nx/cache
/.nx/workspace-data
/node_modules
.angular
.vscode
/helmvalues
/apps/swagger
/ng-swagger-gen
*.json
*.yml

5
.prettierrc.json Normal file
View File

@@ -0,0 +1,5 @@
{
"singleQuote": true,
"printWidth": 140
}

View File

@@ -1,10 +1,7 @@
{
"recommendations": [
"johnpapa.angular2",
"esbenp.prettier-vscode",
"angular.ng-template",
"nrwl.angular-console",
"dbaeumer.vscode-eslint",
"firsttris.vscode-jest-runner"
]
}
"recommendations": [
"johnpapa.angular2",
"esbenp.prettier-vscode",
"angular.ng-template",
]
}

93
.vscode/settings.json vendored
View File

@@ -1,98 +1,15 @@
{
"editor.accessibilitySupport": "off",
"typescript.tsdk": "node_modules/typescript/lib",
"exportall.config.exclude": [".test.", ".spec.", ".stories."],
"editor.formatOnSave": true,
"typescriptHero.imports.insertSemicolons": false,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"eslint.validate": [
"json"
],
"[html]": {
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"exportall.config.folderListener": [
"/libs/oms/data-access/src/lib/models",
"/libs/oms/data-access/src/lib/schemas",
"/libs/catalogue/data-access/src/lib/models",
"/libs/common/data-access/src/lib/models",
"/libs/common/data-access/src/lib/error",
"/libs/oms/data-access/src/lib/errors/return-process"
],
"github.copilot.chat.commitMessageGeneration.instructions": [
{
"file": ".github/commit-instructions.md"
}
],
"github.copilot.chat.codeGeneration.instructions": [
{
"file": ".github/copilot-instructions.md"
},
{
"file": ".github/review-instructions.md"
},
{
"file": ".github/testing-instructions.md"
},
{
"file": "docs/tech-stack.md"
},
{
"file": "docs/guidelines/code-style.md"
},
{
"file": "docs/guidelines/project-structure.md"
},
{
"file": "docs/guidelines/state-management.md"
},
{
"file": "docs/guidelines/testing.md"
}
],
"github.copilot.chat.testGeneration.instructions": [
{
"file": ".github/copilot-instructions.md"
},
{
"file": ".github/testing-instructions.md"
},
{
"file": "docs/tech-stack.md"
},
{
"file": "docs/guidelines/code-style.md"
},
{
"file": "docs/guidelines/testing.md"
}
],
"github.copilot.chat.reviewSelection.instructions": [
{
"file": ".github/copilot-instructions.md"
},
{
"file": ".github/review-instructions.md"
},
{
"file": "docs/tech-stack.md"
},
{
"file": "docs/guidelines/code-style.md"
},
{
"file": "docs/guidelines/project-structure.md"
},
{
"file": "docs/guidelines/state-management.md"
},
{
"file": "docs/guidelines/testing.md"
}
],
"css.validate": false,
"less.validate": false,
"scss.validate": false
}

View File

@@ -1,5 +1,5 @@
#stage 1
FROM node:22 as base
FROM node:18 as base
ARG IS_PRODUCTION=false
ARG SEMVERSION=1.0.0
ARG BuildUniqueID
@@ -8,7 +8,7 @@ WORKDIR /app
COPY . .
RUN umask 0022
RUN npm version ${SEMVERSION}
RUN npm install --foreground-scripts
RUN npm install --always-auth=false
RUN if [ "${IS_PRODUCTION}" = "true" ] ; then npm run-script build-prod ; else npm run-script build ; fi
# stage final

164
angular.json Normal file
View File

@@ -0,0 +1,164 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "apps",
"projects": {
"isa-app": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"style": "scss"
},
"@schematics/angular:application": {
"strict": true
}
},
"root": "apps/isa-app",
"sourceRoot": "apps/isa-app/src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"allowedCommonJsDependencies": [
"lodash",
"moment",
"jsrsasign",
"pdfjs-dist/build/pdf",
"pdfjs-dist/web/pdf_viewer",
"pdfjs-dist/es5/build/pdf",
"pdfjs-dist/es5/web/pdf_viewer"
],
"outputPath": "dist/isa-app",
"index": "apps/isa-app/src/index.html",
"main": "apps/isa-app/src/main.ts",
"polyfills": "apps/isa-app/src/polyfills.ts",
"tsConfig": "apps/isa-app/tsconfig.app.json",
"inlineStyleLanguage": "scss",
"assets": [
"apps/isa-app/src/favicon.ico",
"apps/isa-app/src/assets",
"apps/isa-app/src/config",
"apps/isa-app/src/silent-refresh.html",
"apps/isa-app/src/manifest.webmanifest",
{
"glob": "**/*",
"input": "node_modules/scandit-web-datacapture-barcode/build/engine",
"output": "scandit"
}
],
"styles": [
"apps/isa-app/src/styles.scss"
],
"scripts": [],
"serviceWorker": true,
"ngswConfigPath": "apps/isa-app/ngsw-config.json"
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "25kb"
}
],
"fileReplacements": [
{
"replace": "apps/isa-app/src/environments/environment.ts",
"with": "apps/isa-app/src/environments/environment.prod.ts"
}
],
"outputHashing": "all"
},
"development": {
"buildOptimizer": false,
"optimization": false,
"vendorChunk": true,
"extractLicenses": false,
"sourceMap": true,
"namedChunks": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"buildTarget": "isa-app:build:production"
},
"development": {
"buildTarget": "isa-app:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"buildTarget": "isa-app:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"tsConfig": "apps/isa-app/tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"polyfills": [
"zone.js",
"zone.js/testing"
],
"inlineStyleLanguage": "scss",
"assets": [
"apps/isa-app/src/favicon.ico",
"apps/isa-app/src/assets",
"apps/isa-app/src/manifest.webmanifest"
],
"styles": [
"apps/isa-app/src/styles.scss"
],
"scripts": []
}
},
"storybook": {
"builder": "@storybook/angular:start-storybook",
"options": {
"configDir": "apps/isa-app/.storybook",
"browserTarget": "isa-app:build",
"compodoc": true,
"compodocArgs": [
"-e",
"json",
"-d",
"apps/isa-app"
],
"port": 6006
}
},
"build-storybook": {
"builder": "@storybook/angular:build-storybook",
"options": {
"configDir": "apps/isa-app/.storybook",
"browserTarget": "isa-app:build",
"compodoc": true,
"compodocArgs": [
"-e",
"json",
"-d",
"apps/isa-app"
],
"outputDir": "storybook-static"
}
}
}
}
},
"cli": {
"analytics": false
}
}

View File

@@ -1,21 +1,17 @@
import type { StorybookConfig } from '@storybook/angular';
const config: StorybookConfig = {
stories: ['../stories/**/*.@(mdx|stories.@(js|jsx|ts|tsx))'],
addons: ['@storybook/addon-essentials', '@storybook/addon-interactions'],
staticDirs: ['../src/assets'],
previewHead: (head) => `
${head}
<link href="/assets/fonts/fonts.css" rel="stylesheet" />
`,
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
addons: [
'@storybook/addon-onboarding',
'@storybook/addon-links',
'@storybook/addon-essentials',
'@chromatic-com/storybook',
'@storybook/addon-interactions',
],
framework: {
name: '@storybook/angular',
options: {},
},
};
export default config;
// To customize your webpack configuration you can use the webpackFinal field.
// Check https://storybook.js.org/docs/react/builders/webpack#extending-storybooks-webpack-config
// and https://nx.dev/recipes/storybook/custom-builder-configs

View File

@@ -1,7 +1,17 @@
import type { Preview } from '@storybook/angular';
import { setCompodocJson } from '@storybook/addon-docs/angular';
import docJson from '../documentation.json';
setCompodocJson(docJson);
const preview: Preview = {
tags: ['autodocs'],
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
},
};
export default preview;

View File

@@ -0,0 +1,10 @@
// This tsconfig is used by Compodoc to generate the documentation for the project.
// If Compodoc is not used, this file can be deleted.
{
"extends": "./tsconfig.json",
// Exclude all files that are not needed for documentation generation.
"exclude": ["../src/test.ts", "../src/**/*.spec.ts", "../src/**/*.stories.ts"],
// Please make sure to include all files from which Compodoc should generate documentation.
"include": ["../src/**/*"],
"files": ["./typings.d.ts"]
}

View File

@@ -1,21 +1,11 @@
{
"extends": "../tsconfig.json",
"extends": "../tsconfig.app.json",
"compilerOptions": {
"emitDecoratorMetadata": true
"types": ["node"],
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true
},
"exclude": ["../**/*.spec.ts"],
"include": [
// "../src/**/*.stories.ts",
// "../src/**/*.stories.js",
// "../src/**/*.stories.jsx",
// "../src/**/*.stories.tsx",
// "../src/**/*.stories.mdx",
"../stories/**/*.stories.ts",
"../stories/**/*.stories.js",
"../stories/**/*.stories.jsx",
"../stories/**/*.stories.tsx",
"../stories/**/*.stories.mdx",
"*.js",
"*.ts"
]
"exclude": ["../src/test.ts", "../src/**/*.spec.ts"],
"include": ["../src/**/*.stories.*", "./preview.ts", "../src/polyfills.ts"],
"files": ["./typings.d.ts"]
}

4
apps/isa-app/.storybook/typings.d.ts vendored Normal file
View File

@@ -0,0 +1,4 @@
declare module '*.md' {
const content: string;
export default content;
}

View File

@@ -1,55 +0,0 @@
import nx from '@nx/eslint-plugin';
import baseConfig from '../../eslint.config.mjs';
export default [
...baseConfig,
...nx.configs['flat/angular'],
...nx.configs['flat/angular-template'],
{
files: ['**/*.ts'],
rules: {
'@angular-eslint/directive-selector': [
'error',
{
type: 'attribute',
prefix: 'app',
style: 'camelCase',
},
],
'@angular-eslint/component-selector': [
'error',
{
type: 'element',
prefix: 'app',
style: 'kebab-case',
},
],
},
},
{
files: ['**/*.ts'],
rules: {
'@typescript-eslint/no-unused-expressions': 'warn',
'prefer-const': 'warn',
'@angular-eslint/contextual-lifecycle': 'warn',
'@typescript-eslint/no-explicit-any': 'warn',
'@angular-eslint/no-empty-lifecycle-method': 'warn',
'@typescript-eslint/no-inferrable-types': 'warn',
'@angular-eslint/component-selector': 'warn',
'@angular-eslint/prefer-standalone': 'warn',
'@typescript-eslint/no-inferrable-types': 'warn',
'no-empty-function': 'warn',
'@typescript-eslint/no-empty-function': 'warn',
'@typescript-eslint/no-unused-vars': 'warn',
'@angular-eslint/directive-selector': 'warn',
},
},
{
files: ['**/*.html'],
// Override or add rules here
rules: {
'@angular-eslint/template/elements-content': 'warn',
'@angular-eslint/template/no-autofocus': 'warn',
},
},
];

View File

@@ -1,21 +0,0 @@
export default {
displayName: 'isa-app',
preset: '../../jest.preset.js',
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
coverageDirectory: '../../coverage/apps/prj',
transform: {
'^.+\\.(ts|mjs|js|html)$': [
'jest-preset-angular',
{
tsconfig: '<rootDir>/tsconfig.spec.json',
stringifyContentPathRegex: '\\.(html|svg)$',
},
],
},
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
'jest-preset-angular/build/serializers/html-comment',
],
};

View File

@@ -29,4 +29,4 @@
}
}
]
}
}

View File

@@ -1,164 +0,0 @@
{
"name": "isa-app",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"prefix": "app",
"sourceRoot": "apps/isa-app/src",
"tags": [],
"targets": {
"build": {
"executor": "@angular-devkit/build-angular:application",
"options": {
"allowedCommonJsDependencies": [
"lodash",
"moment",
"jsrsasign",
"pdfjs-dist/build/pdf",
"pdfjs-dist/web/pdf_viewer",
"pdfjs-dist/es5/build/pdf",
"pdfjs-dist/es5/web/pdf_viewer"
],
"outputPath": "dist/isa-app",
"index": "apps/isa-app/src/index.html",
"browser": "apps/isa-app/src/main.ts",
"polyfills": ["zone.js"],
"tsConfig": "apps/isa-app/tsconfig.app.json",
"inlineStyleLanguage": "scss",
"assets": [
"apps/isa-app/src/favicon.ico",
"apps/isa-app/src/assets",
"apps/isa-app/src/config",
"apps/isa-app/src/silent-refresh.html",
"apps/isa-app/src/manifest.webmanifest",
{
"glob": "**/*",
"input": "node_modules/scandit-web-datacapture-barcode/build/engine",
"output": "scandit"
}
],
"styles": [
"@angular/cdk/overlay-prebuilt.css",
"apps/isa-app/src/ui.scss",
"apps/isa-app/src/styles.scss"
],
"scripts": []
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "25kb"
}
],
"fileReplacements": [
{
"replace": "apps/isa-app/src/environments/environment.ts",
"with": "apps/isa-app/src/environments/environment.prod.ts"
}
],
"outputHashing": "all",
"serviceWorker": "apps/isa-app/ngsw-config.json"
},
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
}
},
"defaultConfiguration": "production",
"outputs": ["{options.outputPath}"]
},
"serve": {
"executor": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"buildTarget": "isa-app:build:production"
},
"development": {
"buildTarget": "isa-app:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"executor": "@angular-devkit/build-angular:extract-i18n",
"options": {
"buildTarget": "isa-app:build"
}
},
"lint": {
"executor": "@nx/eslint:lint"
},
"test": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "apps/isa-app/jest.config.ts"
}
},
"serve-static": {
"executor": "@nx/web:file-server",
"options": {
"buildTarget": "isa-app:build",
"staticFilePath": "dist/apps/isa-app/browser",
"spa": true
}
},
"storybook": {
"executor": "@storybook/angular:start-storybook",
"options": {
"port": 4400,
"configDir": "apps/isa-app/.storybook",
"browserTarget": "isa-app:build",
"compodoc": false,
"styles": ["apps/isa-app/src/ui.scss", "apps/isa-app/src/styles.scss"]
},
"configurations": {
"ci": {
"quiet": true
}
}
},
"build-storybook": {
"executor": "@storybook/angular:build-storybook",
"outputs": ["{options.outputDir}"],
"options": {
"outputDir": "dist/storybook/isa-app",
"configDir": "apps/isa-app/.storybook",
"browserTarget": "isa-app:build",
"compodoc": false
},
"configurations": {
"ci": {
"quiet": true
}
}
},
"test-storybook": {
"executor": "nx:run-commands",
"options": {
"command": "test-storybook -c apps/isa-app/.storybook --url=http://localhost:4400"
}
},
"static-storybook": {
"executor": "@nx/web:file-server",
"dependsOn": ["build-storybook"],
"options": {
"buildTarget": "isa-app:build-storybook",
"staticFilePath": "dist/storybook/isa-app",
"spa": true
},
"configurations": {
"ci": {
"buildTarget": "isa-app:build-storybook:ci"
}
}
}
}
}

View File

@@ -1,9 +1,8 @@
import { inject, Injectable } from '@angular/core';
import { EnvironmentService } from '@core/environment';
import { PromptModalData, UiModalService, UiPromptModalComponent } from '@ui/modal';
import { Observable } from 'rxjs';
import { ScanAdapter } from './scan-adapter';
import { Config } from '@core/config';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
@Injectable()
export class DevScanAdapter implements ScanAdapter {
@@ -11,12 +10,11 @@ export class DevScanAdapter implements ScanAdapter {
private _modal = inject(UiModalService);
private _config = inject(Config);
async init(): Promise<boolean> {
return new Promise((resolve, reject) => {
resolve(coerceBooleanProperty(this._config.get('dev-scanner')));
});
return Promise.resolve(false);
// return new Promise((resolve, reject) => {
// resolve(isDevMode());
// });
}
scan(): Observable<string> {

View File

@@ -37,7 +37,7 @@ export class ScanAdapterService {
for (const name of adapterOrder) {
adapter = this.getAdapter(name);
console.log('adapter', adapter);
if (adapter) {
break;
}

View File

@@ -1,12 +1,4 @@
import {
Component,
ChangeDetectionStrategy,
ElementRef,
ViewChild,
NgZone,
AfterViewInit,
OnDestroy, OnInit,
} from '@angular/core';
import { Component, ChangeDetectionStrategy, ElementRef, ViewChild, NgZone, AfterViewInit, OnDestroy, OnInit } from '@angular/core';
import { BarcodeCapture, BarcodeCaptureSettings, Symbology } from 'scandit-web-datacapture-barcode';
import { Camera, DataCaptureContext, DataCaptureView, FrameSourceState } from 'scandit-web-datacapture-core';
@@ -15,9 +7,8 @@ import { Camera, DataCaptureContext, DataCaptureView, FrameSourceState } from 's
templateUrl: 'scandit-overlay.component.html',
styleUrls: ['scandit-overlay.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: false,
})
export class ScanditOverlayComponent implements AfterViewInit, OnDestroy, OnInit {
export class ScanditOverlayComponent implements OnInit, AfterViewInit, OnDestroy {
private dataCaptureContext: DataCaptureContext;
private dataCaptureView: DataCaptureView;
private barcodeCapture: BarcodeCapture;

View File

@@ -21,16 +21,9 @@ import { PreviewComponent } from './preview';
import { BranchSectionResolver, CustomerSectionResolver, ProcessIdResolver } from './resolvers';
import { TokenLoginComponent, TokenLoginModule } from './token-login';
import { ProcessIdGuard } from './guards/process-id.guard';
import {
ActivateProcessIdGuard,
ActivateProcessIdWithConfigKeyGuard,
} from './guards/activate-process-id.guard';
import { MatomoRouteData } from 'ngx-matomo-client';
import { processResolverFn } from '@isa/core/process';
import { provideScrollPositionRestoration } from '@isa/utils/scroll-position';
import { ActivateProcessIdGuard, ActivateProcessIdWithConfigKeyGuard } from './guards/activate-process-id.guard';
const routes: Routes = [
{ path: '', redirectTo: 'kunde/dashboard', pathMatch: 'full' },
{
path: 'login',
children: [
@@ -49,11 +42,6 @@ const routes: Routes = [
{
path: 'dashboard',
loadChildren: () => import('@page/dashboard').then((m) => m.DashboardModule),
data: {
matomo: {
title: 'Dashboard',
} as MatomoRouteData,
},
},
{
path: 'product',
@@ -140,8 +128,7 @@ const routes: Routes = [
},
{
path: 'package-inspection',
loadChildren: () =>
import('@page/package-inspection').then((m) => m.PackageInspectionModule),
loadChildren: () => import('@page/package-inspection').then((m) => m.PackageInspectionModule),
canActivate: [CanActivatePackageInspectionGuard],
},
{
@@ -153,18 +140,7 @@ const routes: Routes = [
],
resolve: { section: BranchSectionResolver },
},
],
},
{
path: ':processId',
component: MainComponent,
resolve: { process: processResolverFn },
canActivate: [IsAuthenticatedGuard],
children: [
{
path: 'return',
loadChildren: () => import('@isa/oms/feature/return-search').then((m) => m.routes),
},
{ path: '**', redirectTo: 'kunde', pathMatch: 'full' },
],
},
];
@@ -179,6 +155,5 @@ if (isDevMode()) {
@NgModule({
imports: [RouterModule.forRoot(routes), TokenLoginModule],
exports: [RouterModule],
providers: [provideScrollPositionRestoration()],
})
export class AppRoutingModule {}

View File

@@ -2,6 +2,7 @@ import { NgModule } from '@angular/core';
import { EffectsModule } from '@ngrx/effects';
import { ActionReducer, MetaReducer, StoreModule } from '@ngrx/store';
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
import { storeFreeze } from 'ngrx-store-freeze';
import packageInfo from 'packageJson';
import { environment } from '../environments/environment';
import { RootStateService } from './store/root-state.service';
@@ -21,9 +22,7 @@ export function storeInLocalStorage(reducer: ActionReducer<any>): ActionReducer<
};
}
export const metaReducers: MetaReducer<RootState>[] = !environment.production
? [storeInLocalStorage]
: [storeInLocalStorage];
export const metaReducers: MetaReducer<RootState>[] = !environment.production ? [storeFreeze, storeInLocalStorage] : [storeInLocalStorage];
@NgModule({
imports: [

View File

@@ -1,16 +1,15 @@
import { HttpInterceptorFn, provideHttpClient, withInterceptors } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { Config } from '@core/config';
import { AvConfiguration } from '@generated/swagger/availability-api';
import { CatConfiguration } from '@generated/swagger/cat-search-api';
import { CheckoutConfiguration } from '@generated/swagger/checkout-api';
import { CrmConfiguration } from '@generated/swagger/crm-api';
import { EisConfiguration } from '@generated/swagger/eis-api';
import { IsaConfiguration } from '@generated/swagger/isa-api';
import { OmsConfiguration } from '@generated/swagger/oms-api';
import { PrintConfiguration } from '@generated/swagger/print-api';
import { RemiConfiguration } from '@generated/swagger/inventory-api';
import { WwsConfiguration } from '@generated/swagger/wws-api';
import { AvConfiguration } from '@swagger/availability';
import { CatConfiguration } from '@swagger/cat';
import { CheckoutConfiguration } from '@swagger/checkout';
import { CrmConfiguration } from '@swagger/crm';
import { EisConfiguration } from '@swagger/eis';
import { IsaConfiguration } from '@swagger/isa';
import { OmsConfiguration } from '@swagger/oms';
import { PrintConfiguration } from '@swagger/print';
import { RemiConfiguration } from '@swagger/remi';
import { WwsConfiguration } from '@swagger/wws';
export function createConfigurationFactory(name: string) {
return function (config: Config): { rootUrl: string } {
@@ -18,13 +17,8 @@ export function createConfigurationFactory(name: string) {
};
}
const serviceWorkerInterceptor: HttpInterceptorFn = (req, next) => {
return next(req.clone({ setHeaders: { 'ngsw-bypass': 'true' } }));
};
@NgModule({
providers: [
provideHttpClient(withInterceptors([serviceWorkerInterceptor])),
{ provide: AvConfiguration, useFactory: createConfigurationFactory('av'), deps: [Config] },
{ provide: CatConfiguration, useFactory: createConfigurationFactory('cat'), deps: [Config] },
{ provide: CheckoutConfiguration, useFactory: createConfigurationFactory('checkout'), deps: [Config] },

View File

@@ -8,7 +8,7 @@ import { Renderer2 } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SwUpdate } from '@angular/service-worker';
import { NotificationsHub } from '@hub/notifications';
import { UserStateService } from '@generated/swagger/isa-api';
import { UserStateService } from '@swagger/isa';
import { UiModalService } from '@ui/modal';
import { AuthService } from '@core/auth';

View File

@@ -1,16 +1,5 @@
import { DOCUMENT } from '@angular/common';
import {
Component,
effect,
HostListener,
inject,
Inject,
Injector,
OnInit,
Renderer2,
signal,
untracked,
} from '@angular/core';
import { Component, effect, HostListener, Inject, OnInit, Renderer2, signal, untracked } from '@angular/core';
import { Title } from '@angular/platform-browser';
import { SwUpdate } from '@angular/service-worker';
import { ApplicationService } from '@core/application';
@@ -18,10 +7,10 @@ import { Config } from '@core/config';
import { NotificationsHub } from '@hub/notifications';
import packageInfo from 'packageJson';
import { asapScheduler, interval, Subscription } from 'rxjs';
import { UserStateService } from '@generated/swagger/isa-api';
import { UserStateService } from '@swagger/isa';
import { IsaLogProvider } from './providers';
import { EnvironmentService } from '@core/environment';
import { AuthService, LoginStrategy } from '@core/auth';
import { AuthService } from '@core/auth';
import { UiMessageModalComponent, UiModalService } from '@ui/modal';
import { injectOnline$ } from './services/network-status.service';
import { toSignal } from '@angular/core/rxjs-interop';
@@ -44,11 +33,8 @@ import { animate, style, transition, trigger } from '@angular/animations';
]),
]),
],
standalone: false,
})
export class AppComponent implements OnInit {
readonly injector = inject(Injector);
$online = toSignal(injectOnline$());
$offlineBannerVisible = signal(false);
@@ -132,10 +118,7 @@ export class AppComponent implements OnInit {
}
logVersion() {
console.log(
`%c${this._config.get('title')}\r\nVersion: ${packageInfo.version}`,
'font-weight: bold; font-size: 20px;',
);
console.log(`%c${this._config.get('title')}\r\nVersion: ${packageInfo.version}`, 'font-weight: bold; font-size: 20px;');
}
determinePlatform() {
@@ -197,9 +180,15 @@ export class AppComponent implements OnInit {
if (this._document.hidden && this._authService.isAuthenticated()) {
this._authService.refresh();
} else if (!this._authService.isAuthenticated()) {
const strategy = this.injector.get(LoginStrategy);
return strategy.login('Sie sind nicht mehr angemeldet');
return this._modal
.open({
content: UiMessageModalComponent,
title: 'Sie sind nicht mehr angemeldet',
data: { message: 'Sie werden neu angemeldet' },
})
.afterClosed$.subscribe(() => {
this._authService.login();
});
}
}
}

View File

@@ -1,22 +1,11 @@
import {
HTTP_INTERCEPTORS,
provideHttpClient,
withInterceptorsFromDi,
} from '@angular/common/http';
import {
ErrorHandler,
Injector,
LOCALE_ID,
NgModule,
inject,
provideAppInitializer,
} from '@angular/core';
import { HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
import { APP_INITIALIZER, ErrorHandler, Injector, LOCALE_ID, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { PlatformModule } from '@angular/cdk/platform';
import { Config } from '@core/config';
import { AuthModule, AuthService, LoginStrategy } from '@core/auth';
import { Config, ConfigModule, JsonConfigLoader } from '@core/config';
import { AuthModule, AuthService } from '@core/auth';
import { CoreCommandModule } from '@core/command';
import { AppRoutingModule } from './app-routing.module';
@@ -28,10 +17,7 @@ import { environment } from '../environments/environment';
import { AppSwaggerModule } from './app-swagger.module';
import { AppDomainModule } from './app-domain.module';
import { UiModalModule } from '@ui/modal';
import {
NotificationsHubModule,
NOTIFICATIONS_HUB_OPTIONS,
} from '@hub/notifications';
import { NotificationsHubModule, NOTIFICATIONS_HUB_OPTIONS } from '@hub/notifications';
import { SignalRHubOptions } from '@core/signalr';
import { CoreBreadcrumbModule } from '@core/breadcrumb';
import { UiCommonModule } from '@ui/common';
@@ -43,11 +29,7 @@ import { HttpErrorInterceptor } from './interceptors';
import { CoreLoggerModule, LOG_PROVIDER } from '@core/logger';
import { IsaLogProvider } from './providers';
import { IsaErrorHandler } from './providers/isa.error-handler';
import {
ScanAdapterModule,
ScanAdapterService,
ScanditScanAdapterModule,
} from '@adapter/scan';
import { ScanAdapterModule, ScanAdapterService, ScanditScanAdapterModule } from '@adapter/scan';
import { RootStateService } from './store/root-state.service';
import * as Commands from './commands';
import { PreviewComponent } from './preview';
@@ -56,34 +38,28 @@ import { ShellModule } from '@shared/shell';
import { MainComponent } from './main.component';
import { IconModule } from '@shared/components/icon';
import { NgIconsModule } from '@ng-icons/core';
import {
matClose,
matWifi,
matWifiOff,
} from '@ng-icons/material-icons/baseline';
import { matClose, matWifi, matWifiOff } from '@ng-icons/material-icons/baseline';
import { NetworkStatusService } from './services/network-status.service';
import { firstValueFrom } from 'rxjs';
import { provideMatomo } from 'ngx-matomo-client';
import { withRouter, withRouteData } from 'ngx-matomo-client';
import {
provideLogging,
withLogLevel,
LogLevel,
withSink,
ConsoleLogSink,
} from '@isa/core/logging';
registerLocaleData(localeDe, localeDeExtra);
registerLocaleData(localeDe, 'de', localeDeExtra);
export function _appInitializerFactory(config: Config, injector: Injector) {
export function _appInitializerFactory(
config: Config,
auth: AuthService,
injector: Injector,
scanAdapter: ScanAdapterService,
nativeContainer: NativeContainerService,
networkStatus: NetworkStatusService,
) {
return async () => {
const statusElement = document.querySelector('#init-status');
const laoderElement = document.querySelector('#init-loader');
try {
let online = false;
const networkStatus = injector.get(NetworkStatusService);
while (!online) {
online = await firstValueFrom(networkStatus.online$);
@@ -95,29 +71,20 @@ export function _appInitializerFactory(config: Config, injector: Injector) {
}
statusElement.innerHTML = 'Konfigurationen werden geladen...';
statusElement.innerHTML = 'Scanner wird initialisiert...';
const scanAdapter = injector.get(ScanAdapterService);
await scanAdapter.init();
await config.init();
statusElement.innerHTML = 'Authentifizierung wird geprüft...';
await auth.init();
const auth = injector.get(AuthService);
try {
await auth.init();
} catch (error) {
statusElement.innerHTML = 'Authentifizierung wird durchgeführt...';
const strategy = injector.get(LoginStrategy);
await strategy.login();
if (auth.isAuthenticated()) {
statusElement.innerHTML = 'App wird initialisiert...';
const state = injector.get(RootStateService);
await state.init();
}
statusElement.innerHTML = 'App wird initialisiert...';
const state = injector.get(RootStateService);
await state.init();
statusElement.innerHTML = 'Native Container wird initialisiert...';
const nativeContainer = injector.get(NativeContainerService);
await nativeContainer.init();
statusElement.innerHTML = 'Scanner wird initialisiert...';
await scanAdapter.init();
} catch (error) {
laoderElement.remove();
statusElement.classList.add('text-xl');
@@ -125,13 +92,7 @@ export function _appInitializerFactory(config: Config, injector: Injector) {
'⚡<br><br><b>Fehler bei der Initialisierung</b><br><br>Bitte prüfen Sie die Netzwerkverbindung (WLAN).<br><br>';
const reload = document.createElement('button');
reload.classList.add(
'bg-brand',
'text-white',
'p-2',
'rounded',
'cursor-pointer',
);
reload.classList.add('bg-brand', 'text-white', 'p-2', 'rounded', 'cursor-pointer');
reload.innerHTML = 'App neu laden';
reload.onclick = () => window.location.reload();
statusElement.appendChild(reload);
@@ -154,10 +115,7 @@ export function _appInitializerFactory(config: Config, injector: Injector) {
};
}
export function _notificationsHubOptionsFactory(
config: Config,
auth: AuthService,
): SignalRHubOptions {
export function _notificationsHubOptionsFactory(config: Config, auth: AuthService): SignalRHubOptions {
const options = { ...config.get('hubs').notifications };
options.httpOptions.accessTokenFactory = () => auth.getToken();
return options;
@@ -174,6 +132,10 @@ export function _notificationsHubOptionsFactory(
AppSwaggerModule,
AppDomainModule,
CoreBreadcrumbModule.forRoot(),
ConfigModule.forRoot({
useConfigLoader: JsonConfigLoader,
jsonConfigLoaderUrl: '/config/config.json',
}),
CoreCommandModule.forRoot(Object.values(Commands)),
CoreLoggerModule.forRoot(),
AppStoreModule,
@@ -194,13 +156,12 @@ export function _notificationsHubOptionsFactory(
NgIconsModule.withIcons({ matWifiOff, matClose, matWifi }),
],
providers: [
provideAppInitializer(() => {
const initializerFn = _appInitializerFactory(
inject(Config),
inject(Injector),
);
return initializerFn();
}),
{
provide: APP_INITIALIZER,
useFactory: _appInitializerFactory,
multi: true,
deps: [Config, AuthService, Injector, ScanAdapterService, NativeContainerService, NetworkStatusService],
},
{
provide: NOTIFICATIONS_HUB_OPTIONS,
useFactory: _notificationsHubOptionsFactory,
@@ -222,12 +183,6 @@ export function _notificationsHubOptionsFactory(
},
{ provide: LOCALE_ID, useValue: 'de-DE' },
provideHttpClient(withInterceptorsFromDi()),
provideMatomo(
{ trackerUrl: 'https://matomo.paragon-data.net', siteId: '1' },
withRouter(),
withRouteData(),
),
provideLogging(withLogLevel(LogLevel.Debug), withSink(ConsoleLogSink)),
],
})
export class AppModule {}

View File

@@ -3,14 +3,11 @@ import { Router } from '@angular/router';
import { ApplicationService } from '@core/application';
import { ActionHandler } from '@core/command';
import { Result } from '@domain/defs';
import { CustomerInfoDTO } from '@generated/swagger/crm-api';
import { CustomerInfoDTO } from '@swagger/crm';
@Injectable()
export class CreateCustomerCommand extends ActionHandler<Result<CustomerInfoDTO[]>> {
constructor(
private _router: Router,
private _application: ApplicationService,
) {
constructor(private _router: Router, private _application: ApplicationService) {
super('CREATE_CUSTOMER');
}

View File

@@ -4,7 +4,7 @@ import { ApplicationService } from '@core/application';
import { ActionHandler } from '@core/command';
import { Result } from '@domain/defs';
import { encodeFormData, mapCustomerInfoDtoToCustomerCreateFormData } from '@page/customer';
import { CustomerInfoDTO } from '@generated/swagger/crm-api';
import { CustomerInfoDTO } from '@swagger/crm';
@Injectable()
export class CreateKubiCustomerCommand extends ActionHandler<Result<CustomerInfoDTO[]>> {
@@ -40,12 +40,9 @@ export class CreateKubiCustomerCommand extends ActionHandler<Result<CustomerInfo
customerType = 'store';
}
await this._router.navigate(
['/kunde', this._application.activatedProcessId, 'customer', 'create', `${customerType}-p4m`],
{
queryParams: { formData },
},
);
await this._router.navigate(['/kunde', this._application.activatedProcessId, 'customer', 'create', `${customerType}-p4m`], {
queryParams: { formData },
});
return data;
}
}

View File

@@ -3,15 +3,12 @@ import { ActionHandler } from '@core/command';
import { Result } from '@domain/defs';
import { DomainPrinterService } from '@domain/printer';
import { PrintModalComponent, PrintModalData } from '@modal/printer';
import { CustomerInfoDTO } from '@generated/swagger/crm-api';
import { CustomerInfoDTO } from '@swagger/crm';
import { UiModalService } from '@ui/modal';
@Injectable()
export class PrintKubiCustomerCommand extends ActionHandler<Result<CustomerInfoDTO[]>> {
constructor(
private _uiModal: UiModalService,
private _printerService: DomainPrinterService,
) {
constructor(private _uiModal: UiModalService, private _printerService: DomainPrinterService) {
super('PRINT_KUBI_AGB');
}

View File

@@ -4,10 +4,7 @@ import { ApplicationService } from '@core/application';
import { Config } from '@core/config';
import { take } from 'rxjs/operators';
export const ActivateProcessIdGuard: CanActivateFn = async (
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot,
) => {
export const ActivateProcessIdGuard: CanActivateFn = async (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => {
const application = inject(ApplicationService);
const processIdStr = route.params.processId;
@@ -30,18 +27,20 @@ export const ActivateProcessIdGuard: CanActivateFn = async (
return true;
};
export const ActivateProcessIdWithConfigKeyGuard: (key: string) => CanActivateFn =
(key) => async (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => {
const application = inject(ApplicationService);
const config = inject(Config);
export const ActivateProcessIdWithConfigKeyGuard: (key: string) => CanActivateFn = (key) => async (
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
) => {
const application = inject(ApplicationService);
const config = inject(Config);
const processId = config.get(`process.ids.${key}`);
const processId = config.get(`process.ids.${key}`);
if (isNaN(processId)) {
return false;
}
if (isNaN(processId)) {
return false;
}
application.activateProcess(processId);
application.activateProcess(processId);
return true;
};
return true;
};

View File

@@ -6,16 +6,10 @@ import { first } from 'rxjs/operators';
@Injectable({ providedIn: 'root' })
export class CanActivateAssortmentGuard {
constructor(
private readonly _applicationService: ApplicationService,
private readonly _config: Config,
) {}
constructor(private readonly _applicationService: ApplicationService, private readonly _config: Config) {}
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
const process = await this._applicationService
.getProcessById$(this._config.get('process.ids.assortment'))
.pipe(first())
.toPromise();
const process = await this._applicationService.getProcessById$(this._config.get('process.ids.assortment')).pipe(first()).toPromise();
if (!process) {
await this._applicationService.createProcess({
id: this._config.get('process.ids.assortment'),

View File

@@ -8,7 +8,10 @@ export class CanActivateCartWithProcessIdGuard {
constructor(private readonly _applicationService: ApplicationService) {}
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
const process = await this._applicationService.getProcessById$(+route.params.processId).pipe(first()).toPromise();
const process = await this._applicationService
.getProcessById$(+route.params.processId)
.pipe(first())
.toPromise();
// if (!(process?.type === 'cart')) {
// // TODO:

View File

@@ -14,10 +14,7 @@ export class CanActivateCartGuard {
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
const processes = await this._applicationService.getProcesses$('customer').pipe(first()).toPromise();
let lastActivatedProcessId = (
await this._applicationService
.getLastActivatedProcessWithSectionAndType$('customer', 'cart')
.pipe(first())
.toPromise()
await this._applicationService.getLastActivatedProcessWithSectionAndType$('customer', 'cart').pipe(first()).toPromise()
)?.id;
if (!lastActivatedProcessId) {
lastActivatedProcessId = Date.now();

View File

@@ -6,13 +6,13 @@ import { first } from 'rxjs/operators';
@Injectable({ providedIn: 'root' })
export class CanActivateCustomerOrdersWithProcessIdGuard {
constructor(
private readonly _applicationService: ApplicationService,
private readonly _breadcrumbService: BreadcrumbService,
) {}
constructor(private readonly _applicationService: ApplicationService, private readonly _breadcrumbService: BreadcrumbService) {}
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
const process = await this._applicationService.getProcessById$(+route.params.processId).pipe(first()).toPromise();
const process = await this._applicationService
.getProcessById$(+route.params.processId)
.pipe(first())
.toPromise();
if (!process) {
const processes = await this._applicationService.getProcesses$('customer').pipe(first()).toPromise();
@@ -31,7 +31,10 @@ export class CanActivateCustomerOrdersWithProcessIdGuard {
// Fix #3292: Alle Breadcrumbs die nichts mit dem aktuellen Prozess zu tun haben, müssen removed werden
async removeBreadcrumbWithSameProcessId(route: ActivatedRouteSnapshot) {
const crumbs = await this._breadcrumbService.getBreadcrumbByKey$(+route.params.processId).pipe(first()).toPromise();
const crumbs = await this._breadcrumbService
.getBreadcrumbByKey$(+route.params.processId)
.pipe(first())
.toPromise();
// Entferne alle Crumbs die nichts mit den Kundenbestellungen zu tun haben
if (crumbs.length > 1) {

View File

@@ -17,17 +17,11 @@ export class CanActivateCustomerOrdersGuard {
const processes = await this._applicationService.getProcesses$('customer').pipe(first()).toPromise();
let lastActivatedProcessId = (
await this._applicationService
.getLastActivatedProcessWithSectionAndType$('customer', 'cart')
.pipe(first())
.toPromise()
await this._applicationService.getLastActivatedProcessWithSectionAndType$('customer', 'cart').pipe(first()).toPromise()
)?.id;
const lastActivatedCartCheckoutProcessId = (
await this._applicationService
.getLastActivatedProcessWithSectionAndType$('customer', 'cart-checkout')
.pipe(first())
.toPromise()
await this._applicationService.getLastActivatedProcessWithSectionAndType$('customer', 'cart-checkout').pipe(first()).toPromise()
)?.id;
const activatedProcessId = await this._applicationService.getActivatedProcessId$().pipe(first()).toPromise();

View File

@@ -6,13 +6,13 @@ import { first } from 'rxjs/operators';
@Injectable({ providedIn: 'root' })
export class CanActivateCustomerWithProcessIdGuard {
constructor(
private readonly _applicationService: ApplicationService,
private readonly _breadcrumbService: BreadcrumbService,
) {}
constructor(private readonly _applicationService: ApplicationService, private readonly _breadcrumbService: BreadcrumbService) {}
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
const process = await this._applicationService.getProcessById$(+route.params.processId).pipe(first()).toPromise();
const process = await this._applicationService
.getProcessById$(+route.params.processId)
.pipe(first())
.toPromise();
// if (!(process?.type === 'cart')) {
// // TODO:
@@ -37,7 +37,10 @@ export class CanActivateCustomerWithProcessIdGuard {
// Fix #3292: Alle Breadcrumbs die nichts mit dem aktuellen Prozess zu tun haben, müssen removed werden
async removeBreadcrumbWithSameProcessId(route: ActivatedRouteSnapshot) {
const crumbs = await this._breadcrumbService.getBreadcrumbByKey$(+route.params.processId).pipe(first()).toPromise();
const crumbs = await this._breadcrumbService
.getBreadcrumbByKey$(+route.params.processId)
.pipe(first())
.toPromise();
// Entferne alle Crumbs die nichts mit der Kundensuche zu tun haben
if (crumbs.length > 1) {

View File

@@ -17,24 +17,15 @@ export class CanActivateCustomerGuard {
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
const processes = await this._applicationService.getProcesses$('customer').pipe(first()).toPromise();
let lastActivatedProcessId = (
await this._applicationService
.getLastActivatedProcessWithSectionAndType$('customer', 'cart')
.pipe(first())
.toPromise()
await this._applicationService.getLastActivatedProcessWithSectionAndType$('customer', 'cart').pipe(first()).toPromise()
)?.id;
const lastActivatedCartCheckoutProcessId = (
await this._applicationService
.getLastActivatedProcessWithSectionAndType$('customer', 'cart-checkout')
.pipe(first())
.toPromise()
await this._applicationService.getLastActivatedProcessWithSectionAndType$('customer', 'cart-checkout').pipe(first()).toPromise()
)?.id;
const lastActivatedGoodsOutProcessId = (
await this._applicationService
.getLastActivatedProcessWithSectionAndType$('customer', 'goods-out')
.pipe(first())
.toPromise()
await this._applicationService.getLastActivatedProcessWithSectionAndType$('customer', 'goods-out').pipe(first()).toPromise()
)?.id;
const activatedProcessId = await this._applicationService.getActivatedProcessId$().pipe(first()).toPromise();

View File

@@ -3,21 +3,13 @@ import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { ApplicationService } from '@core/application';
import { Config } from '@core/config';
import { first } from 'rxjs/operators';
import { z } from 'zod';
@Injectable({ providedIn: 'root' })
export class CanActivateGoodsInGuard {
constructor(
private readonly _applicationService: ApplicationService,
private readonly _config: Config,
) {}
constructor(private readonly _applicationService: ApplicationService, private readonly _config: Config) {}
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
const pid = this._config.get('process.ids.goodsIn', z.number());
const process = await this._applicationService
.getProcessById$(pid)
.pipe(first())
.toPromise();
const process = await this._applicationService.getProcessById$(this._config.get('process.ids.goodsIn')).pipe(first()).toPromise();
if (!process) {
await this._applicationService.createProcess({
id: this._config.get('process.ids.goodsIn'),
@@ -26,9 +18,7 @@ export class CanActivateGoodsInGuard {
name: '',
});
}
this._applicationService.activateProcess(
this._config.get('process.ids.goodsIn'),
);
this._applicationService.activateProcess(this._config.get('process.ids.goodsIn'));
return true;
}
}

View File

@@ -6,13 +6,13 @@ import { first } from 'rxjs/operators';
@Injectable({ providedIn: 'root' })
export class CanActivateGoodsOutWithProcessIdGuard {
constructor(
private readonly _applicationService: ApplicationService,
private readonly _breadcrumbService: BreadcrumbService,
) {}
constructor(private readonly _applicationService: ApplicationService, private readonly _breadcrumbService: BreadcrumbService) {}
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
const process = await this._applicationService.getProcessById$(+route.params.processId).pipe(first()).toPromise();
const process = await this._applicationService
.getProcessById$(+route.params.processId)
.pipe(first())
.toPromise();
if (!process) {
// const processes = await this._applicationService.getProcesses$('customer').pipe(first()).toPromise();
@@ -31,7 +31,10 @@ export class CanActivateGoodsOutWithProcessIdGuard {
// Fix #3292: Alle Breadcrumbs die nichts mit dem aktuellen Prozess zu tun haben, müssen removed werden
async removeBreadcrumbWithSameProcessId(route: ActivatedRouteSnapshot) {
const crumbs = await this._breadcrumbService.getBreadcrumbByKey$(+route.params.processId).pipe(first()).toPromise();
const crumbs = await this._breadcrumbService
.getBreadcrumbByKey$(+route.params.processId)
.pipe(first())
.toPromise();
// Entferne alle Crumbs die nichts mit der Warenausgabe zu tun haben
if (crumbs.length > 1) {

View File

@@ -9,7 +9,7 @@ export class CanActivateGoodsOutGuard {
constructor(
private readonly _applicationService: ApplicationService,
private readonly _checkoutService: DomainCheckoutService,
private readonly _router: Router,
private readonly _router: Router
) {}
// !!! Ticket #3272 Code soll vorerst bestehen bleiben. Prozess Warenausgabe soll wieder Vorgang heißen (wie aktuell im Produktiv), bis zum neuen Navigationskonzept
@@ -103,17 +103,11 @@ export class CanActivateGoodsOutGuard {
const processes = await this._applicationService.getProcesses$('customer').pipe(first()).toPromise();
let lastActivatedProcessId = (
await this._applicationService
.getLastActivatedProcessWithSectionAndType$('customer', 'cart')
.pipe(first())
.toPromise()
await this._applicationService.getLastActivatedProcessWithSectionAndType$('customer', 'cart').pipe(first()).toPromise()
)?.id;
const lastActivatedCartCheckoutProcessId = (
await this._applicationService
.getLastActivatedProcessWithSectionAndType$('customer', 'cart-checkout')
.pipe(first())
.toPromise()
await this._applicationService.getLastActivatedProcessWithSectionAndType$('customer', 'cart-checkout').pipe(first()).toPromise()
)?.id;
const activatedProcessId = await this._applicationService.getActivatedProcessId$().pipe(first()).toPromise();

View File

@@ -6,10 +6,7 @@ import { first } from 'rxjs/operators';
@Injectable({ providedIn: 'root' })
export class CanActivatePackageInspectionGuard {
constructor(
private readonly _applicationService: ApplicationService,
private readonly _config: Config,
) {}
constructor(private readonly _applicationService: ApplicationService, private readonly _config: Config) {}
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
const process = await this._applicationService

View File

@@ -6,13 +6,13 @@ import { first } from 'rxjs/operators';
@Injectable({ providedIn: 'root' })
export class CanActivateProductWithProcessIdGuard {
constructor(
private readonly _applicationService: ApplicationService,
private readonly _breadcrumbService: BreadcrumbService,
) {}
constructor(private readonly _applicationService: ApplicationService, private readonly _breadcrumbService: BreadcrumbService) {}
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
const process = await this._applicationService.getProcessById$(+route.params.processId).pipe(first()).toPromise();
const process = await this._applicationService
.getProcessById$(+route.params.processId)
.pipe(first())
.toPromise();
// if (!(process?.type === 'cart')) {
// // TODO:
@@ -37,7 +37,10 @@ export class CanActivateProductWithProcessIdGuard {
// Fix #3292: Alle Breadcrumbs die nichts mit dem aktuellen Prozess zu tun haben, müssen removed werden
async removeBreadcrumbWithSameProcessId(route: ActivatedRouteSnapshot) {
const crumbs = await this._breadcrumbService.getBreadcrumbByKey$(+route.params.processId).pipe(first()).toPromise();
const crumbs = await this._breadcrumbService
.getBreadcrumbByKey$(+route.params.processId)
.pipe(first())
.toPromise();
// Entferne alle Crumbs die nichts mit der Artikelsuche zu tun haben
if (crumbs.length > 1) {

View File

@@ -16,24 +16,15 @@ export class CanActivateProductGuard {
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
const processes = await this._applicationService.getProcesses$('customer').pipe(first()).toPromise();
let lastActivatedProcessId = (
await this._applicationService
.getLastActivatedProcessWithSectionAndType$('customer', 'cart')
.pipe(first())
.toPromise()
await this._applicationService.getLastActivatedProcessWithSectionAndType$('customer', 'cart').pipe(first()).toPromise()
)?.id;
const lastActivatedCartCheckoutProcessId = (
await this._applicationService
.getLastActivatedProcessWithSectionAndType$('customer', 'cart-checkout')
.pipe(first())
.toPromise()
await this._applicationService.getLastActivatedProcessWithSectionAndType$('customer', 'cart-checkout').pipe(first()).toPromise()
)?.id;
const lastActivatedGoodsOutProcessId = (
await this._applicationService
.getLastActivatedProcessWithSectionAndType$('customer', 'goods-out')
.pipe(first())
.toPromise()
await this._applicationService.getLastActivatedProcessWithSectionAndType$('customer', 'goods-out').pipe(first()).toPromise()
)?.id;
const activatedProcessId = await this._applicationService.getActivatedProcessId$().pipe(first()).toPromise();

View File

@@ -9,14 +9,11 @@ export class CanActivateRemissionGuard {
constructor(
private readonly _applicationService: ApplicationService,
private readonly _config: Config,
private readonly _router: Router,
private readonly _router: Router
) {}
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
const process = await this._applicationService
.getProcessById$(this._config.get('process.ids.remission'))
.pipe(first())
.toPromise();
const process = await this._applicationService.getProcessById$(this._config.get('process.ids.remission')).pipe(first()).toPromise();
if (!process) {
await this._applicationService.createProcess({
id: this._config.get('process.ids.remission'),

View File

@@ -6,16 +6,10 @@ import { first } from 'rxjs/operators';
@Injectable({ providedIn: 'root' })
export class CanActivateTaskCalendarGuard {
constructor(
private readonly _applicationService: ApplicationService,
private readonly _config: Config,
) {}
constructor(private readonly _applicationService: ApplicationService, private readonly _config: Config) {}
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
const process = await this._applicationService
.getProcessById$(this._config.get('process.ids.taskCalendar'))
.pipe(first())
.toPromise();
const process = await this._applicationService.getProcessById$(this._config.get('process.ids.taskCalendar')).pipe(first()).toPromise();
if (!process) {
await this._applicationService.createProcess({
id: this._config.get('process.ids.taskCalendar'),

View File

@@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router';
import { AuthService } from '@core/auth';
import { ScanAdapterService } from '@adapter/scan';
import { AuthService as IsaAuthService } from '@generated/swagger/isa-api';
import { AuthService as IsaAuthService } from '@swagger/isa';
import { UiConfirmModalComponent, UiErrorModalComponent, UiModalResult, UiModalService } from '@ui/modal';
import { EnvironmentService } from '@core/environment';
import { injectNetworkStatus$ } from '../services/network-status.service';

View File

@@ -5,7 +5,7 @@ import { take } from 'rxjs/operators';
export const ProcessIdGuard: CanActivateFn = async (
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot,
state: RouterStateSnapshot
): Promise<boolean | UrlTree> => {
const application = inject(ApplicationService);
const router = inject(Router);

View File

@@ -1,9 +1,9 @@
import { inject, Injectable, Injector } from '@angular/core';
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpEvent, HttpHandler, HttpRequest, HttpErrorResponse } from '@angular/common/http';
import { from, NEVER, Observable, throwError } from 'rxjs';
import { NEVER, Observable, throwError } from 'rxjs';
import { UiMessageModalComponent, UiModalService } from '@ui/modal';
import { catchError, filter, mergeMap, takeUntil, tap } from 'rxjs/operators';
import { AuthService, LoginStrategy } from '@core/auth';
import { AuthService } from '@core/auth';
import { IsaLogProvider } from '../providers';
import { LogLevel } from '@core/logger';
import { injectOnline$ } from '../services/network-status.service';
@@ -11,7 +11,6 @@ import { injectOnline$ } from '../services/network-status.service';
@Injectable()
export class HttpErrorInterceptor implements HttpInterceptor {
readonly offline$ = injectOnline$().pipe(filter((online) => !online));
readonly injector = inject(Injector);
constructor(
private _modal: UiModalService,
@@ -28,9 +27,18 @@ export class HttpErrorInterceptor implements HttpInterceptor {
handleError(error: HttpErrorResponse): Observable<any> {
if (error.status === 401) {
const strategy = this.injector.get(LoginStrategy);
return from(strategy.login('Sie sind nicht mehr angemeldet')).pipe(mergeMap(() => NEVER));
return this._modal
.open({
content: UiMessageModalComponent,
title: 'Sie sind nicht mehr angemeldet',
data: { message: 'Sie werden neu angemeldet' },
})
.afterClosed$.pipe(
tap(() => {
this._auth.login();
}),
mergeMap(() => NEVER),
);
}
if (!error.url.endsWith('/isa/logging')) {

View File

@@ -4,7 +4,6 @@ import { Component, ChangeDetectionStrategy } from '@angular/core';
selector: 'app-main',
templateUrl: 'main.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: false,
})
export class MainComponent {
constructor() {}

View File

@@ -2,7 +2,7 @@ import { Platform, PlatformModule } from '@angular/cdk/platform';
import { CommonModule } from '@angular/common';
import { Component, OnInit } from '@angular/core';
import { BranchSelectorComponent } from '@shared/components/branch-selector';
import { BranchDTO } from '@generated/swagger/checkout-api';
import { BranchDTO } from '@swagger/checkout';
import { BehaviorSubject } from 'rxjs';
@Component({
@@ -10,6 +10,7 @@ import { BehaviorSubject } from 'rxjs';
templateUrl: 'preview.component.html',
styleUrls: ['preview.component.css'],
imports: [CommonModule, BranchSelectorComponent, PlatformModule],
standalone: true,
})
export class PreviewComponent implements OnInit {
selectedBranch$ = new BehaviorSubject<BranchDTO>({});

View File

@@ -1,6 +1,6 @@
import { Injectable, Injector } from '@angular/core';
import { LogLevel, LogProvider } from '@core/logger';
import { UserStateService } from '@generated/swagger/isa-api';
import { UserStateService } from '@swagger/isa';
import { environment } from '../../environments/environment';
@Injectable({ providedIn: 'root' })

View File

@@ -4,8 +4,9 @@ import { Observable } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class ProcessIdResolver {
constructor() {}
resolve(route: ActivatedRouteSnapshot): Observable<number> | Promise<number> | number {
return route.params.processId;
}
}

View File

@@ -4,10 +4,7 @@ import { ApplicationService } from '@core/application';
import { Observable } from 'rxjs';
export abstract class SectionResolver {
constructor(
protected section: 'customer' | 'branch',
protected applicationService: ApplicationService,
) {}
constructor(protected section: 'customer' | 'branch', protected applicationService: ApplicationService) {}
resolve(route: ActivatedRouteSnapshot): Observable<string> | Promise<string> | string {
this.applicationService.setSection(this.section);

View File

@@ -1 +0,0 @@
export * from './network-status.service';

View File

@@ -1,32 +1,28 @@
import { Injectable } from '@angular/core';
import { Logger, LogLevel } from '@core/logger';
import { Store } from '@ngrx/store';
import { UserStateService } from '@swagger/isa';
import { debounceTime, switchMap, takeUntil } from 'rxjs/operators';
import { RootState } from './root.state';
import packageInfo from 'packageJson';
import { environment } from '../../environments/environment';
import { Subject } from 'rxjs';
import { AuthService } from '@core/auth';
import { injectStorage, UserStorageProvider } from '@isa/core/storage';
import { isEqual } from 'lodash';
@Injectable({ providedIn: 'root' })
export class RootStateService {
static LOCAL_STORAGE_KEY = 'ISA_APP_INITIALSTATE';
#storage = injectStorage(UserStorageProvider);
private _cancelSave = new Subject<void>();
constructor(
private readonly _authService: AuthService,
private readonly _userStateService: UserStateService,
private _logger: Logger,
private _store: Store,
) {
if (!environment.production) {
console.log(
'Die UserState kann in der Konsole mit der Funktion "clearUserState()" geleert werden.',
);
console.log('Die UserState kann in der Konsole mit der Funktion "clearUserState()" geleert werden.');
}
window['clearUserState'] = () => {
@@ -47,17 +43,9 @@ export class RootStateService {
takeUntil(this._cancelSave),
debounceTime(1000),
switchMap((state) => {
const data = {
...state,
version: packageInfo.version,
sub: this._authService.getClaimByKey('sub'),
};
RootStateService.SaveToLocalStorageRaw(JSON.stringify(data));
return this.#storage.set('state', {
...state,
version: packageInfo.version,
sub: this._authService.getClaimByKey('sub'),
});
const raw = JSON.stringify({ ...state, version: packageInfo.version, sub: this._authService.getClaimByKey('sub') });
RootStateService.SaveToLocalStorageRaw(raw);
return this._userStateService.UserStateSetUserState({ content: raw });
}),
)
.subscribe();
@@ -68,15 +56,16 @@ export class RootStateService {
*/
async load(): Promise<boolean> {
try {
const res = await this.#storage.get('state');
const res = await this._userStateService.UserStateGetUserState().toPromise();
const resContent = res?.result?.content ?? null;
const storageContent = RootStateService.LoadFromLocalStorageRaw();
if (res) {
RootStateService.SaveToLocalStorageRaw(JSON.stringify(res));
if (resContent) {
RootStateService.SaveToLocalStorageRaw(res.result.content);
}
if (!isEqual(res, storageContent)) {
if (resContent !== storageContent) {
return true;
}
} catch (error) {
@@ -88,7 +77,7 @@ export class RootStateService {
async clear() {
try {
this._cancelSave.next();
await this.#storage.clear('state');
await this._userStateService.UserStateResetUserState().toPromise();
await new Promise((resolve) => setTimeout(resolve, 100));
RootStateService.RemoveFromLocalStorage();
await new Promise((resolve) => setTimeout(resolve, 100));

View File

@@ -7,14 +7,9 @@ import { AuthService } from '@core/auth';
templateUrl: 'token-login.component.html',
styleUrls: ['token-login.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: false,
})
export class TokenLoginComponent implements OnInit {
constructor(
private _route: ActivatedRoute,
private _authService: AuthService,
private _router: Router,
) {}
constructor(private _route: ActivatedRoute, private _authService: AuthService, private _router: Router) {}
ngOnInit() {
if (this._route.snapshot.params.token && !this._authService.isAuthenticated()) {

View File

@@ -1,222 +1,219 @@
/* cyrillic-ext */
@font-face {
font-family: "Open Sans";
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
font-stretch: normal;
src: url("./Open_Sans-400-cyrillic-ext1.woff2") format("woff2");
src: url('./Open_Sans-400-cyrillic-ext1.woff2') format('woff2');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: "Open Sans";
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
font-stretch: normal;
src: url("./Open_Sans-400-cyrillic2.woff2") format("woff2");
src: url('./Open_Sans-400-cyrillic2.woff2') format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: "Open Sans";
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
font-stretch: normal;
src: url("./Open_Sans-400-greek-ext3.woff2") format("woff2");
src: url('./Open_Sans-400-greek-ext3.woff2') format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: "Open Sans";
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
font-stretch: normal;
src: url("./Open_Sans-400-greek4.woff2") format("woff2");
src: url('./Open_Sans-400-greek4.woff2') format('woff2');
unicode-range: U+0370-03FF;
}
/* hebrew */
@font-face {
font-family: "Open Sans";
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
font-stretch: normal;
src: url("./Open_Sans-400-hebrew5.woff2") format("woff2");
src: url('./Open_Sans-400-hebrew5.woff2') format('woff2');
unicode-range: U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
}
/* vietnamese */
@font-face {
font-family: "Open Sans";
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
font-stretch: normal;
src: url("./Open_Sans-400-vietnamese6.woff2") format("woff2");
src: url('./Open_Sans-400-vietnamese6.woff2') format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: "Open Sans";
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
font-stretch: normal;
src: url("./Open_Sans-400-latin-ext7.woff2") format("woff2");
src: url('./Open_Sans-400-latin-ext7.woff2') format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: "Open Sans";
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
font-stretch: normal;
src: url("./Open_Sans-400-latin8.woff2") format("woff2");
unicode-range:
U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,
U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
src: url('./Open_Sans-400-latin8.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193,
U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: "Open Sans";
font-family: 'Open Sans';
font-style: normal;
font-weight: 600;
font-stretch: normal;
src: url("./Open_Sans-600-cyrillic-ext9.woff2") format("woff2");
src: url('./Open_Sans-600-cyrillic-ext9.woff2') format('woff2');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: "Open Sans";
font-family: 'Open Sans';
font-style: normal;
font-weight: 600;
font-stretch: normal;
src: url("./Open_Sans-600-cyrillic10.woff2") format("woff2");
src: url('./Open_Sans-600-cyrillic10.woff2') format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: "Open Sans";
font-family: 'Open Sans';
font-style: normal;
font-weight: 600;
font-stretch: normal;
src: url("./Open_Sans-600-greek-ext11.woff2") format("woff2");
src: url('./Open_Sans-600-greek-ext11.woff2') format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: "Open Sans";
font-family: 'Open Sans';
font-style: normal;
font-weight: 600;
font-stretch: normal;
src: url("./Open_Sans-600-greek12.woff2") format("woff2");
src: url('./Open_Sans-600-greek12.woff2') format('woff2');
unicode-range: U+0370-03FF;
}
/* hebrew */
@font-face {
font-family: "Open Sans";
font-family: 'Open Sans';
font-style: normal;
font-weight: 600;
font-stretch: normal;
src: url("./Open_Sans-600-hebrew13.woff2") format("woff2");
src: url('./Open_Sans-600-hebrew13.woff2') format('woff2');
unicode-range: U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
}
/* vietnamese */
@font-face {
font-family: "Open Sans";
font-family: 'Open Sans';
font-style: normal;
font-weight: 600;
font-stretch: normal;
src: url("./Open_Sans-600-vietnamese14.woff2") format("woff2");
src: url('./Open_Sans-600-vietnamese14.woff2') format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: "Open Sans";
font-family: 'Open Sans';
font-style: normal;
font-weight: 600;
font-stretch: normal;
src: url("./Open_Sans-600-latin-ext15.woff2") format("woff2");
src: url('./Open_Sans-600-latin-ext15.woff2') format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: "Open Sans";
font-family: 'Open Sans';
font-style: normal;
font-weight: 600;
font-stretch: normal;
src: url("./Open_Sans-600-latin16.woff2") format("woff2");
unicode-range:
U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,
U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
src: url('./Open_Sans-600-latin16.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193,
U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: "Open Sans";
font-family: 'Open Sans';
font-style: normal;
font-weight: 700;
font-stretch: normal;
src: url("./Open_Sans-700-cyrillic-ext17.woff2") format("woff2");
src: url('./Open_Sans-700-cyrillic-ext17.woff2') format('woff2');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: "Open Sans";
font-family: 'Open Sans';
font-style: normal;
font-weight: 700;
font-stretch: normal;
src: url("./Open_Sans-700-cyrillic18.woff2") format("woff2");
src: url('./Open_Sans-700-cyrillic18.woff2') format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: "Open Sans";
font-family: 'Open Sans';
font-style: normal;
font-weight: 700;
font-stretch: normal;
src: url("./Open_Sans-700-greek-ext19.woff2") format("woff2");
src: url('./Open_Sans-700-greek-ext19.woff2') format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: "Open Sans";
font-family: 'Open Sans';
font-style: normal;
font-weight: 700;
font-stretch: normal;
src: url("./Open_Sans-700-greek20.woff2") format("woff2");
src: url('./Open_Sans-700-greek20.woff2') format('woff2');
unicode-range: U+0370-03FF;
}
/* hebrew */
@font-face {
font-family: "Open Sans";
font-family: 'Open Sans';
font-style: normal;
font-weight: 700;
font-stretch: normal;
src: url("./Open_Sans-700-hebrew21.woff2") format("woff2");
src: url('./Open_Sans-700-hebrew21.woff2') format('woff2');
unicode-range: U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
}
/* vietnamese */
@font-face {
font-family: "Open Sans";
font-family: 'Open Sans';
font-style: normal;
font-weight: 700;
font-stretch: normal;
src: url("./Open_Sans-700-vietnamese22.woff2") format("woff2");
src: url('./Open_Sans-700-vietnamese22.woff2') format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: "Open Sans";
font-family: 'Open Sans';
font-style: normal;
font-weight: 700;
font-stretch: normal;
src: url("./Open_Sans-700-latin-ext23.woff2") format("woff2");
src: url('./Open_Sans-700-latin-ext23.woff2') format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: "Open Sans";
font-family: 'Open Sans';
font-style: normal;
font-weight: 700;
font-stretch: normal;
src: url("./Open_Sans-700-latin24.woff2") format("woff2");
unicode-range:
U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,
U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
src: url('./Open_Sans-700-latin24.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193,
U+2212, U+2215, U+FEFF, U+FFFD;
}

View File

@@ -1,85 +1,86 @@
{
"title": "ISA - Feature",
"silentRefresh": {
"interval": 300000
},
"@cdn/product-image": {
"url": "https://produktbilder.paragon-data.net"
},
"@core/auth": {
"issuer": "https://sso-test.paragon-data.de",
"clientId": "hug-isa",
"responseType": "id_token token",
"oidc": true,
"scope": "openid profile cmf_user isa-isa-webapi isa-checkout-webapi isa-cat-webapi isa-ava-webapi isa-crm-webapi isa-review-webapi isa-kpi-webapi isa-oms-webapi isa-nbo-webapi isa-print-webapi eis-service isa-inv-webapi isa-wws-webapi"
},
"@core/logger": {
"logLevel": "debug"
},
"@domain/checkout": {
"olaExpiration": "5m"
},
"@swagger/isa": {
"rootUrl": "https://isa-test.paragon-data.net/isa/v1"
},
"@swagger/cat": {
"rootUrl": "https://isa-test.paragon-data.net/catsearch/v6"
},
"@swagger/av": {
"rootUrl": "https://isa-test.paragon-data.net/ava/v6"
},
"@swagger/checkout": {
"rootUrl": "https://isa-feature.paragon-data.net/checkout/v6"
},
"@swagger/crm": {
"rootUrl": "https://isa-feature.paragon-data.net/crm/v6"
},
"@swagger/oms": {
"rootUrl": "https://isa-feature.paragon-data.net/oms/v6"
},
"@swagger/print": {
"rootUrl": "https://isa-test.paragon-data.net/print/v1"
},
"@swagger/eis": {
"rootUrl": "https://filialinformationsystem-test.paragon-systems.de/eiswebapi/v1"
},
"@swagger/remi": {
"rootUrl": "https://isa-test.paragon-data.net/inv/v6"
},
"@swagger/wws": {
"rootUrl": "https://isa-test.paragon-data.net/wws/v1"
},
"hubs": {
"notifications": {
"url": "https://isa-test.paragon-data.net/isa/v1/rt",
"enableAutomaticReconnect": false,
"httpOptions": {
"transport": 1,
"logMessageContent": true,
"skipNegotiation": true
}
}
},
"process": {
"ids": {
"goodsOut": 1000,
"goodsIn": 2000,
"taskCalendar": 3000,
"remission": 4000,
"packageInspection": 5000,
"assortment": 6000,
"pickupShelf": 7000
}
},
"checkForUpdates": 3600000,
"licence": {
"scandit": "Ae8F2Wx2RMq5Lvn7UUAlWzVFZTt2+ubMAF8XtDpmPlNkBeG/LWs1M7AbgDW0LQqYLnszClEENaEHS56/6Ts2vrJ1Ux03CXUjK3jUvZpF5OchXR1CpnmpepJ6WxPCd7LMVHUGG1BbwPLDTFjP3y8uT0caTSmmGrYQWAs4CZcEF+ZBabP0z7vfm+hCZF/ebj9qqCJZcW8nH/n19hohshllzYBjFXjh87P2lIh1s6yZS3OaQWWXo/o0AKdxx7T6CVyR0/G5zq6uYJWf6rs3euUBEhpzOZHbHZK86Lvy2AVBEyVkkcttlDW1J2fA4l1W1JV/Xibz8AQV6kG482EpGF42KEoK48paZgX3e1AQsqUtmqzw294dcP4zMVstnw5/WrwKKi/5E/nOOJT2txYP1ZufIjPrwNFsqTlv7xCQlHjMzFGYwT816yD5qLRLbwOtjrkUPXNZLZ06T4upvWwJDmm8XgdeoDqMjHdcO4lwji1bl9EiIYJ/2qnsk9yZ2FqSaHzn4cbiL0f5u2HFlNAP0GUujGRlthGhHi6o4dFU+WAxKsFMKVt+SfoQUazNKHFVQgiAklTIZxIc/HUVzRvOLMxf+wFDerraBtcqGJg+g/5mrWYqeDBGhCBHtKiYf6244IJ4afzNTiH1/30SJcRzXwbEa3A7q1fJTx9/nLTOfVPrJKBQs7f/OQs2dA7LDCel8mzXdbjvsNQaeU5+iCIAq6zbTNKy1xT8wwj+VZrQmtNJs+qeznD+u29nCM24h8xCmRpvNPo4/Mww/lrTNrrNwLBSn1pMIwsH7yS9hH0v0oNAM3A6bVtk1D9qEkbyw+xZa+MZGpMP0D0CdcsqHalPcm5r/Ik="
},
"gender": {
"0": "Keine Anrede",
"1": "Enby",
"2": "Herr",
"4": "Frau"
},
"@shared/icon": "/assets/icons.json"
}
"title": "ISA - Feature",
"silentRefresh": {
"interval": 300000
},
"@cdn/product-image": {
"url": "https://produktbilder.paragon-data.net"
},
"@core/auth": {
"issuer": "https://sso-test.paragon-data.de",
"clientId": "hug-isa",
"responseType": "id_token token",
"oidc": true,
"scope": "openid profile cmf_user isa-isa-webapi isa-checkout-webapi isa-cat-webapi isa-ava-webapi isa-crm-webapi isa-review-webapi isa-kpi-webapi isa-oms-webapi isa-nbo-webapi isa-print-webapi eis-service isa-inv-webapi isa-wws-webapi"
},
"@core/logger": {
"logLevel": "debug"
},
"@domain/checkout": {
"olaExpiration": "5m"
},
"@swagger/isa": {
"rootUrl": "https://isa-test.paragon-data.net/isa/v1"
},
"@swagger/cat": {
"rootUrl": "https://isa-test.paragon-data.net/catsearch/v6"
},
"@swagger/av": {
"rootUrl": "https://isa-test.paragon-data.net/ava/v6"
},
"@swagger/checkout": {
"rootUrl": "https://isa-test.paragon-data.net/checkout/v6"
},
"@swagger/crm": {
"rootUrl": "https://isa-test.paragon-data.net/crm/v6"
},
"@swagger/oms": {
"rootUrl": "https://isa-test.paragon-data.net/oms/v6"
},
"@swagger/print": {
"rootUrl": "https://isa-test.paragon-data.net/print/v1"
},
"@swagger/eis": {
"rootUrl": "https://filialinformationsystem-test.paragon-systems.de/eiswebapi/v1"
},
"@swagger/remi": {
"rootUrl": "https://isa-test.paragon-data.net/inv/v6"
},
"@swagger/wws": {
"rootUrl": "https://isa-test.paragon-data.net/wws/v1"
},
"hubs": {
"notifications": {
"url": "https://isa-test.paragon-data.net/isa/v1/rt",
"enableAutomaticReconnect": false,
"httpOptions": {
"transport": 1,
"logMessageContent": true,
"skipNegotiation": true
}
}
},
"process": {
"ids": {
"goodsOut": 1000,
"goodsIn": 2000,
"taskCalendar": 3000,
"remission": 4000,
"packageInspection": 5000,
"assortment": 6000,
"pickupShelf": 7000
}
},
"checkForUpdates": 3600000,
"licence": {
"scandit": "ApI1Sg5/KzWICPhn0CbflI4cjgXiFkKnhgEmUvAt0D3CWsck0SCIm557hJvGTqXD0A1mt3IvEwA8eQ1CBXL3EoA9lASWUcLt+FSWP9gHao+LT6Oz9QMrsJoJRC/OboodKS4/F+ZVa02eUT53X0GX9jJeEnh8EerqZimrl6oDjNLvHCzBnxtmsMZidAUaErqK5yaQQCkUKcfTQhRzVhqvsjdENR76EmrrvCSr93oMCnnDHjcyOz8O3stcNrCOGbxwDBIMwf05CzoEBhxF9SoT/fwvfWa9BRvlMTxl9lUUE2f3Aw9+0CBG7PYgZ9XGCiCM2Wy9T5cf0C5MOLr2MAIgQrlDua0oEtmC5FjFojQHJEITRmLyQnAyUU8Y+jkUUCtQNmweZKtTQT9le+G9QCzRoSsmVsVxCKxII3+8iLA+Lh9meI/vJSaQ2VpDgZVrV1Vc4V/z0Exu7Ap7R0MpzCy4hhRzdpWgf4OG815ZctwMvM7qfBqykG98B9oP3YC/LG+cpVK4Ipx5VnWBUIwVHDiocHhMjyroTBAeH3BfeCdOEpKwNK9vOxrbvUpqAc8ZOaaal08hWgFVFR8BQN+rEkiqKx5fONRXefkkyUWMdqwvoBn7ZzCr+F9GmD52Q7ZuDcyAigm2ArwDipT9QUH7gxphogcKRNAWAyaENWWKRBFWmeNYXJOuGXLr05Zye7joGFbJHxPYyUJ6KMombmYlI2GHc4JQGJNneDuL93v8AkYcfM2WfOjf0hm+SVw0TP95bD1xJwSCoKO5L6FxqMu5GPcOqik3sjGHg6XsEr10m7jt7G2PfNTgS3ft+TP32OjeSu9M80Fu3nmeqLM4a4lx9fFbGMpWDEU9wdftmPByeW7KoXyS2bkd1Mg9UP8ci0nKo2d/ThPn+iMEjXSibNeIGibaIT4ys3pjDLW2mx0IxQpwnuQfZzAHBszpMkwKuG4+FrqXff9+bZGuRQlVwhm2v5xbKRjgi99i14QkuNVUkePxllLZGFgQH0DjsLINZb+R9stBYjQmMGc2kkJfyhHx2/zvT2D2mpgcaq0ZRKco2CqDiLUJoHwLPse/fu4PWvqSKVpwiqhCehuloWTE3jkFUncwDI50hPvrfCx92cOJUE8RTsQgDoAh7JKsPHctSZfUicZyLHOuHMhtIoxs6l/lzFowbhZob/Uo7aRsipOcP/iZgEPnMEu/obXcLR3QrlQ+WuEppU4g2MZ9+xENLCqVDLEHiz+jZIvl42ePc4Ip4lJ5RQFcSSR1yYb+z/4KB/3bc308PHnEMEB5TYvVpK7M3kg2CTjos5lh733qQKwqYamgFjpr5oPUxRYwRvj41GYpcLN3Tr4WOi4lfevR5eVFBu1Jc9baY8JIOvcCL5WhXsEemnx5dUhcnP2ozloFCvU1IS60KHkXtF4NJl89tu5EECquTN5Lf9cvtIhlbmPsVg=="
},
"gender": {
"0": "Keine Anrede",
"1": "Enby",
"2": "Herr",
"4": "Frau"
},
"@shared/icon": "/assets/icons.json"
}

View File

@@ -17,7 +17,7 @@
},
"@domain/checkout": {
"olaExpiration": "5m"
},
},
"@swagger/isa": {
"rootUrl": "https://isa-integration.paragon-data.net/isa/v1"
},
@@ -72,7 +72,7 @@
},
"checkForUpdates": 900000,
"licence": {
"scandit": "ASq1ZCbuDs0nJ4cPGkFOSkQEBHSL/kuljVAq2ttwz2Q5CEwULWAlasJIkjsIe/nptzNx1IBSxwDPWh0gXmvF08BRBDTzDUELdX6q02N/HUddSHE4A1dMXoFg+I8of+iZQijQIogxmP5RfSpj6ixzD2cGV0w+BG6o9xRXG4lEv9vNAOC3+emfcuVj2Whmfbuzao4HcWweVSz4HJh8dcbxTPF10MH8qR4kkV7fNWIF+PaYww69relfuvrkgdbWPcLs4Xb7WW4AISX9pTmJmSuv2LvD9gMoPNlmhdTWi+qFcmgZiPRDb1I0Bldo7jKLdboylEXkVQVWF1D2fv3iO5BrijG+n52XQd3ew49/1ssJi6dWhxiZQMlN+mz7cfSPmGzJc0eqaf52tEA8PkIupXjly/6dckSVcqTsex+wHcQuTinyW2bu9/YY3ZTMCp5du+lFZBD+AQSsq7EALzzithpijNG3jQka/8yRSHPBP6JtfobMdbv7SlAESslnN5R5lKJms4HnSm4CwxBAX2sUrhQeuit74/5pwe8qI+CaEGzjKYfmcRhFXd+Kz/ySV3HyrVuieH6+6ytBjGh3EkwGvQ8Ovu4KsruWAyAjHwRyBdrFIRYyLWdou77VH8gSLwHi69QsMGshcnYaREpqDEb5LLR9Ok7jP5IjMyPzX4USS327G9f4I8ujTaCKXQ9a384yV/wP9IeSuJld4dgdGy80rDP2sWZ+NJ65cNWpAfi/Nmbm6ucjICoUHILtchyx/w/YNYUzfCWFjUWHn83TzG+31f3dkjWoI3XpCOg5RKREs55htHSAyZtYhYiQr1FxDVFNEQI2MrDR0fBt2qOeZB08yA=="
"scandit": "ApI1Sg5/KzWICPhn0CbflI4cjgXiFkKnhgEmUvAt0D3CWsck0SCIm557hJvGTqXD0A1mt3IvEwA8eQ1CBXL3EoA9lASWUcLt+FSWP9gHao+LT6Oz9QMrsJoJRC/OboodKS4/F+ZVa02eUT53X0GX9jJeEnh8EerqZimrl6oDjNLvHCzBnxtmsMZidAUaErqK5yaQQCkUKcfTQhRzVhqvsjdENR76EmrrvCSr93oMCnnDHjcyOz8O3stcNrCOGbxwDBIMwf05CzoEBhxF9SoT/fwvfWa9BRvlMTxl9lUUE2f3Aw9+0CBG7PYgZ9XGCiCM2Wy9T5cf0C5MOLr2MAIgQrlDua0oEtmC5FjFojQHJEITRmLyQnAyUU8Y+jkUUCtQNmweZKtTQT9le+G9QCzRoSsmVsVxCKxII3+8iLA+Lh9meI/vJSaQ2VpDgZVrV1Vc4V/z0Exu7Ap7R0MpzCy4hhRzdpWgf4OG815ZctwMvM7qfBqykG98B9oP3YC/LG+cpVK4Ipx5VnWBUIwVHDiocHhMjyroTBAeH3BfeCdOEpKwNK9vOxrbvUpqAc8ZOaaal08hWgFVFR8BQN+rEkiqKx5fONRXefkkyUWMdqwvoBn7ZzCr+F9GmD52Q7ZuDcyAigm2ArwDipT9QUH7gxphogcKRNAWAyaENWWKRBFWmeNYXJOuGXLr05Zye7joGFbJHxPYyUJ6KMombmYlI2GHc4JQGJNneDuL93v8AkYcfM2WfOjf0hm+SVw0TP95bD1xJwSCoKO5L6FxqMu5GPcOqik3sjGHg6XsEr10m7jt7G2PfNTgS3ft+TP32OjeSu9M80Fu3nmeqLM4a4lx9fFbGMpWDEU9wdftmPByeW7KoXyS2bkd1Mg9UP8ci0nKo2d/ThPn+iMEjXSibNeIGibaIT4ys3pjDLW2mx0IxQpwnuQfZzAHBszpMkwKuG4+FrqXff9+bZGuRQlVwhm2v5xbKRjgi99i14QkuNVUkePxllLZGFgQH0DjsLINZb+R9stBYjQmMGc2kkJfyhHx2/zvT2D2mpgcaq0ZRKco2CqDiLUJoHwLPse/fu4PWvqSKVpwiqhCehuloWTE3jkFUncwDI50hPvrfCx92cOJUE8RTsQgDoAh7JKsPHctSZfUicZyLHOuHMhtIoxs6l/lzFowbhZob/Uo7aRsipOcP/iZgEPnMEu/obXcLR3QrlQ+WuEppU4g2MZ9+xENLCqVDLEHiz+jZIvl42ePc4Ip4lJ5RQFcSSR1yYb+z/4KB/3bc308PHnEMEB5TYvVpK7M3kg2CTjos5lh733qQKwqYamgFjpr5oPUxRYwRvj41GYpcLN3Tr4WOi4lfevR5eVFBu1Jc9baY8JIOvcCL5WhXsEemnx5dUhcnP2ozloFCvU1IS60KHkXtF4NJl89tu5EECquTN5Lf9cvtIhlbmPsVg=="
},
"gender": {
"0": "Keine Anrede",
@@ -81,4 +81,4 @@
"4": "Frau"
},
"@shared/icon": "/assets/icons.json"
}
}

View File

@@ -1,87 +1,86 @@
{
"title": "ISA - Local",
"silentRefresh": {
"interval": 300000
},
"debug": true,
"dev-scanner": true,
"@cdn/product-image": {
"url": "https://produktbilder.paragon-data.net"
},
"@core/auth": {
"issuer": "https://sso-test.paragon-data.de",
"clientId": "isa-client",
"responseType": "code",
"scope": "openid profile cmf_user isa-isa-webapi isa-checkout-webapi isa-cat-webapi isa-ava-webapi isa-crm-webapi isa-review-webapi isa-kpi-webapi isa-oms-webapi isa-nbo-webapi isa-print-webapi eis-service isa-inv-webapi isa-wws-webapi",
"showDebugInformation": true
},
"@core/logger": {
"logLevel": "debug"
},
"@swagger/isa": {
"rootUrl": "https://isa-test.paragon-data.net/isa/v1"
},
"@swagger/cat": {
"rootUrl": "https://isa-test.paragon-data.net/catsearch/v6"
},
"@swagger/av": {
"rootUrl": "https://isa-test.paragon-data.net/ava/v6"
},
"@swagger/checkout": {
"rootUrl": "https://isa-test.paragon-data.net/checkout/v6"
},
"@swagger/crm": {
"rootUrl": "https://isa-test.paragon-data.net/crm/v6"
},
"@swagger/oms": {
"rootUrl": "https://isa-test.paragon-data.net/oms/v6"
},
"@swagger/print": {
"rootUrl": "https://isa-test.paragon-data.net/print/v1"
},
"@swagger/eis": {
"rootUrl": "https://filialinformationsystem-test.paragon-systems.de/eiswebapi/v1"
},
"@swagger/remi": {
"rootUrl": "https://isa-test.paragon-data.net/inv/v6"
},
"@swagger/wws": {
"rootUrl": "https://isa-test.paragon-data.net/wws/v1"
},
"@domain/checkout": {
"olaExpiration": "5m"
},
"hubs": {
"notifications": {
"url": "https://isa-test.paragon-data.net/isa/v1/rt",
"enableAutomaticReconnect": false,
"httpOptions": {
"transport": 1,
"logMessageContent": true,
"skipNegotiation": true
}
}
},
"process": {
"ids": {
"goodsOut": 1000,
"goodsIn": 2000,
"taskCalendar": 3000,
"remission": 4000,
"packageInspection": 5000,
"assortment": 6000,
"pickupShelf": 7000
}
},
"checkForUpdates": 3600000,
"licence": {
"scandit": "Ae8F2Wx2RMq5Lvn7UUAlWzVFZTt2+ubMAF8XtDpmPlNkBeG/LWs1M7AbgDW0LQqYLnszClEENaEHS56/6Ts2vrJ1Ux03CXUjK3jUvZpF5OchXR1CpnmpepJ6WxPCd7LMVHUGG1BbwPLDTFjP3y8uT0caTSmmGrYQWAs4CZcEF+ZBabP0z7vfm+hCZF/ebj9qqCJZcW8nH/n19hohshllzYBjFXjh87P2lIh1s6yZS3OaQWWXo/o0AKdxx7T6CVyR0/G5zq6uYJWf6rs3euUBEhpzOZHbHZK86Lvy2AVBEyVkkcttlDW1J2fA4l1W1JV/Xibz8AQV6kG482EpGF42KEoK48paZgX3e1AQsqUtmqzw294dcP4zMVstnw5/WrwKKi/5E/nOOJT2txYP1ZufIjPrwNFsqTlv7xCQlHjMzFGYwT816yD5qLRLbwOtjrkUPXNZLZ06T4upvWwJDmm8XgdeoDqMjHdcO4lwji1bl9EiIYJ/2qnsk9yZ2FqSaHzn4cbiL0f5u2HFlNAP0GUujGRlthGhHi6o4dFU+WAxKsFMKVt+SfoQUazNKHFVQgiAklTIZxIc/HUVzRvOLMxf+wFDerraBtcqGJg+g/5mrWYqeDBGhCBHtKiYf6244IJ4afzNTiH1/30SJcRzXwbEa3A7q1fJTx9/nLTOfVPrJKBQs7f/OQs2dA7LDCel8mzXdbjvsNQaeU5+iCIAq6zbTNKy1xT8wwj+VZrQmtNJs+qeznD+u29nCM24h8xCmRpvNPo4/Mww/lrTNrrNwLBSn1pMIwsH7yS9hH0v0oNAM3A6bVtk1D9qEkbyw+xZa+MZGpMP0D0CdcsqHalPcm5r/Ik="
},
"gender": {
"0": "Keine Anrede",
"1": "Enby",
"2": "Herr",
"4": "Frau"
},
"@shared/icon": "/assets/icons.json"
}
"title": "ISA - Local",
"silentRefresh": {
"interval": 300000
},
"debug": true,
"@cdn/product-image": {
"url": "https://produktbilder.paragon-data.net"
},
"@core/auth": {
"issuer": "https://sso-test.paragon-data.de",
"clientId": "isa-client",
"responseType": "code",
"scope": "openid profile cmf_user isa-isa-webapi isa-checkout-webapi isa-cat-webapi isa-ava-webapi isa-crm-webapi isa-review-webapi isa-kpi-webapi isa-oms-webapi isa-nbo-webapi isa-print-webapi eis-service isa-inv-webapi isa-wws-webapi",
"showDebugInformation": true
},
"@core/logger": {
"logLevel": "debug"
},
"@swagger/isa": {
"rootUrl": "https://isa-test.paragon-data.net/isa/v1"
},
"@swagger/cat": {
"rootUrl": "https://isa-test.paragon-data.net/catsearch/v6"
},
"@swagger/av": {
"rootUrl": "https://isa-test.paragon-data.net/ava/v6"
},
"@swagger/checkout": {
"rootUrl": "https://isa-test.paragon-data.net/checkout/v6"
},
"@swagger/crm": {
"rootUrl": "https://isa-test.paragon-data.net/crm/v6"
},
"@swagger/oms": {
"rootUrl": "https://isa-test.paragon-data.net/oms/v6"
},
"@swagger/print": {
"rootUrl": "https://isa-test.paragon-data.net/print/v1"
},
"@swagger/eis": {
"rootUrl": "https://filialinformationsystem-test.paragon-systems.de/eiswebapi/v1"
},
"@swagger/remi": {
"rootUrl": "https://isa-test.paragon-data.net/inv/v6"
},
"@swagger/wws": {
"rootUrl": "https://isa-test.paragon-data.net/wws/v1"
},
"@domain/checkout": {
"olaExpiration": "5m"
},
"hubs": {
"notifications": {
"url": "https://isa-test.paragon-data.net/isa/v1/rt",
"enableAutomaticReconnect": false,
"httpOptions": {
"transport": 1,
"logMessageContent": true,
"skipNegotiation": true
}
}
},
"process": {
"ids": {
"goodsOut": 1000,
"goodsIn": 2000,
"taskCalendar": 3000,
"remission": 4000,
"packageInspection": 5000,
"assortment": 6000,
"pickupShelf": 7000
}
},
"checkForUpdates": 3600000,
"licence": {
"scandit": "ApI1Sg5/KzWICPhn0CbflI4cjgXiFkKnhgEmUvAt0D3CWsck0SCIm557hJvGTqXD0A1mt3IvEwA8eQ1CBXL3EoA9lASWUcLt+FSWP9gHao+LT6Oz9QMrsJoJRC/OboodKS4/F+ZVa02eUT53X0GX9jJeEnh8EerqZimrl6oDjNLvHCzBnxtmsMZidAUaErqK5yaQQCkUKcfTQhRzVhqvsjdENR76EmrrvCSr93oMCnnDHjcyOz8O3stcNrCOGbxwDBIMwf05CzoEBhxF9SoT/fwvfWa9BRvlMTxl9lUUE2f3Aw9+0CBG7PYgZ9XGCiCM2Wy9T5cf0C5MOLr2MAIgQrlDua0oEtmC5FjFojQHJEITRmLyQnAyUU8Y+jkUUCtQNmweZKtTQT9le+G9QCzRoSsmVsVxCKxII3+8iLA+Lh9meI/vJSaQ2VpDgZVrV1Vc4V/z0Exu7Ap7R0MpzCy4hhRzdpWgf4OG815ZctwMvM7qfBqykG98B9oP3YC/LG+cpVK4Ipx5VnWBUIwVHDiocHhMjyroTBAeH3BfeCdOEpKwNK9vOxrbvUpqAc8ZOaaal08hWgFVFR8BQN+rEkiqKx5fONRXefkkyUWMdqwvoBn7ZzCr+F9GmD52Q7ZuDcyAigm2ArwDipT9QUH7gxphogcKRNAWAyaENWWKRBFWmeNYXJOuGXLr05Zye7joGFbJHxPYyUJ6KMombmYlI2GHc4JQGJNneDuL93v8AkYcfM2WfOjf0hm+SVw0TP95bD1xJwSCoKO5L6FxqMu5GPcOqik3sjGHg6XsEr10m7jt7G2PfNTgS3ft+TP32OjeSu9M80Fu3nmeqLM4a4lx9fFbGMpWDEU9wdftmPByeW7KoXyS2bkd1Mg9UP8ci0nKo2d/ThPn+iMEjXSibNeIGibaIT4ys3pjDLW2mx0IxQpwnuQfZzAHBszpMkwKuG4+FrqXff9+bZGuRQlVwhm2v5xbKRjgi99i14QkuNVUkePxllLZGFgQH0DjsLINZb+R9stBYjQmMGc2kkJfyhHx2/zvT2D2mpgcaq0ZRKco2CqDiLUJoHwLPse/fu4PWvqSKVpwiqhCehuloWTE3jkFUncwDI50hPvrfCx92cOJUE8RTsQgDoAh7JKsPHctSZfUicZyLHOuHMhtIoxs6l/lzFowbhZob/Uo7aRsipOcP/iZgEPnMEu/obXcLR3QrlQ+WuEppU4g2MZ9+xENLCqVDLEHiz+jZIvl42ePc4Ip4lJ5RQFcSSR1yYb+z/4KB/3bc308PHnEMEB5TYvVpK7M3kg2CTjos5lh733qQKwqYamgFjpr5oPUxRYwRvj41GYpcLN3Tr4WOi4lfevR5eVFBu1Jc9baY8JIOvcCL5WhXsEemnx5dUhcnP2ozloFCvU1IS60KHkXtF4NJl89tu5EECquTN5Lf9cvtIhlbmPsVg=="
},
"gender": {
"0": "Keine Anrede",
"1": "Enby",
"2": "Herr",
"4": "Frau"
},
"@shared/icon": "/assets/icons.json"
}

View File

@@ -3,8 +3,7 @@
"silentRefresh": {
"interval": 300000
},
"debug": false,
"debug": true,
"@cdn/product-image": {
"url": "https://produktbilder.paragon-data.net"
},
@@ -75,8 +74,7 @@
},
"checkForUpdates": 3600000,
"licence": {
"scandit": "Ae8F2Wx2RMq5Lvn7UUAlWzVFZTt2+ubMAF8XtDpmPlNkBeG/LWs1M7AbgDW0LQqYLnszClEENaEHS56/6Ts2vrJ1Ux03CXUjK3jUvZpF5OchXR1CpnmpepJ6WxPCd7LMVHUGG1BbwPLDTFjP3y8uT0caTSmmGrYQWAs4CZcEF+ZBabP0z7vfm+hCZF/ebj9qqCJZcW8nH/n19hohshllzYBjFXjh87P2lIh1s6yZS3OaQWWXo/o0AKdxx7T6CVyR0/G5zq6uYJWf6rs3euUBEhpzOZHbHZK86Lvy2AVBEyVkkcttlDW1J2fA4l1W1JV/Xibz8AQV6kG482EpGF42KEoK48paZgX3e1AQsqUtmqzw294dcP4zMVstnw5/WrwKKi/5E/nOOJT2txYP1ZufIjPrwNFsqTlv7xCQlHjMzFGYwT816yD5qLRLbwOtjrkUPXNZLZ06T4upvWwJDmm8XgdeoDqMjHdcO4lwji1bl9EiIYJ/2qnsk9yZ2FqSaHzn4cbiL0f5u2HFlNAP0GUujGRlthGhHi6o4dFU+WAxKsFMKVt+SfoQUazNKHFVQgiAklTIZxIc/HUVzRvOLMxf+wFDerraBtcqGJg+g/5mrWYqeDBGhCBHtKiYf6244IJ4afzNTiH1/30SJcRzXwbEa3A7q1fJTx9/nLTOfVPrJKBQs7f/OQs2dA7LDCel8mzXdbjvsNQaeU5+iCIAq6zbTNKy1xT8wwj+VZrQmtNJs+qeznD+u29nCM24h8xCmRpvNPo4/Mww/lrTNrrNwLBSn1pMIwsH7yS9hH0v0oNAM3A6bVtk1D9qEkbyw+xZa+MZGpMP0D0CdcsqHalPcm5r/Ik="
},
"scandit": "ApI1Sg5/KzWICPhn0CbflI4cjgXiFkKnhgEmUvAt0D3CWsck0SCIm557hJvGTqXD0A1mt3IvEwA8eQ1CBXL3EoA9lASWUcLt+FSWP9gHao+LT6Oz9QMrsJoJRC/OboodKS4/F+ZVa02eUT53X0GX9jJeEnh8EerqZimrl6oDjNLvHCzBnxtmsMZidAUaErqK5yaQQCkUKcfTQhRzVhqvsjdENR76EmrrvCSr93oMCnnDHjcyOz8O3stcNrCOGbxwDBIMwf05CzoEBhxF9SoT/fwvfWa9BRvlMTxl9lUUE2f3Aw9+0CBG7PYgZ9XGCiCM2Wy9T5cf0C5MOLr2MAIgQrlDua0oEtmC5FjFojQHJEITRmLyQnAyUU8Y+jkUUCtQNmweZKtTQT9le+G9QCzRoSsmVsVxCKxII3+8iLA+Lh9meI/vJSaQ2VpDgZVrV1Vc4V/z0Exu7Ap7R0MpzCy4hhRzdpWgf4OG815ZctwMvM7qfBqykG98B9oP3YC/LG+cpVK4Ipx5VnWBUIwVHDiocHhMjyroTBAeH3BfeCdOEpKwNK9vOxrbvUpqAc8ZOaaal08hWgFVFR8BQN+rEkiqKx5fONRXefkkyUWMdqwvoBn7ZzCr+F9GmD52Q7ZuDcyAigm2ArwDipT9QUH7gxphogcKRNAWAyaENWWKRBFWmeNYXJOuGXLr05Zye7joGFbJHxPYyUJ6KMombmYlI2GHc4JQGJNneDuL93v8AkYcfM2WfOjf0hm+SVw0TP95bD1xJwSCoKO5L6FxqMu5GPcOqik3sjGHg6XsEr10m7jt7G2PfNTgS3ft+TP32OjeSu9M80Fu3nmeqLM4a4lx9fFbGMpWDEU9wdftmPByeW7KoXyS2bkd1Mg9UP8ci0nKo2d/ThPn+iMEjXSibNeIGibaIT4ys3pjDLW2mx0IxQpwnuQfZzAHBszpMkwKuG4+FrqXff9+bZGuRQlVwhm2v5xbKRjgi99i14QkuNVUkePxllLZGFgQH0DjsLINZb+R9stBYjQmMGc2kkJfyhHx2/zvT2D2mpgcaq0ZRKco2CqDiLUJoHwLPse/fu4PWvqSKVpwiqhCehuloWTE3jkFUncwDI50hPvrfCx92cOJUE8RTsQgDoAh7JKsPHctSZfUicZyLHOuHMhtIoxs6l/lzFowbhZob/Uo7aRsipOcP/iZgEPnMEu/obXcLR3QrlQ+WuEppU4g2MZ9+xENLCqVDLEHiz+jZIvl42ePc4Ip4lJ5RQFcSSR1yYb+z/4KB/3bc308PHnEMEB5TYvVpK7M3kg2CTjos5lh733qQKwqYamgFjpr5oPUxRYwRvj41GYpcLN3Tr4WOi4lfevR5eVFBu1Jc9baY8JIOvcCL5WhXsEemnx5dUhcnP2ozloFCvU1IS60KHkXtF4NJl89tu5EECquTN5Lf9cvtIhlbmPsVg==" },
"gender": {
"0": "Keine Anrede",
"1": "Enby",

View File

@@ -1,6 +1,6 @@
import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { BranchDTO } from '@generated/swagger/checkout-api';
import { BranchDTO } from '@swagger/checkout';
import { isBoolean, isNumber } from '@utils/common';
import { BehaviorSubject, Observable } from 'rxjs';
import { first, map, switchMap } from 'rxjs/operators';
@@ -35,9 +35,7 @@ export class ApplicationService {
getProcesses$(section?: 'customer' | 'branch') {
const processes$ = this.store.select(selectProcesses);
return processes$.pipe(
map((processes) => processes.filter((process) => (section ? process.section === section : true))),
);
return processes$.pipe(map((processes) => processes.filter((process) => (section ? process.section === section : true))));
}
getProcessById$(processId: number): Observable<ApplicationProcess> {
@@ -137,10 +135,7 @@ export class ApplicationService {
this.store.dispatch(setSection({ section }));
}
getLastActivatedProcessWithSectionAndType$(
section: 'customer' | 'branch',
type: string,
): Observable<ApplicationProcess> {
getLastActivatedProcessWithSectionAndType$(section: 'customer' | 'branch', type: string): Observable<ApplicationProcess> {
return this.getProcesses$(section).pipe(
map((processes) =>
processes

View File

@@ -11,17 +11,8 @@ export const addProcess = createAction(`${prefix} Add Process`, props<{ process:
export const removeProcess = createAction(`${prefix} Remove Process`, props<{ processId: number }>());
export const setActivatedProcess = createAction(
`${prefix} Set Activated Process`,
props<{ activatedProcessId: number }>(),
);
export const setActivatedProcess = createAction(`${prefix} Set Activated Process`, props<{ activatedProcessId: number }>());
export const patchProcess = createAction(
`${prefix} Patch Process`,
props<{ processId: number; changes: Partial<ApplicationProcess> }>(),
);
export const patchProcess = createAction(`${prefix} Patch Process`, props<{ processId: number; changes: Partial<ApplicationProcess> }>());
export const patchProcessData = createAction(
`${prefix} Patch Process Data`,
props<{ processId: number; data: Record<string, any> }>(),
);
export const patchProcessData = createAction(`${prefix} Patch Process Data`, props<{ processId: number; data: Record<string, any> }>());

View File

@@ -1,10 +1,10 @@
import { coerceArray } from '@angular/cdk/coercion';
import { inject, Injectable } from '@angular/core';
import { coerceArray, coerceStringArray } from '@angular/cdk/coercion';
import { Injectable } from '@angular/core';
import { Config } from '@core/config';
import { isNullOrUndefined } from '@utils/common';
import { AuthConfig, OAuthService } from 'angular-oauth2-oidc';
import { JwksValidationHandler } from 'angular-oauth2-oidc-jwks';
import { BehaviorSubject } from 'rxjs';
import { asapScheduler, BehaviorSubject } from 'rxjs';
@Injectable({
providedIn: 'root',
@@ -44,26 +44,21 @@ export class AuthService {
this._oAuthService.tokenValidationHandler = new JwksValidationHandler();
this._oAuthService.setupAutomaticSilentRefresh();
try {
await this._oAuthService.loadDiscoveryDocumentAndTryLogin();
} catch (error) {
this.login();
await this._oAuthService.loadDiscoveryDocumentAndTryLogin();
throw error;
}
this._initialized.next(true);
}
isAuthenticated() {
return this.isIdTokenValid();
}
isIdTokenValid() {
console.log('ID Token Expiration:', new Date(this._oAuthService.getIdTokenExpiration()));
return this._oAuthService.hasValidIdToken();
}
isAccessTokenValid() {
console.log('ACCESS Token Expiration:', new Date(this._oAuthService.getAccessTokenExpiration()));
return this._oAuthService.hasValidAccessToken();
}
getToken() {
return this._oAuthService.getAccessToken();
}
@@ -104,6 +99,9 @@ export class AuthService {
async logout() {
await this._oAuthService.revokeTokenAndLogout();
// asapScheduler.schedule(() => {
// window.location.reload();
// }, 250);
}
hasRole(role: string | string[]) {

View File

@@ -3,7 +3,6 @@ import { AuthService } from './auth.service';
@Directive({
selector: '[ifRole],[ifRoleElse],[ifNotRole],[ifNotRoleElse]',
standalone: false,
})
export class IfRoleDirective implements OnChanges {
@Input()

View File

@@ -1,4 +1,3 @@
export * from './auth.module';
export * from './auth.service';
export * from './if-role.directive';
export * from './login.strategy';

View File

@@ -1,78 +0,0 @@
import { ScanAdapterService } from '@adapter/scan';
import { inject, Injectable } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { EnvironmentService } from '@core/environment';
import { UiConfirmModalComponent, UiModalResult, UiModalService } from '@ui/modal';
import { injectNetworkStatus$ } from '../../app/services';
import { AuthService } from './auth.service';
import { AuthService as IsaAuthService } from '@generated/swagger/isa-api';
import { firstValueFrom, lastValueFrom } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class LoginStrategy {
private readonly environmentService = inject(EnvironmentService);
private readonly networkStatus = toSignal(injectNetworkStatus$());
private readonly modal = inject(UiModalService);
private readonly scanAdapterService = inject(ScanAdapterService);
private readonly isaAuthService = inject(IsaAuthService);
private readonly authService = inject(AuthService);
async login(title = 'Anmeldung') {
console.log('LoginStrategy.login');
let loginModalResult: UiModalResult<boolean>;
if (this.authService.isIdTokenValid()) {
console.log('LoginStrategy.login: idToken is valid');
await this.authService.login();
return;
}
try {
if (this.environmentService.isMobileDevice()) {
console.log('LoginStrategy.login: isMobileDevice');
do {
const loginModal = this.modal.open({
content: UiConfirmModalComponent,
title,
config: {
canClose: false,
backdropClose: false,
},
data: {
message: 'Bitte wählen Sie die Anmeldeoption aus.',
rejectLabel: 'Anmeldung mit Logindaten',
confirmLabel: 'Anmeldung mit Keycard',
},
});
loginModalResult = await loginModal.afterClosed$.toPromise();
} while (this.networkStatus() === 'offline');
}
} catch (error) {}
try {
if (loginModalResult.data) {
const result = await lastValueFrom(this.scanAdapterService.scan());
if (typeof result === 'string') {
const res = await firstValueFrom(
this.isaAuthService.AuthLogin({
code: result,
application: 'isa',
hostname: location.host,
}),
);
if (res.token) {
console.log('LoginStrategy.login: setKeyCardToken', res.token);
this.authService.setKeyCardToken(res.token);
}
}
}
} catch (error) {}
console.log('LoginStrategy.login: login');
await this.authService.login();
}
}

View File

@@ -120,10 +120,7 @@ export class BreadcrumbService {
if (recursive) {
const breadcrumbs = await this.getBreadcrumbByKey$(breadcrumb.key).pipe(take(1)).toPromise();
breadcrumbsToRemove = [
...breadcrumbsToRemove,
...breadcrumbs.filter((crumb) => crumb.timestamp > breadcrumb.timestamp),
];
breadcrumbsToRemove = [...breadcrumbsToRemove, ...breadcrumbs.filter((crumb) => crumb.timestamp > breadcrumb.timestamp)];
}
if (!breadcrumbsToRemove.length) {
@@ -138,10 +135,7 @@ export class BreadcrumbService {
crumbs.forEach((crumb) => this.removeBreadcrumb(crumb.id));
}
getLatestBreadcrumbForSection(
section: 'customer' | 'branch',
predicate: (crumb: Breadcrumb) => boolean = (_) => true,
) {
getLatestBreadcrumbForSection(section: 'customer' | 'branch', predicate: (crumb: Breadcrumb) => boolean = (_) => true) {
return this.store
.select(selectors.selectBreadcrumbsBySection, { section })
.pipe(map((crumbs) => crumbs.sort((a, b) => b.timestamp - a.timestamp).find((f) => predicate(f))));

View File

@@ -27,7 +27,7 @@ export interface Breadcrumb {
/**
* Query Parameter
*/
params?: object;
params?: Object;
/**
* Timestamp

View File

@@ -11,10 +11,7 @@ export const addBreadcrumb = createAction(`${prefix} Add Breadcrumb`, props<{ br
/**
* Action um Breadcrumb im State zu ändern
*/
export const updateBreadcrumb = createAction(
`${prefix} Update Breadcrumb`,
props<{ id: number; changes: Partial<Breadcrumb> }>(),
);
export const updateBreadcrumb = createAction(`${prefix} Update Breadcrumb`, props<{ id: number; changes: Partial<Breadcrumb> }>());
/**
* Action um Breadcrumb im State zu entfernen

View File

@@ -43,10 +43,7 @@ describe('Breadcrumb Reducer', () => {
const state = breadcrumbReducer(INIT, action.addBreadcrumb({ breadcrumb }));
const fixture = breadcrumbReducer(
state,
action.updateBreadcrumb({ id: breadcrumb.id, changes: { name: 'Test Name 2' } }),
);
const fixture = breadcrumbReducer(state, action.updateBreadcrumb({ id: breadcrumb.id, changes: { name: 'Test Name 2' } }));
expect(fixture.entities[breadcrumb.id]).toEqual(expected);
});

View File

@@ -10,46 +10,26 @@ describe('Breadcrumb Selectors', () => {
beforeEach(() => {
state = breadcrumbReducer(
INIT,
action.addBreadcrumb({
breadcrumb: { id: 1, key: 'unit-test-1', path: '', name: 'Unit Test 1', section: 'customer' },
}),
action.addBreadcrumb({ breadcrumb: { id: 1, key: 'unit-test-1', path: '', name: 'Unit Test 1', section: 'customer' } }),
);
state = breadcrumbReducer(
state,
action.addBreadcrumb({
breadcrumb: {
id: 2,
key: 'unit-test-1',
path: '',
name: 'Unit Test 1',
tags: ['details'],
section: 'customer',
},
breadcrumb: { id: 2, key: 'unit-test-1', path: '', name: 'Unit Test 1', tags: ['details'], section: 'customer' },
}),
);
state = breadcrumbReducer(
state,
action.addBreadcrumb({
breadcrumb: { id: 3, key: 'unit-test-2', path: '', name: 'Unit Test 1', section: 'customer' },
}),
action.addBreadcrumb({ breadcrumb: { id: 3, key: 'unit-test-2', path: '', name: 'Unit Test 1', section: 'customer' } }),
);
state = breadcrumbReducer(
state,
action.addBreadcrumb({ breadcrumb: { id: 4, key: 'unit-test-3', path: '', name: 'Unit Test 1', section: 'customer' } }),
);
state = breadcrumbReducer(
state,
action.addBreadcrumb({
breadcrumb: { id: 4, key: 'unit-test-3', path: '', name: 'Unit Test 1', section: 'customer' },
}),
);
state = breadcrumbReducer(
state,
action.addBreadcrumb({
breadcrumb: {
id: 5,
key: 'unit-test-3',
path: '',
name: 'Unit Test 1',
tags: ['details'],
section: 'customer',
},
breadcrumb: { id: 5, key: 'unit-test-3', path: '', name: 'Unit Test 1', tags: ['details'], section: 'customer' },
}),
);
});

View File

@@ -20,9 +20,7 @@ export const selectBreadcrumbById = createSelector(selectEntities, (entities, id
/**
* Gibt alle Breadcrumb Entities als Array zurück die den key enthalten
*/
export const selectBreadcrumbsByKey = createSelector(selectAll, (entities, key: string) =>
entities.filter((crumb) => crumb.key == key),
);
export const selectBreadcrumbsByKey = createSelector(selectAll, (entities, key: string) => entities.filter((crumb) => crumb.key == key));
/**
* Gibt alle Breadcrumb Entities als Array zurück die den key und tag enthalten
@@ -39,15 +37,12 @@ export const selectBreadcrumbsByKeyAndTag = createSelector(
export const selectBreadcrumbsByKeyAndTags = createSelector(
selectAll,
(entities: Breadcrumb[], { key, tags }: { key: string; tags: string[] }) =>
entities.filter(
(crumb) => crumb.key == key && isArray(crumb.tags) && tags.every((tag) => crumb.tags.includes(tag)),
),
entities.filter((crumb) => crumb.key == key && isArray(crumb.tags) && tags.every((tag) => crumb.tags.includes(tag))),
);
/**
* Gibt alle Breadcrumb Entities als Array zurück die die tags enthalten
*/
export const selectBreadcrumbsBySection = createSelector(
selectAll,
(entities: Breadcrumb[], { section }: { section: string }) => entities.filter((crumb) => crumb.section === section),
export const selectBreadcrumbsBySection = createSelector(selectAll, (entities: Breadcrumb[], { section }: { section: string }) =>
entities.filter((crumb) => crumb.section === section),
);

View File

@@ -1,7 +1,7 @@
import { createEntityAdapter, EntityState } from '@ngrx/entity';
import { Breadcrumb } from '../defs';
export type BreadcrumbState = EntityState<Breadcrumb>
export interface BreadcrumbState extends EntityState<Breadcrumb> {}
export const featureName = 'core-breadcrumb';

View File

@@ -34,7 +34,7 @@ export class CacheService {
return 'cache';
}
private getKey(token: object | string): string {
private getKey(token: Object | string): string {
if (typeof token === 'string') {
return this.hash(token);
}
@@ -105,7 +105,7 @@ export class CacheService {
});
}
private async cached(token: object | string): Promise<DbEntry<any> | undefined> {
private async cached(token: Object | string): Promise<DbEntry<any> | undefined> {
const store = await this.getObjectStore();
return new Promise<DbEntry<any> | undefined>((resolve, reject) => {
const request = store.get(this.getKey(token));
@@ -118,7 +118,7 @@ export class CacheService {
});
}
async get<T = any>(token: object | string): Promise<T | undefined> {
async get<T = any>(token: Object | string): Promise<T | undefined> {
const cached = await this.cached(token);
if (!cached) {
@@ -133,7 +133,7 @@ export class CacheService {
return cached.data;
}
async delete(token: object | string): Promise<void> {
async delete(token: Object | string): Promise<void> {
const store = await this.getObjectStore('readwrite');
return new Promise<void>((resolve, reject) => {
const request = store.delete(this.getKey(token));

View File

@@ -4,10 +4,7 @@ import { CommandService } from './command.service';
import { FEATURE_ACTION_HANDLERS, ROOT_ACTION_HANDLERS } from './tokens';
export function provideActionHandlers(actionHandlers: Type<ActionHandler>[]): Provider[] {
return [
CommandService,
actionHandlers.map((handler) => ({ provide: FEATURE_ACTION_HANDLERS, useClass: handler, multi: true })),
];
return [CommandService, actionHandlers.map((handler) => ({ provide: FEATURE_ACTION_HANDLERS, useClass: handler, multi: true }))];
}
@NgModule({})
@@ -15,20 +12,14 @@ export class CoreCommandModule {
static forRoot(actionHandlers: Type<ActionHandler>[]): ModuleWithProviders<CoreCommandModule> {
return {
ngModule: CoreCommandModule,
providers: [
CommandService,
actionHandlers.map((handler) => ({ provide: ROOT_ACTION_HANDLERS, useClass: handler, multi: true })),
],
providers: [CommandService, actionHandlers.map((handler) => ({ provide: ROOT_ACTION_HANDLERS, useClass: handler, multi: true }))],
};
}
static forChild(actionHandlers: Type<ActionHandler>[]): ModuleWithProviders<CoreCommandModule> {
return {
ngModule: CoreCommandModule,
providers: [
CommandService,
actionHandlers.map((handler) => ({ provide: FEATURE_ACTION_HANDLERS, useClass: handler, multi: true })),
],
providers: [CommandService, actionHandlers.map((handler) => ({ provide: FEATURE_ACTION_HANDLERS, useClass: handler, multi: true }))],
};
}
}

View File

@@ -0,0 +1,8 @@
import { Observable } from 'rxjs';
/**
* Config loader interface for loading configurations
*/
export interface ConfigLoader {
load(): Promise<any>;
}

View File

@@ -0,0 +1,4 @@
// start:ng42.barrel
export * from './config-loader';
export * from './json.config-loader';
// end:ng42.barrel

View File

@@ -0,0 +1,36 @@
// // unit test JsonConfigLoader
// import { HttpTestingController } from '@angular/common/http/testing';
// import { createServiceFactory, SpectatorService } from '@ngneat/spectator';
// import { CORE_JSON_CONFIG_LOADER_URL } from '../tokens';
// import { JsonConfigLoader } from './json.config-loader';
// describe('JsonConfigLoader', () => {
// let spectator: SpectatorService<JsonConfigLoader>;
// const createService = createServiceFactory({
// imports: [HttpClientTestingModule],
// service: JsonConfigLoader,
// mocks: [],
// providers: [{ provide: CORE_JSON_CONFIG_LOADER_URL, useValue: '/assets/config.json' }],
// });
// let httpTestingController: HttpTestingController;
// beforeEach(() => {
// spectator = createService();
// httpTestingController = spectator.inject(HttpTestingController);
// });
// it('should create', () => {
// expect(spectator.service).toBeTruthy();
// });
// describe('load', () => {
// it('should call the provided url', async () => {
// const reqPromise = spectator.service.load();
// const req = httpTestingController.expectOne('/assets/config.json');
// req.flush({ unit: 'test' });
// const result = await reqPromise;
// httpTestingController.verify();
// expect(result).toEqual({ unit: 'test' });
// });
// });
// });

View File

@@ -0,0 +1,16 @@
import { HttpClient } from '@angular/common/http';
import { Inject, Injectable } from '@angular/core';
import { ConfigLoader } from './config-loader';
import { CORE_JSON_CONFIG_LOADER_URL } from '../tokens';
@Injectable()
export class JsonConfigLoader implements ConfigLoader {
constructor(
@Inject(CORE_JSON_CONFIG_LOADER_URL) private url: string,
private http: HttpClient,
) {}
load(): Promise<any> {
return this.http.get(this.url).toPromise();
}
}

View File

@@ -0,0 +1,7 @@
import { Type } from '@angular/core';
import { ConfigLoader } from './config-loaders';
export interface ConfigModuleOptions {
useConfigLoader: Type<ConfigLoader>;
jsonConfigLoaderUrl?: string;
}

View File

@@ -0,0 +1,28 @@
import { APP_INITIALIZER, ModuleWithProviders, NgModule } from '@angular/core';
import { CORE_CONFIG_LOADER } from '@core/config';
import { Config } from './config';
import { ConfigModuleOptions } from './config-module-options';
import { CORE_JSON_CONFIG_LOADER_URL } from './tokens';
export function _initializeConfigFactory(config: Config) {
return () => config.init();
}
@NgModule({})
export class ConfigModule {
static forRoot(options: ConfigModuleOptions): ModuleWithProviders<ConfigModule> {
const configLoaderProvider = {
provide: CORE_CONFIG_LOADER,
useClass: options.useConfigLoader,
};
return {
ngModule: ConfigModule,
providers: [
Config,
configLoaderProvider,
options.jsonConfigLoaderUrl ? { provide: CORE_JSON_CONFIG_LOADER_URL, useValue: options.jsonConfigLoaderUrl } : null,
],
};
}
}

View File

@@ -0,0 +1,45 @@
import { createServiceFactory, SpectatorService } from '@ngneat/spectator';
import { Config } from './config';
import { ConfigLoader } from './config-loaders';
import { CORE_CONFIG_LOADER } from './tokens';
class TestConfigLoader implements ConfigLoader {
load() {
return Promise.resolve({});
}
}
// Unit test Config
describe('Config', () => {
let spectator: SpectatorService<Config>;
const createService = createServiceFactory({
service: Config,
providers: [{ provide: CORE_CONFIG_LOADER, useClass: TestConfigLoader }],
});
let configLoader: ConfigLoader;
beforeEach(() => {
spectator = createService();
configLoader = spectator.inject(CORE_CONFIG_LOADER);
});
it('should create', () => {
expect(spectator.service).toBeTruthy();
});
describe('init()', () => {
it('should load config and assigns it to _config', async () => {
const config = { unit: 'test' };
spyOn(configLoader, 'load').and.returnValue(Promise.resolve(config));
await spectator.service.init();
expect(spectator.service['_config']).toEqual(config);
});
});
describe('get()', () => {
it('should return config value', () => {
spectator.service['_config'] = { test: 'test' };
expect(spectator.service.get('test')).toEqual('test');
});
});
});

View File

@@ -0,0 +1,27 @@
import { Inject, Injectable } from '@angular/core';
import { ReplaySubject } from 'rxjs';
import { ConfigLoader } from './config-loaders';
import { CORE_CONFIG_LOADER } from './tokens';
import { pick } from './utils';
@Injectable()
export class Config {
private _config: any;
private readonly _initilized = new ReplaySubject<void>(1);
get initialized() {
return this._initilized.asObservable();
}
constructor(@Inject(CORE_CONFIG_LOADER) private readonly _configLoader: ConfigLoader) {}
// load config and assign it to this._config
async init() {
this._config = await this._configLoader.load();
this._initilized.next();
}
get(path: string) {
return pick(path, this._config);
}
}

View File

@@ -0,0 +1,6 @@
export * from './config-loaders';
export * from './config-module-options';
export * from './config.module';
export * from './config';
export * from './tokens';
export * from './utils';

View File

@@ -0,0 +1,6 @@
import { InjectionToken } from '@angular/core';
import { ConfigLoader } from './config-loaders';
export const CORE_CONFIG_LOADER = new InjectionToken<ConfigLoader>('core.config.loader');
export const CORE_JSON_CONFIG_LOADER_URL = new InjectionToken<ConfigLoader>('core.json.config.loader.url');

View File

@@ -0,0 +1,3 @@
// start:ng42.barrel
export * from './pick';
// end:ng42.barrel

View File

@@ -0,0 +1,41 @@
import { pick } from './pick';
describe('pick', () => {
it('should pick properties from the 1st level from the object', () => {
const obj = {
foo: 'bar',
};
expect(pick('foo', obj)).toEqual('bar');
});
it('should pick properties from the 2nd level from the object', () => {
const obj = {
foo: {
bar: 'baz',
},
};
expect(pick('foo.bar', obj)).toEqual('baz');
});
it('should pick properties from the 3rd level from the object', () => {
const obj = {
foo: {
bar: {
baz: 'qux',
},
},
};
expect(pick('foo.bar.baz', obj)).toEqual('qux');
});
it('should throw an error of obj is not an object', () => {
expect(() => pick('foo', 'bar')).toThrowError(`bar is not an object`);
});
it('should return undefined if the property is not found', () => {
const obj = {
foo: 'bar',
};
expect(pick('bar', obj)).toEqual(undefined);
});
});

View File

@@ -0,0 +1,33 @@
/**
* Pick a value from an object at a given path.
* @param path path of the value to pick
* @param obj object to pick from
* @returns the value at the path or undefined
* @throws if obj is not an object
*/
export function pick<T = any>(path: string, obj: Object): T {
const paths = path.split('.');
// check if obj is null or undefined
if (obj == null) {
return undefined;
}
// check if obj is of type object and not an array
// and throw an error if not
if (typeof obj !== 'object' || Array.isArray(obj)) {
throw new Error(`${obj} is not an object`);
}
let result = obj;
// loop through the path and pick the value
// early exit if the path is empty
for (const path of paths) {
result = result[path];
if (result == null) {
return undefined;
}
}
return result as T;
}

View File

@@ -62,10 +62,6 @@ export class EnvironmentService {
matchDesktopXXLarge$ = this._breakpointObserver.observe(MATCH_DESKTOP_XXLARGE).pipe(map((result) => result.matches));
isMobileDevice(): boolean {
return this._platform.ANDROID || this._platform.IOS;
}
/**
* @deprecated Use `matchDesktopSmall` or 'matchDesktop' instead.
*/

View File

@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core';
import { ItemDTO } from '@generated/swagger/cat-search-api';
import { ItemDTO } from '@swagger/cat';
import {
AvailabilityDTO,
BranchDTO,
@@ -7,25 +7,20 @@ import {
StoreCheckoutBranchService,
StoreCheckoutSupplierService,
SupplierDTO,
} from '@generated/swagger/checkout-api';
} from '@swagger/checkout';
import { BehaviorSubject, combineLatest, Observable, of } from 'rxjs';
import {
AvailabilityRequestDTO,
AvailabilityService,
AvailabilityDTO as SwaggerAvailabilityDTO,
AvailabilityType,
} from '@generated/swagger/availability-api';
import { AvailabilityDTO as CatAvailabilityDTO } from '@generated/swagger/cat-search-api';
} from '@swagger/availability';
import { AvailabilityDTO as CatAvailabilityDTO } from '@swagger/cat';
import { map, shareReplay, switchMap, withLatestFrom, mergeMap, timeout, first } from 'rxjs/operators';
import { isArray, memorize } from '@utils/common';
import { LogisticianDTO, LogisticianService } from '@generated/swagger/oms-api';
import {
ResponseArgsOfIEnumerableOfStockInfoDTO,
StockDTO,
StockInfoDTO,
StockService,
} from '@generated/swagger/inventory-api';
import { PriceDTO } from '@generated/swagger/availability-api';
import { LogisticianDTO, LogisticianService } from '@swagger/oms';
import { ResponseArgsOfIEnumerableOfStockInfoDTO, StockDTO, StockInfoDTO, StockService } from '@swagger/remi';
import { PriceDTO } from '@swagger/availability';
import { AvailabilityByBranchDTO, ItemData, Ssc } from './defs';
import { Availability } from './defs/availability';
import { isEmpty } from 'lodash';
@@ -165,7 +160,7 @@ export class DomainAvailabilityService {
quantity: number;
branch?: BranchDTO;
}): Observable<AvailabilityDTO> {
const request = branch ? this.getStockByBranch(branch.id) : this.getDefaultStock();
const request = !!branch ? this.getStockByBranch(branch.id) : this.getDefaultStock();
return request.pipe(
switchMap((s) =>
combineLatest([
@@ -176,13 +171,7 @@ export class DomainAvailabilityService {
),
map(([response, supplier, defaultBranch]) => {
const price = item?.price;
return this._mapToTakeAwayAvailability({
response,
supplier,
branchId: branch?.id ?? defaultBranch?.id,
quantity,
price,
});
return this._mapToTakeAwayAvailability({ response, supplier, branchId: branch?.id ?? defaultBranch?.id, quantity, price });
}),
shareReplay(1),
);
@@ -222,18 +211,12 @@ export class DomainAvailabilityService {
quantity: number;
branchId?: number;
}): Observable<AvailabilityDTO> {
const request = branchId ? this.getStockByBranch(branchId) : this.getDefaultStock();
const request = !!branchId ? this.getStockByBranch(branchId) : this.getDefaultStock();
return request.pipe(
switchMap((s) => this._stockService.StockInStockByEAN({ eans, stockId: s.id })),
withLatestFrom(this.getTakeAwaySupplier(), this.getDefaultBranch()),
map(([response, supplier, defaultBranch]) => {
return this._mapToTakeAwayAvailability({
response,
supplier,
branchId: branchId ?? defaultBranch.id,
quantity,
price,
});
return this._mapToTakeAwayAvailability({ response, supplier, branchId: branchId ?? defaultBranch.id, quantity, price });
}),
shareReplay(1),
);
@@ -346,10 +329,7 @@ export class DomainAvailabilityService {
this.getPickUpAvailability({ item, quantity, branch: branch ?? defaultBranch }).pipe(
mergeMap((availability) =>
logistician$.pipe(
map((logistician) => ({
...(availability?.length > 0 ? availability[0] : []),
logistician: { id: logistician.id },
})),
map((logistician) => ({ ...(availability?.length > 0 ? availability[0] : []), logistician: { id: logistician.id } })),
),
),
shareReplay(1),
@@ -447,9 +427,7 @@ export class DomainAvailabilityService {
return this.getPickUpAvailabilities(payload, true).pipe(
timeout(20000),
switchMap((availability) =>
logistician$.pipe(
map((logistician) => ({ availability: [...availability], logistician: { id: logistician.id } })),
),
logistician$.pipe(map((logistician) => ({ availability: [...availability], logistician: { id: logistician.id } }))),
),
shareReplay(1),
);
@@ -534,9 +512,7 @@ export class DomainAvailabilityService {
return availability;
}
private _mapToPickUpAvailability(
availabilities: SwaggerAvailabilityDTO[],
): Availability<AvailabilityDTO, SwaggerAvailabilityDTO>[] {
private _mapToPickUpAvailability(availabilities: SwaggerAvailabilityDTO[]): Availability<AvailabilityDTO, SwaggerAvailabilityDTO>[] {
if (isArray(availabilities)) {
const preferred = availabilities.filter((f) => f.preferred === 1);
const totalAvailable = availabilities.reduce((sum, av) => sum + (av?.qty || 0), 0);
@@ -596,9 +572,7 @@ export class DomainAvailabilityService {
}
const stock$ = branchId$.pipe(
mergeMap((branchId) =>
this._stockService.StockGetStocksByBranch({ branchId }).pipe(map((response) => response.result?.[0])),
),
mergeMap((branchId) => this._stockService.StockGetStocksByBranch({ branchId }).pipe(map((response) => response.result?.[0]))),
);
return stock$.pipe(
@@ -624,9 +598,7 @@ export class DomainAvailabilityService {
getInStock({ itemIds, branchId }: { itemIds: number[]; branchId: number }): Observable<StockInfoDTO[]> {
return this.getStockByBranch(branchId).pipe(
mergeMap((stock) =>
this._stockService
.StockInStock({ articleIds: itemIds, stockId: stock.id })
.pipe(map((response) => response.result)),
this._stockService.StockInStock({ articleIds: itemIds, stockId: stock.id }).pipe(map((response) => response.result)),
),
);
}

View File

@@ -1,4 +1,4 @@
import { AvailabilityDTO } from '@generated/swagger/checkout-api';
import { AvailabilityDTO } from '@swagger/checkout';
export interface AvailabilityByBranchDTO extends AvailabilityDTO {
availableQuantity?: number;

View File

@@ -1,4 +1,4 @@
import { PriceDTO } from '@generated/swagger/availability-api';
import { PriceDTO } from '@swagger/availability';
export interface ItemData {
ean: string;

View File

@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core';
import { StockInfoDTO } from '@generated/swagger/inventory-api';
import { StockInfoDTO } from '@swagger/remi';
import { groupBy, isEqual, uniqWith } from 'lodash';
import { BehaviorSubject, combineLatest, Observable, Subject } from 'rxjs';
import { buffer, debounceTime, distinctUntilChanged } from 'rxjs/operators';
@@ -90,15 +90,10 @@ export class DomainInStockService {
const grouped = groupBy(itemBranchData, 'branchId');
Object.keys(grouped).forEach((key) => {
const branchId = Number(key);
const itemIds = itemBranchData
.filter((itemBranch) => itemBranch.branchId === branchId)
.map((item) => item.itemId);
const itemIds = itemBranchData.filter((itemBranch) => itemBranch.branchId === branchId).map((item) => item.itemId);
this._availability
.getInStock({ itemIds, branchId })
.subscribe(
this._fetchStockDataResponse({ itemIds, branchId }),
this._fetchStockDataError({ itemIds, branchId }),
);
.subscribe(this._fetchStockDataResponse({ itemIds, branchId }), this._fetchStockDataError({ itemIds, branchId }));
});
}
@@ -106,9 +101,7 @@ export class DomainInStockService {
({ itemIds, branchId }: { itemIds: number[]; branchId: number }) =>
(stockInfos: StockInfoDTO[]) => {
itemIds.forEach((itemId) => {
const stockInfo = stockInfos.find(
(stockInfo) => stockInfo.itemId === itemId && stockInfo.branchId === branchId,
);
const stockInfo = stockInfos.find((stockInfo) => stockInfo.itemId === itemId && stockInfo.branchId === branchId);
let inStock = 0;
if (stockInfo?.inStock) {

View File

@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core';
import { OLAAvailabilityDTO } from '@generated/swagger/checkout-api';
import { OLAAvailabilityDTO } from '@swagger/checkout';
import { Observable } from 'rxjs';
import { CartItem } from './defs/cart-item.model';

Some files were not shown because too many files have changed in this diff Show More