mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
Compare commits
131 Commits
nx-build-e
...
hotfix/dea
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
353864e2f0 | ||
|
|
1b6b726036 | ||
|
|
4c56f394c5 | ||
|
|
a086111ab5 | ||
|
|
15a4718e58 | ||
|
|
40592b4477 | ||
|
|
d430f544f0 | ||
|
|
62e586cfda | ||
|
|
304f8a64e5 | ||
|
|
c672ae4012 | ||
|
|
fd693a4beb | ||
|
|
2c70339f23 | ||
|
|
59f0cc7d43 | ||
|
|
0ca58fe1bf | ||
|
|
8cf80a60a0 | ||
|
|
cffa7721bc | ||
|
|
066ab5d5be | ||
|
|
3bbf79a3c3 | ||
|
|
357485e32f | ||
|
|
39984342a6 | ||
|
|
c52f18e979 | ||
|
|
e58ec93087 | ||
|
|
4e6204817d | ||
|
|
c41355bcdf | ||
|
|
fa8e601660 | ||
|
|
708ec01704 | ||
|
|
332699ca74 | ||
|
|
3b0a63a53a | ||
|
|
327fdc745d | ||
|
|
297ec9100d | ||
|
|
298ab1acbe | ||
|
|
fe77a0ea8b | ||
|
|
48f588f53b | ||
|
|
7f4af304ac | ||
|
|
643b2b0e60 | ||
|
|
cd1ff5f277 | ||
|
|
46c70cae3e | ||
|
|
2cb1f9ec99 | ||
|
|
d2dcf638e3 | ||
|
|
a4241cbd7a | ||
|
|
dd3705f8bc | ||
|
|
514715589b | ||
|
|
0740273dbc | ||
|
|
bbb9c5d39c | ||
|
|
f0bd957a07 | ||
|
|
e4f289c67d | ||
|
|
2af16d92ea | ||
|
|
99e8e7cfe0 | ||
|
|
ac728f2dd9 | ||
|
|
2e012a124a | ||
|
|
d22e320294 | ||
|
|
a0f24aac17 | ||
|
|
7ae484fc83 | ||
|
|
0dcb31973f | ||
|
|
c2f393d249 | ||
|
|
2dbf7dda37 | ||
|
|
cce15a2137 | ||
|
|
14a5a67a1e | ||
|
|
d7d535c10d | ||
|
|
ad00899b6e | ||
|
|
0addf392b6 | ||
|
|
1e84223076 | ||
|
|
244984b6cf | ||
|
|
b39abe630d | ||
|
|
239ab52890 | ||
|
|
4732656a0f | ||
|
|
0da9800ca0 | ||
|
|
baf4a0dfbc | ||
|
|
da5a42280a | ||
|
|
4d29189c8d | ||
|
|
32bd3e26d2 | ||
|
|
6d26f7f6c0 | ||
|
|
72bcacefb6 | ||
|
|
71e9a6da0e | ||
|
|
b339a6d79f | ||
|
|
0b4aef5f6c | ||
|
|
c5182809ac | ||
|
|
f4b541c7c0 | ||
|
|
afe6c6abcc | ||
|
|
3f233f9580 | ||
|
|
6f9d4d9218 | ||
|
|
4111663d8c | ||
|
|
2beeba5c92 | ||
|
|
edab1322c8 | ||
|
|
59ce736faa | ||
|
|
3cd6f4bd58 | ||
|
|
594acaa5f5 | ||
|
|
76ff54dd3a | ||
|
|
598df7d5ed | ||
|
|
442670bdd0 | ||
|
|
b015e97e1f | ||
|
|
65ab3bfc0a | ||
|
|
e674378080 | ||
|
|
40c9d51dfc | ||
|
|
5f74c6ddf8 | ||
|
|
a36d746fb8 | ||
|
|
f6b2b554bb | ||
|
|
465df27858 | ||
|
|
7c907645dc | ||
|
|
b7e7155577 | ||
|
|
b28c204f23 | ||
|
|
e7a807cfbd | ||
|
|
344dc61a90 | ||
|
|
8d063428fc | ||
|
|
06b0c6264a | ||
|
|
4fe633e973 | ||
|
|
2463a803ea | ||
|
|
1663dcec73 | ||
|
|
827aa565c5 | ||
|
|
39fc4ce1ce | ||
|
|
4f4b072e25 | ||
|
|
9af4a72a76 | ||
|
|
7a44101e90 | ||
|
|
6fee35c756 | ||
|
|
c15077aa86 | ||
|
|
f051a97e53 | ||
|
|
1b26a44a37 | ||
|
|
80b2508708 | ||
|
|
e9affd2359 | ||
|
|
8f8b9153b0 | ||
|
|
9a4121e2bf | ||
|
|
50b7f21394 | ||
|
|
a67375557d | ||
|
|
6e7c56fcb9 | ||
|
|
05e257b922 | ||
|
|
d7d61915fa | ||
|
|
d0220b6246 | ||
|
|
32336ba5b4 | ||
|
|
1f26d5285b | ||
|
|
be0bff0535 | ||
|
|
cb7391e66f |
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"mcp__context7__resolve-library-id",
|
||||
"mcp__context7__get-library-docs"
|
||||
],
|
||||
"deny": []
|
||||
}
|
||||
}
|
||||
2
.github/copilot-instructions.md
vendored
2
.github/copilot-instructions.md
vendored
@@ -4,6 +4,8 @@
|
||||
|
||||
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.
|
||||
|
||||
**Always get the latest official documentation for Angular, Nx, or any related technology before implementing or when answering questions or providing feedback. Use Context7:**
|
||||
|
||||
## Tone and Personality
|
||||
|
||||
Maintain a professional, objective, and direct tone consistently:
|
||||
|
||||
40
.github/instructions/nx.instructions.md
vendored
40
.github/instructions/nx.instructions.md
vendored
@@ -1,40 +0,0 @@
|
||||
---
|
||||
applyTo: '**'
|
||||
---
|
||||
|
||||
// This file is automatically generated by Nx Console
|
||||
|
||||
You are in an nx workspace using Nx 21.2.0 and npm as the package manager.
|
||||
|
||||
You have access to the Nx MCP server and the tools it provides. Use them. Follow these guidelines in order to best help the user:
|
||||
|
||||
# General Guidelines
|
||||
- When answering questions, use the nx_workspace tool first to gain an understanding of the workspace architecture
|
||||
- For questions around nx configuration, best practices or if you're unsure, use the nx_docs tool to get relevant, up-to-date docs!! Always use this instead of assuming things about nx configuration
|
||||
- If the user needs help with an Nx configuration or project graph error, use the 'nx_workspace' tool to get any errors
|
||||
- To help answer questions about the workspace structure or simply help with demonstrating how tasks depend on each other, use the 'nx_visualize_graph' tool
|
||||
|
||||
# Generation Guidelines
|
||||
If the user wants to generate something, use the following flow:
|
||||
|
||||
- learn about the nx workspace and any specifics the user needs by using the 'nx_workspace' tool and the 'nx_project_details' tool if applicable
|
||||
- get the available generators using the 'nx_generators' tool
|
||||
- decide which generator to use. If no generators seem relevant, check the 'nx_available_plugins' tool to see if the user could install a plugin to help them
|
||||
- get generator details using the 'nx_generator_schema' tool
|
||||
- you may use the 'nx_docs' tool to learn more about a specific generator or technology if you're unsure
|
||||
- decide which options to provide in order to best complete the user's request. Don't make any assumptions and keep the options minimalistic
|
||||
- open the generator UI using the 'nx_open_generate_ui' tool
|
||||
- wait for the user to finish the generator
|
||||
- read the generator log file using the 'nx_read_generator_log' tool
|
||||
- use the information provided in the log file to answer the user's question or continue with what they were doing
|
||||
|
||||
# Running Tasks Guidelines
|
||||
If the user wants help with tasks or commands (which include keywords like "test", "build", "lint", or other similar actions), use the following flow:
|
||||
- Use the 'nx_current_running_tasks_details' tool to get the list of tasks (this can include tasks that were completed, stopped or failed).
|
||||
- If there are any tasks, ask the user if they would like help with a specific task then use the 'nx_current_running_task_output' tool to get the terminal output for that task/command
|
||||
- Use the terminal output from 'nx_current_running_task_output' to see what's wrong and help the user fix their problem. Use the appropriate tools if necessary
|
||||
- If the user would like to rerun the task or command, always use `nx run <taskId>` to rerun in the terminal. This will ensure that the task will run in the nx context and will be run the same way it originally executed
|
||||
- If the task was marked as "continuous" do not offer to rerun the task. This task is already running and the user can see the output in the terminal. You can use 'nx_current_running_task_output' to get the output of the task to verify the output.
|
||||
|
||||
|
||||
|
||||
39
.github/testing-instructions.md
vendored
39
.github/testing-instructions.md
vendored
@@ -2,8 +2,10 @@
|
||||
|
||||
## Framework and Tools
|
||||
|
||||
- Use **Jest** as the testing framework.
|
||||
- For unit tests, utilize **Spectator** to simplify Angular component testing.
|
||||
- **Vitest** is the recommended testing framework.
|
||||
[Vitest Documentation (latest)](https://context7.com/vitest-dev/vitest/llms.txt?topic=getting+started)
|
||||
- **Jest** and **Spectator** are **deprecated**.
|
||||
Do not use them for new tests. Existing tests should be migrated to Vitest where possible.
|
||||
|
||||
## Guidelines
|
||||
|
||||
@@ -23,28 +25,31 @@
|
||||
## Example Test Structure
|
||||
|
||||
```typescript
|
||||
// Example using Jest and Spectator
|
||||
import { createComponentFactory, Spectator } from '@ngneat/spectator';
|
||||
// Example using Vitest (Jest and Spectator are deprecated)
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import { render } from '@testing-library/angular';
|
||||
import { MyComponent } from './my-component.component';
|
||||
|
||||
describe('MyComponent', () => {
|
||||
let spectator: Spectator<MyComponent>;
|
||||
const createComponent = createComponentFactory(MyComponent);
|
||||
let component: MyComponent;
|
||||
|
||||
beforeEach(() => {
|
||||
spectator = createComponent();
|
||||
beforeEach(async () => {
|
||||
const { fixture } = await render(MyComponent);
|
||||
component = fixture.componentInstance;
|
||||
});
|
||||
|
||||
it('should display the correct title', () => {
|
||||
it('should display the correct title', async () => {
|
||||
// Arrange
|
||||
const expectedTitle = 'Hello World';
|
||||
|
||||
// Act
|
||||
spectator.component.title = expectedTitle;
|
||||
spectator.detectChanges();
|
||||
component.title = expectedTitle;
|
||||
// If using Angular, trigger change detection:
|
||||
// fixture.detectChanges();
|
||||
|
||||
// Assert
|
||||
expect(spectator.query('h1')).toHaveText(expectedTitle);
|
||||
const compiled = fixture.nativeElement as HTMLElement;
|
||||
expect(compiled.querySelector('h1')?.textContent).toBe(expectedTitle);
|
||||
});
|
||||
|
||||
it('should handle error cases gracefully', () => {
|
||||
@@ -52,15 +57,17 @@ describe('MyComponent', () => {
|
||||
const invalidInput = null;
|
||||
|
||||
// Act
|
||||
spectator.component.input = invalidInput;
|
||||
component.input = invalidInput;
|
||||
|
||||
// Assert
|
||||
expect(() => spectator.component.processInput()).toThrowError('Invalid input');
|
||||
expect(() => component.processInput()).toThrowError('Invalid input');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- [Jest Documentation](https://jestjs.io/docs/getting-started)
|
||||
- [Spectator Documentation](https://ngneat.github.io/spectator/)
|
||||
- [Vitest Documentation (latest)](https://context7.com/vitest-dev/vitest/llms.txt?topic=getting+started)
|
||||
- [Vitest Official Guide](https://vitest.dev/guide/)
|
||||
- [Testing Library for Angular](https://testing-library.com/docs/angular-testing-library/intro/)
|
||||
- **Jest** and **Spectator** documentation are deprecated
|
||||
|
||||
147
.gitignore
vendored
147
.gitignore
vendored
@@ -1,70 +1,77 @@
|
||||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
.matomo
|
||||
junit.xml
|
||||
|
||||
# compiled output
|
||||
/dist
|
||||
/tmp
|
||||
/out-tsc
|
||||
|
||||
/
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
|
||||
# profiling files
|
||||
chrome-profiler-events.json
|
||||
speed-measure-plugin.json
|
||||
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# IDE - VSCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
.history/*
|
||||
|
||||
# misc
|
||||
/.angular/cache
|
||||
/.sass-cache
|
||||
/connect.lock
|
||||
/coverage
|
||||
/testresults
|
||||
/libpeerconnection.log
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
yarn.lock
|
||||
testem.log
|
||||
/typings
|
||||
|
||||
# System Files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
libs/swagger/src/lib/*
|
||||
*storybook.log
|
||||
|
||||
|
||||
.nx/cache
|
||||
.nx/workspace-data
|
||||
.angular
|
||||
|
||||
|
||||
storybook-static
|
||||
|
||||
.cursor\rules\nx-rules.mdc
|
||||
.github\instructions\nx.instructions.md
|
||||
.cursor/rules/nx-rules.mdc
|
||||
.github/instructions/nx.instructions.md
|
||||
|
||||
vite.config.*.timestamp*
|
||||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
.matomo
|
||||
junit.xml
|
||||
|
||||
# compiled output
|
||||
/dist
|
||||
/tmp
|
||||
/out-tsc
|
||||
|
||||
/
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
|
||||
# profiling files
|
||||
chrome-profiler-events.json
|
||||
speed-measure-plugin.json
|
||||
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# IDE - VSCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
.history/*
|
||||
|
||||
# misc
|
||||
/.angular/cache
|
||||
/.sass-cache
|
||||
/connect.lock
|
||||
/coverage
|
||||
/testresults
|
||||
/libpeerconnection.log
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
yarn.lock
|
||||
testem.log
|
||||
/typings
|
||||
|
||||
# System Files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
libs/swagger/src/lib/*
|
||||
*storybook.log
|
||||
|
||||
|
||||
.nx/cache
|
||||
.nx/workspace-data
|
||||
.angular
|
||||
.claude
|
||||
|
||||
|
||||
storybook-static
|
||||
|
||||
.cursor\rules\nx-rules.mdc
|
||||
.github\instructions\nx.instructions.md
|
||||
.cursor/rules/nx-rules.mdc
|
||||
.github/instructions/nx.instructions.md
|
||||
|
||||
vite.config.*.timestamp*
|
||||
vitest.config.*.timestamp*
|
||||
|
||||
.mcp.json
|
||||
.memory.json
|
||||
|
||||
nx.instructions.md
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"*.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"
|
||||
}
|
||||
{
|
||||
"*.ts": "npx eslint --fix --config eslint.config.js",
|
||||
"*.tsx": "npx eslint --fix --config eslint.config.js",
|
||||
"*.js": "npx eslint --fix --config eslint.config.js",
|
||||
"*.jsx": "npx eslint --fix --config eslint.config.js",
|
||||
"*.html": "npx eslint --fix --config eslint.config.js"
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ WORKDIR /app
|
||||
COPY . .
|
||||
RUN umask 0022
|
||||
RUN npm version ${SEMVERSION}
|
||||
RUN npm install --foreground-scripts --legacy-peer-deps
|
||||
RUN npm install --foreground-scripts
|
||||
RUN if [ "${IS_PRODUCTION}" = "true" ] ; then npm run-script build-prod ; else npm run-script build ; fi
|
||||
|
||||
# stage final
|
||||
|
||||
@@ -1,55 +1,55 @@
|
||||
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',
|
||||
},
|
||||
},
|
||||
];
|
||||
const nx = require('@nx/eslint-plugin');
|
||||
const baseConfig = require('../../eslint.config.js');
|
||||
|
||||
module.exports = [
|
||||
...baseConfig,
|
||||
...nx.configs['flat/angular'],
|
||||
...nx.configs['flat/angular-template'],
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
rules: {
|
||||
'@angular-eslint/directive-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'attribute',
|
||||
prefix: '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',
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -38,7 +38,7 @@
|
||||
],
|
||||
"styles": [
|
||||
"@angular/cdk/overlay-prebuilt.css",
|
||||
"apps/isa-app/src/ui.scss",
|
||||
"apps/isa-app/src/tailwind.scss",
|
||||
"apps/isa-app/src/styles.scss"
|
||||
],
|
||||
"scripts": []
|
||||
@@ -128,7 +128,7 @@
|
||||
],
|
||||
"styles": [
|
||||
"@angular/cdk/overlay-prebuilt.css",
|
||||
"apps/isa-app/src/ui.scss",
|
||||
"apps/isa-app/src/tailwind.scss",
|
||||
"apps/isa-app/src/styles.scss"
|
||||
]
|
||||
},
|
||||
@@ -148,7 +148,7 @@
|
||||
"compodoc": false,
|
||||
"styles": [
|
||||
"@angular/cdk/overlay-prebuilt.css",
|
||||
"apps/isa-app/src/ui.scss",
|
||||
"apps/isa-app/src/tailwind.scss",
|
||||
"apps/isa-app/src/styles.scss"
|
||||
]
|
||||
},
|
||||
|
||||
@@ -1,214 +1,231 @@
|
||||
import { isDevMode, NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import {
|
||||
CanActivateCartGuard,
|
||||
CanActivateCartWithProcessIdGuard,
|
||||
CanActivateCustomerGuard,
|
||||
CanActivateCustomerOrdersGuard,
|
||||
CanActivateCustomerOrdersWithProcessIdGuard,
|
||||
CanActivateCustomerWithProcessIdGuard,
|
||||
CanActivateGoodsInGuard,
|
||||
CanActivateProductGuard,
|
||||
CanActivateProductWithProcessIdGuard,
|
||||
CanActivateRemissionGuard,
|
||||
CanActivateTaskCalendarGuard,
|
||||
IsAuthenticatedGuard,
|
||||
} from './guards';
|
||||
import { CanActivateAssortmentGuard } from './guards/can-activate-assortment.guard';
|
||||
import { CanActivatePackageInspectionGuard } from './guards/can-activate-package-inspection.guard';
|
||||
import { MainComponent } from './main.component';
|
||||
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 { tabResolverFn } from '@isa/core/tabs';
|
||||
import { provideScrollPositionRestoration } from '@isa/utils/scroll-position';
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: '', redirectTo: 'kunde/dashboard', pathMatch: 'full' },
|
||||
{
|
||||
path: 'login',
|
||||
children: [
|
||||
{ path: ':token', component: TokenLoginComponent },
|
||||
{ path: '**', redirectTo: 'kunde', pathMatch: 'full' },
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
canActivate: [IsAuthenticatedGuard],
|
||||
children: [
|
||||
{
|
||||
path: 'kunde',
|
||||
component: MainComponent,
|
||||
children: [
|
||||
{
|
||||
path: 'dashboard',
|
||||
loadChildren: () =>
|
||||
import('@page/dashboard').then((m) => m.DashboardModule),
|
||||
data: {
|
||||
matomo: {
|
||||
title: 'Dashboard',
|
||||
} as MatomoRouteData,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'product',
|
||||
loadChildren: () =>
|
||||
import('@page/catalog').then((m) => m.PageCatalogModule),
|
||||
canActivate: [CanActivateProductGuard],
|
||||
},
|
||||
{
|
||||
path: ':processId/product',
|
||||
loadChildren: () =>
|
||||
import('@page/catalog').then((m) => m.PageCatalogModule),
|
||||
canActivate: [CanActivateProductWithProcessIdGuard],
|
||||
resolve: { processId: ProcessIdResolver },
|
||||
},
|
||||
{
|
||||
path: 'order',
|
||||
loadChildren: () =>
|
||||
import('@page/customer-order').then((m) => m.CustomerOrderModule),
|
||||
canActivate: [CanActivateCustomerOrdersGuard],
|
||||
},
|
||||
{
|
||||
path: ':processId/order',
|
||||
loadChildren: () =>
|
||||
import('@page/customer-order').then((m) => m.CustomerOrderModule),
|
||||
canActivate: [CanActivateCustomerOrdersWithProcessIdGuard],
|
||||
resolve: { processId: ProcessIdResolver },
|
||||
},
|
||||
{
|
||||
path: 'customer',
|
||||
loadChildren: () =>
|
||||
import('@page/customer').then((m) => m.CustomerModule),
|
||||
canActivate: [CanActivateCustomerGuard],
|
||||
},
|
||||
{
|
||||
path: ':processId/customer',
|
||||
loadChildren: () =>
|
||||
import('@page/customer').then((m) => m.CustomerModule),
|
||||
canActivate: [CanActivateCustomerWithProcessIdGuard],
|
||||
resolve: { processId: ProcessIdResolver },
|
||||
},
|
||||
{
|
||||
path: 'cart',
|
||||
loadChildren: () =>
|
||||
import('@page/checkout').then((m) => m.PageCheckoutModule),
|
||||
canActivate: [CanActivateCartGuard],
|
||||
},
|
||||
{
|
||||
path: ':processId/cart',
|
||||
loadChildren: () =>
|
||||
import('@page/checkout').then((m) => m.PageCheckoutModule),
|
||||
canActivate: [CanActivateCartWithProcessIdGuard],
|
||||
},
|
||||
{
|
||||
path: 'pickup-shelf',
|
||||
canActivate: [ProcessIdGuard],
|
||||
// NOTE: This is a workaround for the canActivate guard not being called
|
||||
loadChildren: () =>
|
||||
import('@page/pickup-shelf').then((m) => m.PickupShelfOutModule),
|
||||
},
|
||||
{
|
||||
path: ':processId/pickup-shelf',
|
||||
canActivate: [ActivateProcessIdGuard],
|
||||
loadChildren: () =>
|
||||
import('@page/pickup-shelf').then((m) => m.PickupShelfOutModule),
|
||||
},
|
||||
{ path: '**', redirectTo: 'dashboard', pathMatch: 'full' },
|
||||
],
|
||||
resolve: { section: CustomerSectionResolver },
|
||||
},
|
||||
{
|
||||
path: 'filiale',
|
||||
component: MainComponent,
|
||||
children: [
|
||||
{
|
||||
path: 'task-calendar',
|
||||
loadChildren: () =>
|
||||
import('@page/task-calendar').then(
|
||||
(m) => m.PageTaskCalendarModule,
|
||||
),
|
||||
canActivate: [CanActivateTaskCalendarGuard],
|
||||
},
|
||||
{
|
||||
path: 'pickup-shelf',
|
||||
canActivate: [ActivateProcessIdWithConfigKeyGuard('pickupShelf')],
|
||||
// NOTE: This is a workaround for the canActivate guard not being called
|
||||
loadChildren: () =>
|
||||
import('@page/pickup-shelf').then((m) => m.PickupShelfInModule),
|
||||
},
|
||||
{
|
||||
path: 'goods/in',
|
||||
loadChildren: () =>
|
||||
import('@page/goods-in').then((m) => m.GoodsInModule),
|
||||
canActivate: [CanActivateGoodsInGuard],
|
||||
},
|
||||
{
|
||||
path: 'remission',
|
||||
loadChildren: () =>
|
||||
import('@page/remission').then((m) => m.PageRemissionModule),
|
||||
canActivate: [CanActivateRemissionGuard],
|
||||
},
|
||||
{
|
||||
path: 'package-inspection',
|
||||
loadChildren: () =>
|
||||
import('@page/package-inspection').then(
|
||||
(m) => m.PackageInspectionModule,
|
||||
),
|
||||
canActivate: [CanActivatePackageInspectionGuard],
|
||||
},
|
||||
{
|
||||
path: 'assortment',
|
||||
loadChildren: () =>
|
||||
import('@page/assortment').then((m) => m.AssortmentModule),
|
||||
canActivate: [CanActivateAssortmentGuard],
|
||||
},
|
||||
{ path: '**', redirectTo: 'task-calendar', pathMatch: 'full' },
|
||||
],
|
||||
resolve: { section: BranchSectionResolver },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: ':tabId',
|
||||
component: MainComponent,
|
||||
resolve: { process: tabResolverFn, tab: tabResolverFn },
|
||||
canActivate: [IsAuthenticatedGuard],
|
||||
children: [
|
||||
{
|
||||
path: 'return',
|
||||
loadChildren: () =>
|
||||
import('@isa/oms/feature/return-search').then((m) => m.routes),
|
||||
},
|
||||
{
|
||||
path: 'remission',
|
||||
loadChildren: () =>
|
||||
import('@isa/remission/feature/remission-list').then((m) => m.routes),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
if (isDevMode()) {
|
||||
routes.unshift({
|
||||
path: 'preview',
|
||||
component: PreviewComponent,
|
||||
});
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes), TokenLoginModule],
|
||||
exports: [RouterModule],
|
||||
providers: [provideScrollPositionRestoration()],
|
||||
})
|
||||
export class AppRoutingModule {}
|
||||
import { isDevMode, NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import {
|
||||
CanActivateCartGuard,
|
||||
CanActivateCartWithProcessIdGuard,
|
||||
CanActivateCustomerGuard,
|
||||
CanActivateCustomerOrdersGuard,
|
||||
CanActivateCustomerOrdersWithProcessIdGuard,
|
||||
CanActivateCustomerWithProcessIdGuard,
|
||||
CanActivateGoodsInGuard,
|
||||
CanActivateProductGuard,
|
||||
CanActivateProductWithProcessIdGuard,
|
||||
CanActivateRemissionGuard,
|
||||
CanActivateTaskCalendarGuard,
|
||||
IsAuthenticatedGuard,
|
||||
} from './guards';
|
||||
import { CanActivateAssortmentGuard } from './guards/can-activate-assortment.guard';
|
||||
import { CanActivatePackageInspectionGuard } from './guards/can-activate-package-inspection.guard';
|
||||
import { MainComponent } from './main.component';
|
||||
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 { tabResolverFn } from '@isa/core/tabs';
|
||||
import { provideScrollPositionRestoration } from '@isa/utils/scroll-position';
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: '', redirectTo: 'kunde/dashboard', pathMatch: 'full' },
|
||||
{
|
||||
path: 'login',
|
||||
children: [
|
||||
{ path: ':token', component: TokenLoginComponent },
|
||||
{ path: '**', redirectTo: 'kunde', pathMatch: 'full' },
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
canActivate: [IsAuthenticatedGuard],
|
||||
children: [
|
||||
{
|
||||
path: 'kunde',
|
||||
component: MainComponent,
|
||||
children: [
|
||||
{
|
||||
path: 'dashboard',
|
||||
loadChildren: () =>
|
||||
import('@page/dashboard').then((m) => m.DashboardModule),
|
||||
data: {
|
||||
matomo: {
|
||||
title: 'Dashboard',
|
||||
} as MatomoRouteData,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'product',
|
||||
loadChildren: () =>
|
||||
import('@page/catalog').then((m) => m.PageCatalogModule),
|
||||
canActivate: [CanActivateProductGuard],
|
||||
},
|
||||
{
|
||||
path: ':processId/product',
|
||||
loadChildren: () =>
|
||||
import('@page/catalog').then((m) => m.PageCatalogModule),
|
||||
canActivate: [CanActivateProductWithProcessIdGuard],
|
||||
resolve: { processId: ProcessIdResolver },
|
||||
},
|
||||
{
|
||||
path: 'order',
|
||||
loadChildren: () =>
|
||||
import('@page/customer-order').then((m) => m.CustomerOrderModule),
|
||||
canActivate: [CanActivateCustomerOrdersGuard],
|
||||
},
|
||||
{
|
||||
path: ':processId/order',
|
||||
loadChildren: () =>
|
||||
import('@page/customer-order').then((m) => m.CustomerOrderModule),
|
||||
canActivate: [CanActivateCustomerOrdersWithProcessIdGuard],
|
||||
resolve: { processId: ProcessIdResolver },
|
||||
},
|
||||
{
|
||||
path: 'customer',
|
||||
loadChildren: () =>
|
||||
import('@page/customer').then((m) => m.CustomerModule),
|
||||
canActivate: [CanActivateCustomerGuard],
|
||||
},
|
||||
{
|
||||
path: ':processId/customer',
|
||||
loadChildren: () =>
|
||||
import('@page/customer').then((m) => m.CustomerModule),
|
||||
canActivate: [CanActivateCustomerWithProcessIdGuard],
|
||||
resolve: { processId: ProcessIdResolver },
|
||||
},
|
||||
{
|
||||
path: 'cart',
|
||||
loadChildren: () =>
|
||||
import('@page/checkout').then((m) => m.PageCheckoutModule),
|
||||
canActivate: [CanActivateCartGuard],
|
||||
},
|
||||
{
|
||||
path: ':processId/cart',
|
||||
loadChildren: () =>
|
||||
import('@page/checkout').then((m) => m.PageCheckoutModule),
|
||||
canActivate: [CanActivateCartWithProcessIdGuard],
|
||||
},
|
||||
{
|
||||
path: 'pickup-shelf',
|
||||
canActivate: [ProcessIdGuard],
|
||||
// NOTE: This is a workaround for the canActivate guard not being called
|
||||
loadChildren: () =>
|
||||
import('@page/pickup-shelf').then((m) => m.PickupShelfOutModule),
|
||||
},
|
||||
{
|
||||
path: ':processId/pickup-shelf',
|
||||
canActivate: [ActivateProcessIdGuard],
|
||||
loadChildren: () =>
|
||||
import('@page/pickup-shelf').then((m) => m.PickupShelfOutModule),
|
||||
},
|
||||
{ path: '**', redirectTo: 'dashboard', pathMatch: 'full' },
|
||||
],
|
||||
resolve: { section: CustomerSectionResolver },
|
||||
},
|
||||
{
|
||||
path: 'filiale',
|
||||
component: MainComponent,
|
||||
children: [
|
||||
{
|
||||
path: 'task-calendar',
|
||||
loadChildren: () =>
|
||||
import('@page/task-calendar').then(
|
||||
(m) => m.PageTaskCalendarModule,
|
||||
),
|
||||
canActivate: [CanActivateTaskCalendarGuard],
|
||||
},
|
||||
{
|
||||
path: 'pickup-shelf',
|
||||
canActivate: [ActivateProcessIdWithConfigKeyGuard('pickupShelf')],
|
||||
// NOTE: This is a workaround for the canActivate guard not being called
|
||||
loadChildren: () =>
|
||||
import('@page/pickup-shelf').then((m) => m.PickupShelfInModule),
|
||||
},
|
||||
{
|
||||
path: 'goods/in',
|
||||
loadChildren: () =>
|
||||
import('@page/goods-in').then((m) => m.GoodsInModule),
|
||||
canActivate: [CanActivateGoodsInGuard],
|
||||
},
|
||||
// {
|
||||
// path: 'remission',
|
||||
// loadChildren: () =>
|
||||
// import('@page/remission').then((m) => m.PageRemissionModule),
|
||||
// canActivate: [CanActivateRemissionGuard],
|
||||
// },
|
||||
{
|
||||
path: 'package-inspection',
|
||||
loadChildren: () =>
|
||||
import('@page/package-inspection').then(
|
||||
(m) => m.PackageInspectionModule,
|
||||
),
|
||||
canActivate: [CanActivatePackageInspectionGuard],
|
||||
},
|
||||
{
|
||||
path: 'assortment',
|
||||
loadChildren: () =>
|
||||
import('@page/assortment').then((m) => m.AssortmentModule),
|
||||
canActivate: [CanActivateAssortmentGuard],
|
||||
},
|
||||
{ path: '**', redirectTo: 'task-calendar', pathMatch: 'full' },
|
||||
],
|
||||
resolve: { section: BranchSectionResolver },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: ':tabId',
|
||||
component: MainComponent,
|
||||
resolve: { process: tabResolverFn, tab: tabResolverFn },
|
||||
canActivate: [IsAuthenticatedGuard],
|
||||
children: [
|
||||
{
|
||||
path: 'return',
|
||||
loadChildren: () =>
|
||||
import('@isa/oms/feature/return-search').then((m) => m.routes),
|
||||
},
|
||||
{
|
||||
path: 'remission',
|
||||
children: [
|
||||
{
|
||||
path: 'return-receipt',
|
||||
loadChildren: () =>
|
||||
import(
|
||||
'@isa/remission/feature/remission-return-receipt-list'
|
||||
).then((m) => m.routes),
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
loadChildren: () =>
|
||||
import('@isa/remission/feature/remission-list').then(
|
||||
(m) => m.routes,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
if (isDevMode()) {
|
||||
routes.unshift({
|
||||
path: 'preview',
|
||||
component: PreviewComponent,
|
||||
});
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forRoot(routes, { bindToComponentInputs: true }),
|
||||
TokenLoginModule,
|
||||
],
|
||||
exports: [RouterModule],
|
||||
providers: [provideScrollPositionRestoration()],
|
||||
})
|
||||
export class AppRoutingModule {}
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
withInterceptorsFromDi,
|
||||
} from '@angular/common/http';
|
||||
import {
|
||||
DEFAULT_CURRENCY_CODE,
|
||||
ErrorHandler,
|
||||
Injector,
|
||||
LOCALE_ID,
|
||||
@@ -228,6 +229,10 @@ export function _notificationsHubOptionsFactory(
|
||||
withRouteData(),
|
||||
),
|
||||
provideLogging(withLogLevel(LogLevel.Debug), withSink(ConsoleLogSink)),
|
||||
{
|
||||
provide: DEFAULT_CURRENCY_CODE,
|
||||
useValue: 'EUR',
|
||||
},
|
||||
],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
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 { Component } from '@angular/core';
|
||||
import { BranchDTO } from '@generated/swagger/checkout-api';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
||||
@@ -9,9 +8,9 @@ import { BehaviorSubject } from 'rxjs';
|
||||
selector: 'app-preview',
|
||||
templateUrl: 'preview.component.html',
|
||||
styleUrls: ['preview.component.css'],
|
||||
imports: [CommonModule, BranchSelectorComponent, PlatformModule],
|
||||
imports: [CommonModule, PlatformModule],
|
||||
})
|
||||
export class PreviewComponent implements OnInit {
|
||||
export class PreviewComponent {
|
||||
selectedBranch$ = new BehaviorSubject<BranchDTO>({});
|
||||
|
||||
get appVersion() {
|
||||
@@ -24,7 +23,7 @@ export class PreviewComponent implements OnInit {
|
||||
|
||||
get navigator() {
|
||||
const nav = {};
|
||||
for (let i in window.navigator) nav[i] = navigator[i];
|
||||
for (const i in window.navigator) nav[i] = navigator[i];
|
||||
return nav;
|
||||
}
|
||||
|
||||
@@ -51,8 +50,6 @@ export class PreviewComponent implements OnInit {
|
||||
|
||||
constructor(private readonly _platform: Platform) {}
|
||||
|
||||
ngOnInit() {}
|
||||
|
||||
setNewBranch(branch: BranchDTO) {
|
||||
this.selectedBranch$.next(branch);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { ErrorHandler, Injectable } from '@angular/core';
|
||||
import { AuthService } from '@core/auth';
|
||||
import { DialogModel, UiDialogModalComponent, UiErrorModalComponent, UiModalService } from '@ui/modal';
|
||||
import { IsaLogProvider } from './isa.log-provider';
|
||||
import { LogLevel } from '@core/logger';
|
||||
import { HttpErrorResponse } from "@angular/common/http";
|
||||
import { ErrorHandler, Injectable } from "@angular/core";
|
||||
import { AuthService } from "@core/auth";
|
||||
import {
|
||||
DialogModel,
|
||||
UiDialogModalComponent,
|
||||
UiErrorModalComponent,
|
||||
UiModalService,
|
||||
} from "@ui/modal";
|
||||
import { IsaLogProvider } from "./isa.log-provider";
|
||||
import { LogLevel } from "@core/logger";
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
@Injectable({ providedIn: "root" })
|
||||
export class IsaErrorHandler implements ErrorHandler {
|
||||
constructor(
|
||||
private _modal: UiModalService,
|
||||
@@ -17,7 +22,7 @@ export class IsaErrorHandler implements ErrorHandler {
|
||||
console.error(error);
|
||||
|
||||
// Bei Klick auf Abbrechen auf der Login Seite erneut zur Login Seite weiterleiten
|
||||
if (error?.type === 'token_error') {
|
||||
if (error?.type === "token_error") {
|
||||
this._authService.login();
|
||||
return;
|
||||
}
|
||||
@@ -26,11 +31,14 @@ export class IsaErrorHandler implements ErrorHandler {
|
||||
await this._modal
|
||||
.open({
|
||||
content: UiDialogModalComponent,
|
||||
title: 'Sitzung abgelaufen',
|
||||
title: "Sitzung abgelaufen",
|
||||
data: {
|
||||
handleCommand: false,
|
||||
content: 'Sie waren zu lange nicht in der ISA aktiv. Bitte melden Sie sich erneut an',
|
||||
actions: [{ command: 'CLOSE', selected: true, label: 'Erneut anmelden' }],
|
||||
content:
|
||||
"Sie waren zu lange nicht in der ISA aktiv. Bitte melden Sie sich erneut an",
|
||||
actions: [
|
||||
{ command: "CLOSE", selected: true, label: "Erneut anmelden" },
|
||||
],
|
||||
} as DialogModel,
|
||||
})
|
||||
.afterClosed$.toPromise();
|
||||
@@ -39,7 +47,11 @@ export class IsaErrorHandler implements ErrorHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
this._isaLogProvider.log(LogLevel.ERROR, 'Client Error', error);
|
||||
try {
|
||||
this._isaLogProvider.log(LogLevel.ERROR, "Client Error", error);
|
||||
} catch (logError) {
|
||||
console.error("Error logging to IsaLogProvider:", logError);
|
||||
}
|
||||
|
||||
// this._modal.open({
|
||||
// content: UiErrorModalComponent,
|
||||
|
||||
@@ -1,28 +1,36 @@
|
||||
import { Injectable, Injector } from '@angular/core';
|
||||
import { LogLevel, LogProvider } from '@core/logger';
|
||||
import { UserStateService } from '@generated/swagger/isa-api';
|
||||
import { environment } from '../../environments/environment';
|
||||
import { Injectable } from "@angular/core";
|
||||
import { LogLevel, LogProvider } from "@core/logger";
|
||||
import { UserStateService } from "@generated/swagger/isa-api";
|
||||
import { environment } from "../../environments/environment";
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
@Injectable({ providedIn: "root" })
|
||||
export class IsaLogProvider implements LogProvider {
|
||||
static InfoService: UserStateService | undefined;
|
||||
|
||||
constructor() {}
|
||||
|
||||
log(logLevel: LogLevel, message: string, error: Error, ...optionalParams: any[]): void {
|
||||
if (!environment.production && (logLevel === LogLevel.WARN || logLevel === LogLevel.ERROR)) {
|
||||
IsaLogProvider.InfoService?.UserStateSaveLog({
|
||||
logType: logLevel,
|
||||
message: message,
|
||||
content: JSON.stringify({
|
||||
error: error?.name,
|
||||
message: error?.message,
|
||||
stack: error?.stack,
|
||||
data: optionalParams,
|
||||
}),
|
||||
})
|
||||
.toPromise()
|
||||
.catch(() => {});
|
||||
log(
|
||||
logLevel: LogLevel,
|
||||
message: string,
|
||||
error: Error,
|
||||
...optionalParams: any[]
|
||||
): void {
|
||||
try {
|
||||
if (
|
||||
!environment.production &&
|
||||
(logLevel === LogLevel.WARN || logLevel === LogLevel.ERROR)
|
||||
) {
|
||||
IsaLogProvider.InfoService?.UserStateSaveLog({
|
||||
logType: logLevel,
|
||||
message: message,
|
||||
content: JSON.stringify({
|
||||
error: error?.name,
|
||||
message: error?.message,
|
||||
stack: error?.stack,
|
||||
data: optionalParams,
|
||||
}),
|
||||
}).toPromise();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error logging to InfoService:", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Logger, LogLevel } from '@core/logger';
|
||||
import { Store } from '@ngrx/store';
|
||||
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';
|
||||
import { Injectable } from "@angular/core";
|
||||
import { Logger, LogLevel } from "@core/logger";
|
||||
import { Store } from "@ngrx/store";
|
||||
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' })
|
||||
@Injectable({ providedIn: "root" })
|
||||
export class RootStateService {
|
||||
static LOCAL_STORAGE_KEY = 'ISA_APP_INITIALSTATE';
|
||||
static LOCAL_STORAGE_KEY = "ISA_APP_INITIALSTATE";
|
||||
|
||||
#storage = injectStorage(UserStorageProvider);
|
||||
|
||||
@@ -29,14 +29,17 @@ export class RootStateService {
|
||||
);
|
||||
}
|
||||
|
||||
window['clearUserState'] = () => {
|
||||
window["clearUserState"] = () => {
|
||||
this.clear();
|
||||
};
|
||||
}
|
||||
|
||||
async init() {
|
||||
await this.load();
|
||||
this._store.dispatch({ type: 'HYDRATE', payload: RootStateService.LoadFromLocalStorage() });
|
||||
this._store.dispatch({
|
||||
type: "HYDRATE",
|
||||
payload: RootStateService.LoadFromLocalStorage(),
|
||||
});
|
||||
this.initSave();
|
||||
}
|
||||
|
||||
@@ -50,14 +53,10 @@ export class RootStateService {
|
||||
const data = {
|
||||
...state,
|
||||
version: packageInfo.version,
|
||||
sub: this._authService.getClaimByKey('sub'),
|
||||
sub: this._authService.getClaimByKey("sub"),
|
||||
};
|
||||
RootStateService.SaveToLocalStorageRaw(JSON.stringify(data));
|
||||
return this.#storage.set('state', {
|
||||
...state,
|
||||
version: packageInfo.version,
|
||||
sub: this._authService.getClaimByKey('sub'),
|
||||
});
|
||||
return this.#storage.set("state", data);
|
||||
}),
|
||||
)
|
||||
.subscribe();
|
||||
@@ -68,7 +67,7 @@ export class RootStateService {
|
||||
*/
|
||||
async load(): Promise<boolean> {
|
||||
try {
|
||||
const res = await this.#storage.get('state');
|
||||
const res = await this.#storage.get("state");
|
||||
|
||||
const storageContent = RootStateService.LoadFromLocalStorageRaw();
|
||||
|
||||
@@ -88,7 +87,7 @@ export class RootStateService {
|
||||
async clear() {
|
||||
try {
|
||||
this._cancelSave.next();
|
||||
await this.#storage.clear('state');
|
||||
await this.#storage.clear("state");
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
RootStateService.RemoveFromLocalStorage();
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
@@ -112,7 +111,7 @@ export class RootStateService {
|
||||
try {
|
||||
return JSON.parse(raw);
|
||||
} catch (error) {
|
||||
console.error('Error parsing local storage:', error);
|
||||
console.error("Error parsing local storage:", error);
|
||||
this.RemoveFromLocalStorage();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { LogLevel } from './log-level';
|
||||
import { LogLevel } from "./log-level";
|
||||
|
||||
export interface LogProvider {
|
||||
log(logLevel: LogLevel, message: string, ...optionalParams: any[]): void;
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ItemDTO, ListResponseArgsOfItemDTO, SearchService } from '@generated/swagger/cat-search-api';
|
||||
import {
|
||||
ItemDTO,
|
||||
ListResponseArgsOfItemDTO,
|
||||
SearchService,
|
||||
} from '@generated/swagger/cat-search-api';
|
||||
import {
|
||||
RemiService,
|
||||
StockService,
|
||||
@@ -18,7 +22,11 @@ import { memorize } from '@utils/common';
|
||||
import { Observable, of, throwError } from 'rxjs';
|
||||
import { catchError, map, shareReplay, switchMap } from 'rxjs/operators';
|
||||
import { RemissionListItem } from './defs';
|
||||
import { fromItemDto, mapFromReturnItemDTO, mapFromReturnSuggestionDTO } from './mappings';
|
||||
import {
|
||||
fromItemDto,
|
||||
mapFromReturnItemDTO,
|
||||
mapFromReturnSuggestionDTO,
|
||||
} from './mappings';
|
||||
import { Logger } from '@core/logger';
|
||||
import { RemissionPlacementType } from '@domain/remission';
|
||||
|
||||
@@ -204,7 +212,10 @@ export class DomainRemissionService {
|
||||
);
|
||||
}
|
||||
|
||||
getStockInformation(items: RemissionListItem[], recalculate: boolean = false) {
|
||||
getStockInformation(
|
||||
items: RemissionListItem[],
|
||||
recalculate: boolean = false,
|
||||
) {
|
||||
return this.getCurrentStock().pipe(
|
||||
switchMap((stock) =>
|
||||
this._stockService
|
||||
@@ -218,7 +229,8 @@ export class DomainRemissionService {
|
||||
map((res) => {
|
||||
const o = items.map((item) => {
|
||||
const stockInfo = res?.result?.find(
|
||||
(stockInfo) => stockInfo.itemId === +item.dto.product.catalogProductNumber,
|
||||
(stockInfo) =>
|
||||
stockInfo.itemId === +item.dto.product.catalogProductNumber,
|
||||
);
|
||||
|
||||
if (!stockInfo) {
|
||||
@@ -231,7 +243,8 @@ export class DomainRemissionService {
|
||||
return { ...item, ...defaultStockData };
|
||||
}
|
||||
|
||||
const availableStock = stockInfo.inStock - stockInfo.removedFromStock;
|
||||
const availableStock =
|
||||
stockInfo.inStock - stockInfo.removedFromStock;
|
||||
const inStock = availableStock < 0 ? 0 : availableStock;
|
||||
|
||||
let { remainingQuantity, remissionQuantity } = item;
|
||||
@@ -249,7 +262,12 @@ export class DomainRemissionService {
|
||||
}
|
||||
}
|
||||
|
||||
return { ...item, remainingQuantity, remissionQuantity, inStock };
|
||||
return {
|
||||
...item,
|
||||
remainingQuantity,
|
||||
remissionQuantity,
|
||||
inStock,
|
||||
};
|
||||
});
|
||||
|
||||
return o;
|
||||
@@ -259,7 +277,10 @@ export class DomainRemissionService {
|
||||
);
|
||||
}
|
||||
|
||||
getRequiredCapacities(params: { departments?: string[]; supplierId: number }) {
|
||||
getRequiredCapacities(params: {
|
||||
departments?: string[];
|
||||
supplierId: number;
|
||||
}) {
|
||||
return this.getCurrentStock().pipe(
|
||||
switchMap((stock) =>
|
||||
this._remiService
|
||||
@@ -301,13 +322,18 @@ export class DomainRemissionService {
|
||||
);
|
||||
}
|
||||
|
||||
canAddReturnItem(item: ReturnItemDTO): Observable<BatchResponseArgsOfReturnItemDTOAndReturnItemDTO> {
|
||||
canAddReturnItem(
|
||||
item: ReturnItemDTO,
|
||||
): Observable<BatchResponseArgsOfReturnItemDTOAndReturnItemDTO> {
|
||||
return this._remiService.RemiCanAddReturnItem({
|
||||
data: [item],
|
||||
});
|
||||
}
|
||||
|
||||
async createReturn(supplierId: number, returnGroup?: string): Promise<ReturnDTO> {
|
||||
async createReturn(
|
||||
supplierId: number,
|
||||
returnGroup?: string,
|
||||
): Promise<ReturnDTO> {
|
||||
const response = await this._returnService
|
||||
.ReturnCreateReturn({
|
||||
data: {
|
||||
@@ -343,7 +369,10 @@ export class DomainRemissionService {
|
||||
.toPromise();
|
||||
}
|
||||
|
||||
getReturns(params: { start?: Date; returncompleted: boolean }): Observable<ReturnDTO[]> {
|
||||
getReturns(params: {
|
||||
start?: Date;
|
||||
returncompleted: boolean;
|
||||
}): Observable<ReturnDTO[]> {
|
||||
const queryToken: ReturnQueryTokenDTO = {
|
||||
start: params.start?.toISOString(),
|
||||
filter: {
|
||||
@@ -360,13 +389,20 @@ export class DomainRemissionService {
|
||||
});
|
||||
|
||||
return this.getCurrentStock().pipe(
|
||||
switchMap((stock) => this._returnService.ReturnQueryReturns({ stockId: stock.id, queryToken })),
|
||||
switchMap((stock) =>
|
||||
this._returnService.ReturnQueryReturns({
|
||||
stockId: stock.id,
|
||||
queryToken,
|
||||
}),
|
||||
),
|
||||
map((res) => res.result),
|
||||
);
|
||||
}
|
||||
|
||||
getReturn(returnId: number): Observable<ReturnDTO> {
|
||||
return this._returnService.ReturnGetReturn({ returnId, eagerLoading: 3 }).pipe(map((res) => res.result));
|
||||
return this._returnService
|
||||
.ReturnGetReturn({ returnId, eagerLoading: 3 })
|
||||
.pipe(map((res) => res.result));
|
||||
}
|
||||
|
||||
async deleteReturn(returnId: number) {
|
||||
@@ -393,7 +429,11 @@ export class DomainRemissionService {
|
||||
inStock: number;
|
||||
}) {
|
||||
return this._returnService
|
||||
.ReturnAddReturnItem({ returnId, receiptId, data: { returnItemId, quantity, placementType, inStock } })
|
||||
.ReturnAddReturnItem({
|
||||
returnId,
|
||||
receiptId,
|
||||
data: { returnItemId, quantity, placementType, inStock },
|
||||
})
|
||||
.pipe(map((r) => r.result));
|
||||
}
|
||||
|
||||
@@ -420,7 +460,14 @@ export class DomainRemissionService {
|
||||
.ReturnAddReturnSuggestion({
|
||||
returnId,
|
||||
receiptId,
|
||||
data: { returnSuggestionId, quantity, placementType, inStock, impedimentComment, remainingQuantity },
|
||||
data: {
|
||||
returnSuggestionId,
|
||||
quantity,
|
||||
placementType,
|
||||
inStock,
|
||||
impedimentComment,
|
||||
remainingQuantity,
|
||||
},
|
||||
})
|
||||
.pipe(map((r) => r.result));
|
||||
}
|
||||
@@ -438,18 +485,28 @@ export class DomainRemissionService {
|
||||
receiptId: number;
|
||||
receiptItemId: number;
|
||||
}) {
|
||||
return this._returnService.ReturnRemoveReturnItem({ returnId, receiptItemId, receiptId });
|
||||
return this._returnService.ReturnRemoveReturnItem({
|
||||
returnId,
|
||||
receiptItemId,
|
||||
receiptId,
|
||||
});
|
||||
}
|
||||
|
||||
returnImpediment(itemId: number) {
|
||||
return this._returnService
|
||||
.ReturnReturnItemImpediment({ itemId, data: { comment: 'Produkt nicht gefunden' } })
|
||||
.ReturnReturnItemImpediment({
|
||||
itemId,
|
||||
data: { comment: 'Produkt nicht gefunden' },
|
||||
})
|
||||
.pipe(map((r) => r.result));
|
||||
}
|
||||
|
||||
returnSuggestion(itemId: number) {
|
||||
return this._returnService
|
||||
.ReturnReturnSuggestionImpediment({ itemId, data: { comment: 'Produkt nicht gefunden' } })
|
||||
.ReturnReturnSuggestionImpediment({
|
||||
itemId,
|
||||
data: { comment: 'Produkt nicht gefunden' },
|
||||
})
|
||||
.pipe(map((r) => r.result));
|
||||
}
|
||||
|
||||
@@ -459,7 +516,10 @@ export class DomainRemissionService {
|
||||
* @param receiptNumber Receipt number
|
||||
* @returns ReceiptDTO
|
||||
*/
|
||||
async createReceipt(returnDTO: ReturnDTO, receiptNumber?: string): Promise<ReceiptDTO> {
|
||||
async createReceipt(
|
||||
returnDTO: ReturnDTO,
|
||||
receiptNumber?: string,
|
||||
): Promise<ReceiptDTO> {
|
||||
const stock = await this._getStock();
|
||||
|
||||
const response = await this._returnService
|
||||
@@ -510,7 +570,10 @@ export class DomainRemissionService {
|
||||
return receipt;
|
||||
}
|
||||
|
||||
async completeReceipt(returnId: number, receiptId: number): Promise<ReceiptDTO> {
|
||||
async completeReceipt(
|
||||
returnId: number,
|
||||
receiptId: number,
|
||||
): Promise<ReceiptDTO> {
|
||||
const res = await this._returnService
|
||||
.ReturnFinalizeReceipt({
|
||||
returnId,
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
import { enableProdMode, isDevMode } from '@angular/core';
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
import { CONFIG_DATA } from '@isa/core/config';
|
||||
import { setDefaultOptions } from 'date-fns';
|
||||
import { de } from 'date-fns/locale';
|
||||
import * as moment from 'moment';
|
||||
import { enableProdMode, isDevMode } from "@angular/core";
|
||||
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
|
||||
import { CONFIG_DATA } from "@isa/core/config";
|
||||
import { setDefaultOptions } from "date-fns";
|
||||
import { de } from "date-fns/locale";
|
||||
import * as moment from "moment";
|
||||
import "moment/locale/de";
|
||||
|
||||
setDefaultOptions({ locale: de });
|
||||
moment.locale('de');
|
||||
moment.locale("de");
|
||||
|
||||
import { AppModule } from './app/app.module';
|
||||
import { AppModule } from "./app/app.module";
|
||||
|
||||
if (!isDevMode()) {
|
||||
enableProdMode();
|
||||
}
|
||||
|
||||
async function bootstrap() {
|
||||
const configRes = await fetch('/config/config.json');
|
||||
const configRes = await fetch("/config/config.json");
|
||||
|
||||
const config = await configRes.json();
|
||||
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
<div class="notification-list scroll-bar">
|
||||
@for (notification of notifications; track notification) {
|
||||
<modal-notifications-list-item [item]="notification" (itemSelected)="itemSelected($event)"></modal-notifications-list-item>
|
||||
<modal-notifications-list-item
|
||||
[item]="notification"
|
||||
(itemSelected)="itemSelected($event)"
|
||||
></modal-notifications-list-item>
|
||||
<hr />
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<a class="cta-primary" [routerLink]="['/filiale/remission/create']" (click)="navigated.emit()">Zur Remission</a>
|
||||
<a
|
||||
class="cta-primary"
|
||||
[routerLink]="remissionPath()"
|
||||
(click)="navigated.emit()"
|
||||
>Zur Remission</a
|
||||
>
|
||||
</div>
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output, inject } from '@angular/core';
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
EventEmitter,
|
||||
Input,
|
||||
Output,
|
||||
inject,
|
||||
linkedSignal,
|
||||
} from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { PickupShelfInNavigationService } from '@shared/services/navigation';
|
||||
import { UiFilter } from '@ui/filter';
|
||||
import { MessageBoardItemDTO } from '@hub/notifications';
|
||||
import { TabService } from '@isa/core/tabs';
|
||||
|
||||
@Component({
|
||||
selector: 'modal-notifications-remission-group',
|
||||
@@ -11,7 +20,10 @@ import { MessageBoardItemDTO } from '@hub/notifications';
|
||||
standalone: false,
|
||||
})
|
||||
export class ModalNotificationsRemissionGroupComponent {
|
||||
private _pickupShelfInNavigationService = inject(PickupShelfInNavigationService);
|
||||
tabService = inject(TabService);
|
||||
private _pickupShelfInNavigationService = inject(
|
||||
PickupShelfInNavigationService,
|
||||
);
|
||||
|
||||
@Input()
|
||||
notifications: MessageBoardItemDTO[];
|
||||
@@ -19,11 +31,19 @@ export class ModalNotificationsRemissionGroupComponent {
|
||||
@Output()
|
||||
navigated = new EventEmitter<void>();
|
||||
|
||||
remissionPath = linkedSignal(() => [
|
||||
'/',
|
||||
this.tabService.activatedTab()?.id || this.tabService.nextId(),
|
||||
'remission',
|
||||
]);
|
||||
|
||||
constructor(private _router: Router) {}
|
||||
|
||||
itemSelected(item: MessageBoardItemDTO) {
|
||||
const defaultNav = this._pickupShelfInNavigationService.listRoute();
|
||||
const queryParams = UiFilter.getQueryParamsFromQueryTokenDTO(item.queryToken);
|
||||
const queryParams = UiFilter.getQueryParamsFromQueryTokenDTO(
|
||||
item.queryToken,
|
||||
);
|
||||
this._router.navigate(defaultNav.path, {
|
||||
queryParams: {
|
||||
...defaultNav.queryParams,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<ng-container *ifRole="'Store'">
|
||||
<!-- <ng-container *ifRole="'Store'">
|
||||
@if (customerType !== 'b2b') {
|
||||
<shared-checkbox
|
||||
[ngModel]="p4mUser"
|
||||
@@ -8,15 +8,17 @@
|
||||
Kundenkarte
|
||||
</shared-checkbox>
|
||||
}
|
||||
</ng-container>
|
||||
</ng-container> -->
|
||||
@for (option of filteredOptions$ | async; track option) {
|
||||
@if (option?.enabled !== false) {
|
||||
<shared-checkbox
|
||||
[ngModel]="option.value === customerType"
|
||||
(ngModelChange)="setValue({ customerType: $event ? option.value : undefined })"
|
||||
(ngModelChange)="
|
||||
setValue({ customerType: $event ? option.value : undefined })
|
||||
"
|
||||
[disabled]="isOptionDisabled(option)"
|
||||
[name]="option.value"
|
||||
>
|
||||
>
|
||||
{{ option.label }}
|
||||
</shared-checkbox>
|
||||
}
|
||||
|
||||
@@ -21,7 +21,13 @@ import { OptionDTO } from '@generated/swagger/checkout-api';
|
||||
import { UiCheckboxComponent } from '@ui/checkbox';
|
||||
import { first, isBoolean, isString } from 'lodash';
|
||||
import { combineLatest, Observable, Subject } from 'rxjs';
|
||||
import { distinctUntilChanged, filter, map, shareReplay, switchMap } from 'rxjs/operators';
|
||||
import {
|
||||
distinctUntilChanged,
|
||||
filter,
|
||||
map,
|
||||
shareReplay,
|
||||
switchMap,
|
||||
} from 'rxjs/operators';
|
||||
|
||||
export interface CustomerTypeSelectorState {
|
||||
processId: number;
|
||||
@@ -58,18 +64,18 @@ export class CustomerTypeSelectorComponent
|
||||
|
||||
@Input()
|
||||
get value() {
|
||||
if (this.p4mUser) {
|
||||
return `${this.customerType}-p4m`;
|
||||
}
|
||||
// if (this.p4mUser) {
|
||||
// return `${this.customerType}-p4m`;
|
||||
// }
|
||||
return this.customerType;
|
||||
}
|
||||
set value(value: string) {
|
||||
if (value.includes('-p4m')) {
|
||||
this.p4mUser = true;
|
||||
this.customerType = value.replace('-p4m', '');
|
||||
} else {
|
||||
this.customerType = value;
|
||||
}
|
||||
// if (value.includes('-p4m')) {
|
||||
// this.p4mUser = true;
|
||||
// this.customerType = value.replace('-p4m', '');
|
||||
// } else {
|
||||
this.customerType = value;
|
||||
// }
|
||||
}
|
||||
|
||||
@Output()
|
||||
@@ -111,30 +117,35 @@ export class CustomerTypeSelectorComponent
|
||||
get filteredOptions$() {
|
||||
const options$ = this.select((s) => s.options).pipe(distinctUntilChanged());
|
||||
const p4mUser$ = this.select((s) => s.p4mUser).pipe(distinctUntilChanged());
|
||||
const customerType$ = this.select((s) => s.customerType).pipe(distinctUntilChanged());
|
||||
const customerType$ = this.select((s) => s.customerType).pipe(
|
||||
distinctUntilChanged(),
|
||||
);
|
||||
return combineLatest([options$, p4mUser$, customerType$]).pipe(
|
||||
filter(([options]) => options?.length > 0),
|
||||
map(([options, p4mUser, customerType]) => {
|
||||
const initial = { p4mUser: this.p4mUser, customerType: this.customerType };
|
||||
const initial = {
|
||||
p4mUser: this.p4mUser,
|
||||
customerType: this.customerType,
|
||||
};
|
||||
let result: OptionDTO[] = options;
|
||||
if (p4mUser) {
|
||||
result = result.filter((o) => o.value === 'store' || (o.value === 'webshop' && o.enabled !== false));
|
||||
// if (p4mUser) {
|
||||
// result = result.filter((o) => o.value === 'store' || (o.value === 'webshop' && o.enabled !== false));
|
||||
|
||||
result = result.map((o) => {
|
||||
if (o.value === 'store') {
|
||||
return { ...o, enabled: false };
|
||||
}
|
||||
return o;
|
||||
});
|
||||
}
|
||||
// result = result.map((o) => {
|
||||
// if (o.value === 'store') {
|
||||
// return { ...o, enabled: false };
|
||||
// }
|
||||
// return o;
|
||||
// });
|
||||
// }
|
||||
|
||||
if (customerType === 'b2b' && this.p4mUser) {
|
||||
this.p4mUser = false;
|
||||
}
|
||||
|
||||
if (initial.p4mUser !== this.p4mUser || initial.customerType !== this.customerType) {
|
||||
this.setValue({ customerType: this.customerType, p4mUser: this.p4mUser });
|
||||
}
|
||||
// if (initial.p4mUser !== this.p4mUser || initial.customerType !== this.customerType) {
|
||||
// this.setValue({ customerType: this.customerType, p4mUser: this.p4mUser });
|
||||
// }
|
||||
|
||||
return result;
|
||||
}),
|
||||
@@ -224,42 +235,53 @@ export class CustomerTypeSelectorComponent
|
||||
if (typeof value === 'string') {
|
||||
this.value = value;
|
||||
} else {
|
||||
if (isBoolean(value.p4mUser)) {
|
||||
this.p4mUser = value.p4mUser;
|
||||
}
|
||||
// if (isBoolean(value.p4mUser)) {
|
||||
// this.p4mUser = value.p4mUser;
|
||||
// }
|
||||
if (isString(value.customerType)) {
|
||||
this.customerType = value.customerType;
|
||||
} else if (this.p4mUser) {
|
||||
// Implementierung wie im PBI #3467 beschrieben
|
||||
// wenn customerType nicht gesetzt wird und p4mUser true ist,
|
||||
// dann customerType auf store setzen.
|
||||
// wenn dies nicht möglich ist da der Warenkob keinen store Kunden zulässt,
|
||||
// dann customerType auf webshop setzen.
|
||||
// wenn dies nicht möglich ist da der Warenkob keinen webshop Kunden zulässt,
|
||||
// dann customerType auf den ersten verfügbaren setzen und p4mUser auf false setzen.
|
||||
if (this.enabledOptions.some((o) => o.value === 'store')) {
|
||||
this.customerType = 'store';
|
||||
} else if (this.enabledOptions.some((o) => o.value === 'webshop')) {
|
||||
this.customerType = 'webshop';
|
||||
} else {
|
||||
this.p4mUser = false;
|
||||
const includesGuest = this.enabledOptions.some((o) => o.value === 'guest');
|
||||
this.customerType = includesGuest ? 'guest' : first(this.enabledOptions)?.value;
|
||||
}
|
||||
// } else if (this.p4mUser) {
|
||||
// // Implementierung wie im PBI #3467 beschrieben
|
||||
// // wenn customerType nicht gesetzt wird und p4mUser true ist,
|
||||
// // dann customerType auf store setzen.
|
||||
// // wenn dies nicht möglich ist da der Warenkob keinen store Kunden zulässt,
|
||||
// // dann customerType auf webshop setzen.
|
||||
// // wenn dies nicht möglich ist da der Warenkob keinen webshop Kunden zulässt,
|
||||
// // dann customerType auf den ersten verfügbaren setzen und p4mUser auf false setzen.
|
||||
// if (this.enabledOptions.some((o) => o.value === 'store')) {
|
||||
// this.customerType = 'store';
|
||||
// } else if (this.enabledOptions.some((o) => o.value === 'webshop')) {
|
||||
// this.customerType = 'webshop';
|
||||
// } else {
|
||||
// this.p4mUser = false;
|
||||
// const includesGuest = this.enabledOptions.some(
|
||||
// (o) => o.value === 'guest',
|
||||
// );
|
||||
// this.customerType = includesGuest
|
||||
// ? 'guest'
|
||||
// : first(this.enabledOptions)?.value;
|
||||
// }
|
||||
} else {
|
||||
// wenn customerType nicht gesetzt wird und p4mUser false ist,
|
||||
// dann customerType auf den ersten verfügbaren setzen der nicht mit dem aktuellen customerType übereinstimmt.
|
||||
this.customerType =
|
||||
first(this.enabledOptions.filter((o) => o.value === this.customerType))?.value ?? this.customerType;
|
||||
first(
|
||||
this.enabledOptions.filter((o) => o.value === this.customerType),
|
||||
)?.value ?? this.customerType;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.customerType !== initial.customerType || this.p4mUser !== initial.p4mUser) {
|
||||
if (
|
||||
this.customerType !== initial.customerType ||
|
||||
this.p4mUser !== initial.p4mUser
|
||||
) {
|
||||
this.onChange(this.value);
|
||||
this.onTouched();
|
||||
this.valueChanges.emit(this.value);
|
||||
}
|
||||
|
||||
this.checkboxes?.find((c) => c.name === this.customerType)?.writeValue(true);
|
||||
this.checkboxes
|
||||
?.find((c) => c.name === this.customerType)
|
||||
?.writeValue(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,6 @@ export * from './interests';
|
||||
export * from './name';
|
||||
export * from './newsletter';
|
||||
export * from './organisation';
|
||||
export * from './p4m-number';
|
||||
// export * from './p4m-number';
|
||||
export * from './phone-numbers';
|
||||
export * from './form-block';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// start:ng42.barrel
|
||||
export * from './p4m-number-form-block.component';
|
||||
export * from './p4m-number-form-block.module';
|
||||
// end:ng42.barrel
|
||||
// // start:ng42.barrel
|
||||
// export * from './p4m-number-form-block.component';
|
||||
// export * from './p4m-number-form-block.module';
|
||||
// // end:ng42.barrel
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<shared-form-control label="Kundenkartencode" class="flex-grow">
|
||||
<!-- <shared-form-control label="Kundenkartencode" class="flex-grow">
|
||||
<input
|
||||
placeholder="Kundenkartencode"
|
||||
class="input-control"
|
||||
@@ -13,4 +13,4 @@
|
||||
<button type="button" (click)="scan()">
|
||||
<shared-icon icon="barcode-scan" [size]="32"></shared-icon>
|
||||
</button>
|
||||
}
|
||||
} -->
|
||||
|
||||
@@ -1,49 +1,49 @@
|
||||
import { Component, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
|
||||
import { UntypedFormControl, Validators } from '@angular/forms';
|
||||
import { FormBlockControl } from '../form-block';
|
||||
import { ScanAdapterService } from '@adapter/scan';
|
||||
// import { Component, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
|
||||
// import { UntypedFormControl, Validators } from '@angular/forms';
|
||||
// import { FormBlockControl } from '../form-block';
|
||||
// import { ScanAdapterService } from '@adapter/scan';
|
||||
|
||||
@Component({
|
||||
selector: 'app-p4m-number-form-block',
|
||||
templateUrl: 'p4m-number-form-block.component.html',
|
||||
styleUrls: ['p4m-number-form-block.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: false,
|
||||
})
|
||||
export class P4mNumberFormBlockComponent extends FormBlockControl<string> {
|
||||
get tabIndexEnd() {
|
||||
return this.tabIndexStart;
|
||||
}
|
||||
// @Component({
|
||||
// selector: 'app-p4m-number-form-block',
|
||||
// templateUrl: 'p4m-number-form-block.component.html',
|
||||
// styleUrls: ['p4m-number-form-block.component.scss'],
|
||||
// changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
// standalone: false,
|
||||
// })
|
||||
// export class P4mNumberFormBlockComponent extends FormBlockControl<string> {
|
||||
// get tabIndexEnd() {
|
||||
// return this.tabIndexStart;
|
||||
// }
|
||||
|
||||
constructor(
|
||||
private scanAdapter: ScanAdapterService,
|
||||
private changeDetectorRef: ChangeDetectorRef,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
// constructor(
|
||||
// private scanAdapter: ScanAdapterService,
|
||||
// private changeDetectorRef: ChangeDetectorRef,
|
||||
// ) {
|
||||
// super();
|
||||
// }
|
||||
|
||||
updateValidators(): void {
|
||||
this.control.setValidators([...this.getValidatorFn()]);
|
||||
this.control.setAsyncValidators(this.getAsyncValidatorFn());
|
||||
this.control.updateValueAndValidity();
|
||||
}
|
||||
// updateValidators(): void {
|
||||
// this.control.setValidators([...this.getValidatorFn()]);
|
||||
// this.control.setAsyncValidators(this.getAsyncValidatorFn());
|
||||
// this.control.updateValueAndValidity();
|
||||
// }
|
||||
|
||||
initializeControl(data?: string): void {
|
||||
this.control = new UntypedFormControl(data ?? '', [Validators.required], this.getAsyncValidatorFn());
|
||||
}
|
||||
// initializeControl(data?: string): void {
|
||||
// this.control = new UntypedFormControl(data ?? '', [Validators.required], this.getAsyncValidatorFn());
|
||||
// }
|
||||
|
||||
_patchValue(update: { previous: string; current: string }): void {
|
||||
this.control.patchValue(update.current);
|
||||
}
|
||||
// _patchValue(update: { previous: string; current: string }): void {
|
||||
// this.control.patchValue(update.current);
|
||||
// }
|
||||
|
||||
scan() {
|
||||
this.scanAdapter.scan().subscribe((result) => {
|
||||
this.control.patchValue(result);
|
||||
this.changeDetectorRef.markForCheck();
|
||||
});
|
||||
}
|
||||
// scan() {
|
||||
// this.scanAdapter.scan().subscribe((result) => {
|
||||
// this.control.patchValue(result);
|
||||
// this.changeDetectorRef.markForCheck();
|
||||
// });
|
||||
// }
|
||||
|
||||
canScan() {
|
||||
return this.scanAdapter.isReady();
|
||||
}
|
||||
}
|
||||
// canScan() {
|
||||
// return this.scanAdapter.isReady();
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
// import { NgModule } from '@angular/core';
|
||||
// import { CommonModule } from '@angular/common';
|
||||
|
||||
import { P4mNumberFormBlockComponent } from './p4m-number-form-block.component';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { IconComponent } from '@shared/components/icon';
|
||||
import { FormControlComponent } from '@shared/components/form-control';
|
||||
// import { P4mNumberFormBlockComponent } from './p4m-number-form-block.component';
|
||||
// import { ReactiveFormsModule } from '@angular/forms';
|
||||
// import { IconComponent } from '@shared/components/icon';
|
||||
// import { FormControlComponent } from '@shared/components/form-control';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, ReactiveFormsModule, FormControlComponent, IconComponent],
|
||||
exports: [P4mNumberFormBlockComponent],
|
||||
declarations: [P4mNumberFormBlockComponent],
|
||||
})
|
||||
export class P4mNumberFormBlockModule {}
|
||||
// @NgModule({
|
||||
// imports: [CommonModule, ReactiveFormsModule, FormControlComponent, IconComponent],
|
||||
// exports: [P4mNumberFormBlockComponent],
|
||||
// declarations: [P4mNumberFormBlockComponent],
|
||||
// })
|
||||
// export class P4mNumberFormBlockModule {}
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { ChangeDetectorRef, Directive, OnDestroy, OnInit, ViewChild, inject } from '@angular/core';
|
||||
import {
|
||||
ChangeDetectorRef,
|
||||
Directive,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
ViewChild,
|
||||
inject,
|
||||
} from '@angular/core';
|
||||
import {
|
||||
AbstractControl,
|
||||
AsyncValidatorFn,
|
||||
@@ -11,7 +18,12 @@ import {
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { CrmCustomerService } from '@domain/crm';
|
||||
import { AddressDTO, CustomerDTO, PayerDTO, ShippingAddressDTO } from '@generated/swagger/crm-api';
|
||||
import {
|
||||
AddressDTO,
|
||||
CustomerDTO,
|
||||
PayerDTO,
|
||||
ShippingAddressDTO,
|
||||
} from '@generated/swagger/crm-api';
|
||||
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
|
||||
import { UiValidators } from '@ui/validators';
|
||||
import { isNull } from 'lodash';
|
||||
@@ -42,7 +54,10 @@ import {
|
||||
mapCustomerInfoDtoToCustomerCreateFormData,
|
||||
} from './customer-create-form-data';
|
||||
import { AddressSelectionModalService } from '../modals';
|
||||
import { CustomerCreateNavigation, CustomerSearchNavigation } from '@shared/services/navigation';
|
||||
import {
|
||||
CustomerCreateNavigation,
|
||||
CustomerSearchNavigation,
|
||||
} from '@shared/services/navigation';
|
||||
|
||||
@Directive()
|
||||
export abstract class AbstractCreateCustomer implements OnInit, OnDestroy {
|
||||
@@ -104,7 +119,12 @@ export abstract class AbstractCreateCustomer implements OnInit, OnDestroy {
|
||||
);
|
||||
|
||||
this.processId$
|
||||
.pipe(startWith(undefined), bufferCount(2, 1), takeUntil(this.onDestroy$), delay(100))
|
||||
.pipe(
|
||||
startWith(undefined),
|
||||
bufferCount(2, 1),
|
||||
takeUntil(this.onDestroy$),
|
||||
delay(100),
|
||||
)
|
||||
.subscribe(async ([previous, current]) => {
|
||||
if (previous === undefined) {
|
||||
await this._initFormData();
|
||||
@@ -155,7 +175,10 @@ export abstract class AbstractCreateCustomer implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
async addOrUpdateBreadcrumb(processId: number, formData: CustomerCreateFormData) {
|
||||
async addOrUpdateBreadcrumb(
|
||||
processId: number,
|
||||
formData: CustomerCreateFormData,
|
||||
) {
|
||||
await this.breadcrumb.addOrUpdateBreadcrumbIfNotExists({
|
||||
key: processId,
|
||||
name: 'Kundendaten erfassen',
|
||||
@@ -195,7 +218,10 @@ export abstract class AbstractCreateCustomer implements OnInit, OnDestroy {
|
||||
console.log('customerTypeChanged', customerType);
|
||||
}
|
||||
|
||||
addFormBlock(key: keyof CustomerCreateFormData, block: FormBlock<any, AbstractControl>) {
|
||||
addFormBlock(
|
||||
key: keyof CustomerCreateFormData,
|
||||
block: FormBlock<any, AbstractControl>,
|
||||
) {
|
||||
this.form.addControl(key, block.control);
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
@@ -232,7 +258,10 @@ export abstract class AbstractCreateCustomer implements OnInit, OnDestroy {
|
||||
return true;
|
||||
}
|
||||
// Check Year + Month
|
||||
else if (inputDate.getFullYear() === minBirthDate.getFullYear() && inputDate.getMonth() < minBirthDate.getMonth()) {
|
||||
else if (
|
||||
inputDate.getFullYear() === minBirthDate.getFullYear() &&
|
||||
inputDate.getMonth() < minBirthDate.getMonth()
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
// Check Year + Month + Day
|
||||
@@ -279,70 +308,80 @@ export abstract class AbstractCreateCustomer implements OnInit, OnDestroy {
|
||||
);
|
||||
};
|
||||
|
||||
checkLoyalityCardValidator: AsyncValidatorFn = (control) => {
|
||||
return of(control.value).pipe(
|
||||
delay(500),
|
||||
mergeMap((value) => {
|
||||
const customerId = this.formData?._meta?.customerDto?.id ?? this.formData?._meta?.customerInfoDto?.id;
|
||||
return this.customerService.checkLoyaltyCard({ loyaltyCardNumber: value, customerId }).pipe(
|
||||
map((response) => {
|
||||
if (response.error) {
|
||||
throw response.message;
|
||||
}
|
||||
// checkLoyalityCardValidator: AsyncValidatorFn = (control) => {
|
||||
// return of(control.value).pipe(
|
||||
// delay(500),
|
||||
// mergeMap((value) => {
|
||||
// const customerId = this.formData?._meta?.customerDto?.id ?? this.formData?._meta?.customerInfoDto?.id;
|
||||
// return this.customerService.checkLoyaltyCard({ loyaltyCardNumber: value, customerId }).pipe(
|
||||
// map((response) => {
|
||||
// if (response.error) {
|
||||
// throw response.message;
|
||||
// }
|
||||
|
||||
/**
|
||||
* #4485 Kubi // Verhalten mit angelegte aber nicht verknüpfte Kundenkartencode in Kundensuche und Kundendaten erfassen ist nicht gleich
|
||||
* Fall1: Kundenkarte hat Daten in point4more:
|
||||
* Sobald Kundenkartencode in Feld "Kundenkartencode" reingegeben wird- werden die Daten von point4more in Formular "Kundendaten Erfassen" eingefügt und ersetzen (im Ganzen, nicht inkremental) die Daten in Felder, falls welche schon reingetippt werden.
|
||||
* Fall2: Kundenkarte hat keine Daten in point4more:
|
||||
* Sobald Kundenkartencode in Feld "Kundenkartencode" reingegeben wird- bleiben die Daten in Formular "Kundendaten Erfassen" in Felder, falls welche schon reingetippt werden.
|
||||
*/
|
||||
if (response.result && response.result.customer) {
|
||||
const customer = response.result.customer;
|
||||
const data = mapCustomerInfoDtoToCustomerCreateFormData(customer);
|
||||
// /**
|
||||
// * #4485 Kubi // Verhalten mit angelegte aber nicht verknüpfte Kundenkartencode in Kundensuche und Kundendaten erfassen ist nicht gleich
|
||||
// * Fall1: Kundenkarte hat Daten in point4more:
|
||||
// * Sobald Kundenkartencode in Feld "Kundenkartencode" reingegeben wird- werden die Daten von point4more in Formular "Kundendaten Erfassen" eingefügt und ersetzen (im Ganzen, nicht inkremental) die Daten in Felder, falls welche schon reingetippt werden.
|
||||
// * Fall2: Kundenkarte hat keine Daten in point4more:
|
||||
// * Sobald Kundenkartencode in Feld "Kundenkartencode" reingegeben wird- bleiben die Daten in Formular "Kundendaten Erfassen" in Felder, falls welche schon reingetippt werden.
|
||||
// */
|
||||
// if (response.result && response.result.customer) {
|
||||
// const customer = response.result.customer;
|
||||
// const data = mapCustomerInfoDtoToCustomerCreateFormData(customer);
|
||||
|
||||
if (data.name.firstName && data.name.lastName) {
|
||||
// Fall1
|
||||
this._formData.next(data);
|
||||
} else {
|
||||
// Fall2 Hier müssen die Metadaten gesetzt werden um eine verknüfung zur kundenkarte zu ermöglichen.
|
||||
const current = this.formData;
|
||||
current._meta = data._meta;
|
||||
current.p4m = data.p4m;
|
||||
}
|
||||
}
|
||||
// if (data.name.firstName && data.name.lastName) {
|
||||
// // Fall1
|
||||
// this._formData.next(data);
|
||||
// } else {
|
||||
// // Fall2 Hier müssen die Metadaten gesetzt werden um eine verknüfung zur kundenkarte zu ermöglichen.
|
||||
// const current = this.formData;
|
||||
// current._meta = data._meta;
|
||||
// current.p4m = data.p4m;
|
||||
// }
|
||||
// }
|
||||
|
||||
return null;
|
||||
}),
|
||||
catchError((error) => {
|
||||
if (error instanceof HttpErrorResponse) {
|
||||
if (error?.error?.invalidProperties?.loyaltyCardNumber) {
|
||||
return of({ invalid: error.error.invalidProperties.loyaltyCardNumber });
|
||||
} else {
|
||||
return of({ invalid: 'Kundenkartencode ist ungültig' });
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
}),
|
||||
tap(() => {
|
||||
control.markAsTouched();
|
||||
this.cdr.markForCheck();
|
||||
}),
|
||||
);
|
||||
};
|
||||
// return null;
|
||||
// }),
|
||||
// catchError((error) => {
|
||||
// if (error instanceof HttpErrorResponse) {
|
||||
// if (error?.error?.invalidProperties?.loyaltyCardNumber) {
|
||||
// return of({ invalid: error.error.invalidProperties.loyaltyCardNumber });
|
||||
// } else {
|
||||
// return of({ invalid: 'Kundenkartencode ist ungültig' });
|
||||
// }
|
||||
// }
|
||||
// }),
|
||||
// );
|
||||
// }),
|
||||
// tap(() => {
|
||||
// control.markAsTouched();
|
||||
// this.cdr.markForCheck();
|
||||
// }),
|
||||
// );
|
||||
// };
|
||||
|
||||
async navigateToCustomerDetails(customer: CustomerDTO) {
|
||||
const processId = await this.processId$.pipe(first()).toPromise();
|
||||
const route = this.customerSearchNavigation.detailsRoute({ processId, customerId: customer.id, customer });
|
||||
const route = this.customerSearchNavigation.detailsRoute({
|
||||
processId,
|
||||
customerId: customer.id,
|
||||
customer,
|
||||
});
|
||||
|
||||
return this.router.navigate(route.path, { queryParams: route.urlTree.queryParams });
|
||||
return this.router.navigate(route.path, {
|
||||
queryParams: route.urlTree.queryParams,
|
||||
});
|
||||
}
|
||||
|
||||
async validateAddressData(address: AddressDTO): Promise<AddressDTO> {
|
||||
const addressValidationResult = await this.addressVlidationModal.validateAddress(address);
|
||||
const addressValidationResult =
|
||||
await this.addressVlidationModal.validateAddress(address);
|
||||
|
||||
if (addressValidationResult !== undefined && (addressValidationResult as any) !== 'continue') {
|
||||
if (
|
||||
addressValidationResult !== undefined &&
|
||||
(addressValidationResult as any) !== 'continue'
|
||||
) {
|
||||
address = addressValidationResult;
|
||||
}
|
||||
|
||||
@@ -389,7 +428,9 @@ export abstract class AbstractCreateCustomer implements OnInit, OnDestroy {
|
||||
} catch (error) {
|
||||
this.form.enable();
|
||||
setTimeout(() => {
|
||||
this.addressFormBlock.setAddressValidationError(error.error.invalidProperties);
|
||||
this.addressFormBlock.setAddressValidationError(
|
||||
error.error.invalidProperties,
|
||||
);
|
||||
}, 10);
|
||||
|
||||
return;
|
||||
@@ -397,7 +438,10 @@ export abstract class AbstractCreateCustomer implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
if (data.birthDate && isNull(UiValidators.date(new UntypedFormControl(data.birthDate)))) {
|
||||
if (
|
||||
data.birthDate &&
|
||||
isNull(UiValidators.date(new UntypedFormControl(data.birthDate)))
|
||||
) {
|
||||
customer.dateOfBirth = data.birthDate;
|
||||
}
|
||||
|
||||
@@ -406,11 +450,15 @@ export abstract class AbstractCreateCustomer implements OnInit, OnDestroy {
|
||||
|
||||
if (this.validateShippingAddress) {
|
||||
try {
|
||||
billingAddress.address = await this.validateAddressData(billingAddress.address);
|
||||
billingAddress.address = await this.validateAddressData(
|
||||
billingAddress.address,
|
||||
);
|
||||
} catch (error) {
|
||||
this.form.enable();
|
||||
setTimeout(() => {
|
||||
this.addressFormBlock.setAddressValidationError(error.error.invalidProperties);
|
||||
this.addressFormBlock.setAddressValidationError(
|
||||
error.error.invalidProperties,
|
||||
);
|
||||
}, 10);
|
||||
|
||||
return;
|
||||
@@ -426,15 +474,21 @@ export abstract class AbstractCreateCustomer implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
if (data.deviatingDeliveryAddress?.deviatingAddress) {
|
||||
const shippingAddress = this.mapToShippingAddress(data.deviatingDeliveryAddress);
|
||||
const shippingAddress = this.mapToShippingAddress(
|
||||
data.deviatingDeliveryAddress,
|
||||
);
|
||||
|
||||
if (this.validateShippingAddress) {
|
||||
try {
|
||||
shippingAddress.address = await this.validateAddressData(shippingAddress.address);
|
||||
shippingAddress.address = await this.validateAddressData(
|
||||
shippingAddress.address,
|
||||
);
|
||||
} catch (error) {
|
||||
this.form.enable();
|
||||
setTimeout(() => {
|
||||
this.deviatingDeliveryAddressFormBlock.setAddressValidationError(error.error.invalidProperties);
|
||||
this.deviatingDeliveryAddressFormBlock.setAddressValidationError(
|
||||
error.error.invalidProperties,
|
||||
);
|
||||
}, 10);
|
||||
|
||||
return;
|
||||
@@ -474,7 +528,13 @@ export abstract class AbstractCreateCustomer implements OnInit, OnDestroy {
|
||||
};
|
||||
}
|
||||
|
||||
mapToBillingAddress({ name, address, email, organisation, phoneNumbers }: DeviatingAddressFormBlockData): PayerDTO {
|
||||
mapToBillingAddress({
|
||||
name,
|
||||
address,
|
||||
email,
|
||||
organisation,
|
||||
phoneNumbers,
|
||||
}: DeviatingAddressFormBlockData): PayerDTO {
|
||||
return {
|
||||
gender: name?.gender,
|
||||
title: name?.title,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CreateB2BCustomerModule } from './create-b2b-customer/create-b2b-customer.module';
|
||||
import { CreateGuestCustomerModule } from './create-guest-customer';
|
||||
import { CreateP4MCustomerModule } from './create-p4m-customer';
|
||||
// import { CreateP4MCustomerModule } from './create-p4m-customer';
|
||||
import { CreateStoreCustomerModule } from './create-store-customer/create-store-customer.module';
|
||||
import { CreateWebshopCustomerModule } from './create-webshop-customer/create-webshop-customer.module';
|
||||
import { UpdateP4MWebshopCustomerModule } from './update-p4m-webshop-customer';
|
||||
// import { UpdateP4MWebshopCustomerModule } from './update-p4m-webshop-customer';
|
||||
import { CreateCustomerComponent } from './create-customer.component';
|
||||
|
||||
@NgModule({
|
||||
@@ -13,8 +13,8 @@ import { CreateCustomerComponent } from './create-customer.component';
|
||||
CreateGuestCustomerModule,
|
||||
CreateStoreCustomerModule,
|
||||
CreateWebshopCustomerModule,
|
||||
CreateP4MCustomerModule,
|
||||
UpdateP4MWebshopCustomerModule,
|
||||
// CreateP4MCustomerModule,
|
||||
// UpdateP4MWebshopCustomerModule,
|
||||
CreateCustomerComponent,
|
||||
],
|
||||
exports: [
|
||||
@@ -22,8 +22,8 @@ import { CreateCustomerComponent } from './create-customer.component';
|
||||
CreateGuestCustomerModule,
|
||||
CreateStoreCustomerModule,
|
||||
CreateWebshopCustomerModule,
|
||||
CreateP4MCustomerModule,
|
||||
UpdateP4MWebshopCustomerModule,
|
||||
// CreateP4MCustomerModule,
|
||||
// UpdateP4MWebshopCustomerModule,
|
||||
CreateCustomerComponent,
|
||||
],
|
||||
})
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
@if (formData$ | async; as data) {
|
||||
<!-- @if (formData$ | async; as data) {
|
||||
<form (keydown.enter)="$event.preventDefault()">
|
||||
<h1 class="title flex flex-row items-center justify-center">
|
||||
Kundendaten erfassen
|
||||
<!-- <span
|
||||
class="rounded-full ml-4 h-8 w-8 text-xl text-center border-2 border-solid border-brand text-brand">i</span> -->
|
||||
<span
|
||||
class="rounded-full ml-4 h-8 w-8 text-xl text-center border-2 border-solid border-brand text-brand">i</span>
|
||||
</h1>
|
||||
<p class="description">
|
||||
Um Sie als Kunde beim nächsten
|
||||
@@ -135,4 +135,4 @@
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
} -->
|
||||
|
||||
@@ -1,292 +1,292 @@
|
||||
import { Component, ChangeDetectionStrategy, ViewChild, OnInit } from '@angular/core';
|
||||
import { AsyncValidatorFn, ValidatorFn, Validators } from '@angular/forms';
|
||||
import { Result } from '@domain/defs';
|
||||
import { CustomerDTO, CustomerInfoDTO, KeyValueDTOOfStringAndString } from '@generated/swagger/crm-api';
|
||||
import { UiErrorModalComponent, UiModalResult } from '@ui/modal';
|
||||
import { NEVER, Observable, of } from 'rxjs';
|
||||
import { catchError, distinctUntilChanged, first, map, switchMap, takeUntil, withLatestFrom } from 'rxjs/operators';
|
||||
import {
|
||||
AddressFormBlockComponent,
|
||||
AddressFormBlockData,
|
||||
DeviatingAddressFormBlockComponent,
|
||||
} from '../../components/form-blocks';
|
||||
import { NameFormBlockData } from '../../components/form-blocks/name/name-form-block-data';
|
||||
import { WebshopCustomnerAlreadyExistsModalComponent, WebshopCustomnerAlreadyExistsModalData } from '../../modals';
|
||||
import { validateEmail } from '../../validators/email-validator';
|
||||
import { AbstractCreateCustomer } from '../abstract-create-customer';
|
||||
import { encodeFormData, mapCustomerDtoToCustomerCreateFormData } from '../customer-create-form-data';
|
||||
import { zipCodeValidator } from '../../validators/zip-code-validator';
|
||||
// import { Component, ChangeDetectionStrategy, ViewChild, OnInit } from '@angular/core';
|
||||
// import { AsyncValidatorFn, ValidatorFn, Validators } from '@angular/forms';
|
||||
// import { Result } from '@domain/defs';
|
||||
// import { CustomerDTO, CustomerInfoDTO, KeyValueDTOOfStringAndString } from '@generated/swagger/crm-api';
|
||||
// import { UiErrorModalComponent, UiModalResult } from '@ui/modal';
|
||||
// import { NEVER, Observable, of } from 'rxjs';
|
||||
// import { catchError, distinctUntilChanged, first, map, switchMap, takeUntil, withLatestFrom } from 'rxjs/operators';
|
||||
// import {
|
||||
// AddressFormBlockComponent,
|
||||
// AddressFormBlockData,
|
||||
// DeviatingAddressFormBlockComponent,
|
||||
// } from '../../components/form-blocks';
|
||||
// import { NameFormBlockData } from '../../components/form-blocks/name/name-form-block-data';
|
||||
// import { WebshopCustomnerAlreadyExistsModalComponent, WebshopCustomnerAlreadyExistsModalData } from '../../modals';
|
||||
// import { validateEmail } from '../../validators/email-validator';
|
||||
// import { AbstractCreateCustomer } from '../abstract-create-customer';
|
||||
// import { encodeFormData, mapCustomerDtoToCustomerCreateFormData } from '../customer-create-form-data';
|
||||
// import { zipCodeValidator } from '../../validators/zip-code-validator';
|
||||
|
||||
@Component({
|
||||
selector: 'app-create-p4m-customer',
|
||||
templateUrl: 'create-p4m-customer.component.html',
|
||||
styleUrls: ['../create-customer.scss', 'create-p4m-customer.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: false,
|
||||
})
|
||||
export class CreateP4MCustomerComponent extends AbstractCreateCustomer implements OnInit {
|
||||
validateAddress = true;
|
||||
// @Component({
|
||||
// selector: 'app-create-p4m-customer',
|
||||
// templateUrl: 'create-p4m-customer.component.html',
|
||||
// styleUrls: ['../create-customer.scss', 'create-p4m-customer.component.scss'],
|
||||
// changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
// standalone: false,
|
||||
// })
|
||||
// export class CreateP4MCustomerComponent extends AbstractCreateCustomer implements OnInit {
|
||||
// validateAddress = true;
|
||||
|
||||
validateShippingAddress = true;
|
||||
// validateShippingAddress = true;
|
||||
|
||||
get _customerType() {
|
||||
return this.activatedRoute.snapshot.data.customerType;
|
||||
}
|
||||
// get _customerType() {
|
||||
// return this.activatedRoute.snapshot.data.customerType;
|
||||
// }
|
||||
|
||||
get customerType() {
|
||||
return `${this._customerType}-p4m`;
|
||||
}
|
||||
// get customerType() {
|
||||
// return `${this._customerType}-p4m`;
|
||||
// }
|
||||
|
||||
nameRequiredMarks: (keyof NameFormBlockData)[] = ['gender', 'firstName', 'lastName'];
|
||||
// nameRequiredMarks: (keyof NameFormBlockData)[] = ['gender', 'firstName', 'lastName'];
|
||||
|
||||
nameValidationFns: Record<keyof NameFormBlockData, ValidatorFn[]> = {
|
||||
firstName: [Validators.required],
|
||||
lastName: [Validators.required],
|
||||
gender: [Validators.required],
|
||||
title: [],
|
||||
};
|
||||
// nameValidationFns: Record<keyof NameFormBlockData, ValidatorFn[]> = {
|
||||
// firstName: [Validators.required],
|
||||
// lastName: [Validators.required],
|
||||
// gender: [Validators.required],
|
||||
// title: [],
|
||||
// };
|
||||
|
||||
emailRequiredMark: boolean;
|
||||
// emailRequiredMark: boolean;
|
||||
|
||||
emailValidatorFn: ValidatorFn[];
|
||||
// emailValidatorFn: ValidatorFn[];
|
||||
|
||||
asyncEmailVlaidtorFn: AsyncValidatorFn[];
|
||||
// asyncEmailVlaidtorFn: AsyncValidatorFn[];
|
||||
|
||||
asyncLoyaltyCardValidatorFn: AsyncValidatorFn[];
|
||||
// asyncLoyaltyCardValidatorFn: AsyncValidatorFn[];
|
||||
|
||||
shippingAddressRequiredMarks: (keyof AddressFormBlockData)[] = [
|
||||
'street',
|
||||
'streetNumber',
|
||||
'zipCode',
|
||||
'city',
|
||||
'country',
|
||||
];
|
||||
// shippingAddressRequiredMarks: (keyof AddressFormBlockData)[] = [
|
||||
// 'street',
|
||||
// 'streetNumber',
|
||||
// 'zipCode',
|
||||
// 'city',
|
||||
// 'country',
|
||||
// ];
|
||||
|
||||
shippingAddressValidators: Record<string, ValidatorFn[]> = {
|
||||
street: [Validators.required],
|
||||
streetNumber: [Validators.required],
|
||||
zipCode: [Validators.required, zipCodeValidator()],
|
||||
city: [Validators.required],
|
||||
country: [Validators.required],
|
||||
};
|
||||
// shippingAddressValidators: Record<string, ValidatorFn[]> = {
|
||||
// street: [Validators.required],
|
||||
// streetNumber: [Validators.required],
|
||||
// zipCode: [Validators.required, zipCodeValidator()],
|
||||
// city: [Validators.required],
|
||||
// country: [Validators.required],
|
||||
// };
|
||||
|
||||
addressRequiredMarks: (keyof AddressFormBlockData)[];
|
||||
// addressRequiredMarks: (keyof AddressFormBlockData)[];
|
||||
|
||||
addressValidatorFns: Record<string, ValidatorFn[]>;
|
||||
// addressValidatorFns: Record<string, ValidatorFn[]>;
|
||||
|
||||
@ViewChild(AddressFormBlockComponent, { static: false })
|
||||
addressFormBlock: AddressFormBlockComponent;
|
||||
// @ViewChild(AddressFormBlockComponent, { static: false })
|
||||
// addressFormBlock: AddressFormBlockComponent;
|
||||
|
||||
@ViewChild(DeviatingAddressFormBlockComponent, { static: false })
|
||||
deviatingDeliveryAddressFormBlock: DeviatingAddressFormBlockComponent;
|
||||
// @ViewChild(DeviatingAddressFormBlockComponent, { static: false })
|
||||
// deviatingDeliveryAddressFormBlock: DeviatingAddressFormBlockComponent;
|
||||
|
||||
agbValidatorFns = [Validators.requiredTrue];
|
||||
// agbValidatorFns = [Validators.requiredTrue];
|
||||
|
||||
birthDateValidatorFns = [];
|
||||
// birthDateValidatorFns = [];
|
||||
|
||||
existingCustomer$: Observable<CustomerInfoDTO | CustomerDTO | null>;
|
||||
// existingCustomer$: Observable<CustomerInfoDTO | CustomerDTO | null>;
|
||||
|
||||
ngOnInit(): void {
|
||||
super.ngOnInit();
|
||||
this.initMarksAndValidators();
|
||||
this.existingCustomer$ = this.customerExists$.pipe(
|
||||
distinctUntilChanged(),
|
||||
switchMap((exists) => {
|
||||
if (exists) {
|
||||
return this.fetchCustomerInfo();
|
||||
}
|
||||
return of(null);
|
||||
}),
|
||||
);
|
||||
// ngOnInit(): void {
|
||||
// super.ngOnInit();
|
||||
// this.initMarksAndValidators();
|
||||
// this.existingCustomer$ = this.customerExists$.pipe(
|
||||
// distinctUntilChanged(),
|
||||
// switchMap((exists) => {
|
||||
// if (exists) {
|
||||
// return this.fetchCustomerInfo();
|
||||
// }
|
||||
// return of(null);
|
||||
// }),
|
||||
// );
|
||||
|
||||
this.existingCustomer$
|
||||
.pipe(
|
||||
takeUntil(this.onDestroy$),
|
||||
switchMap((info) => {
|
||||
if (info) {
|
||||
return this.customerService.getCustomer(info.id, 2).pipe(
|
||||
map((res) => res.result),
|
||||
catchError((err) => NEVER),
|
||||
);
|
||||
}
|
||||
return NEVER;
|
||||
}),
|
||||
withLatestFrom(this.processId$),
|
||||
)
|
||||
.subscribe(([customer, processId]) => {
|
||||
if (customer) {
|
||||
this.modal
|
||||
.open({
|
||||
content: WebshopCustomnerAlreadyExistsModalComponent,
|
||||
data: {
|
||||
customer,
|
||||
processId,
|
||||
} as WebshopCustomnerAlreadyExistsModalData,
|
||||
title: 'Es existiert bereits ein Onlinekonto mit dieser E-Mail-Adresse',
|
||||
})
|
||||
.afterClosed$.subscribe(async (result: UiModalResult<boolean>) => {
|
||||
if (result.data) {
|
||||
this.navigateToUpdatePage(customer);
|
||||
} else {
|
||||
this.formData.email = '';
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
// this.existingCustomer$
|
||||
// .pipe(
|
||||
// takeUntil(this.onDestroy$),
|
||||
// switchMap((info) => {
|
||||
// if (info) {
|
||||
// return this.customerService.getCustomer(info.id, 2).pipe(
|
||||
// map((res) => res.result),
|
||||
// catchError((err) => NEVER),
|
||||
// );
|
||||
// }
|
||||
// return NEVER;
|
||||
// }),
|
||||
// withLatestFrom(this.processId$),
|
||||
// )
|
||||
// .subscribe(([customer, processId]) => {
|
||||
// if (customer) {
|
||||
// this.modal
|
||||
// .open({
|
||||
// content: WebshopCustomnerAlreadyExistsModalComponent,
|
||||
// data: {
|
||||
// customer,
|
||||
// processId,
|
||||
// } as WebshopCustomnerAlreadyExistsModalData,
|
||||
// title: 'Es existiert bereits ein Onlinekonto mit dieser E-Mail-Adresse',
|
||||
// })
|
||||
// .afterClosed$.subscribe(async (result: UiModalResult<boolean>) => {
|
||||
// if (result.data) {
|
||||
// this.navigateToUpdatePage(customer);
|
||||
// } else {
|
||||
// this.formData.email = '';
|
||||
// this.cdr.markForCheck();
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
async navigateToUpdatePage(customer: CustomerDTO) {
|
||||
const processId = await this.processId$.pipe(first()).toPromise();
|
||||
this.router.navigate(['/kunde', processId, 'customer', 'create', 'webshop-p4m', 'update'], {
|
||||
queryParams: {
|
||||
formData: encodeFormData({
|
||||
...mapCustomerDtoToCustomerCreateFormData(customer),
|
||||
p4m: this.formData.p4m,
|
||||
}),
|
||||
},
|
||||
});
|
||||
}
|
||||
// async navigateToUpdatePage(customer: CustomerDTO) {
|
||||
// const processId = await this.processId$.pipe(first()).toPromise();
|
||||
// this.router.navigate(['/kunde', processId, 'customer', 'create', 'webshop-p4m', 'update'], {
|
||||
// queryParams: {
|
||||
// formData: encodeFormData({
|
||||
// ...mapCustomerDtoToCustomerCreateFormData(customer),
|
||||
// p4m: this.formData.p4m,
|
||||
// }),
|
||||
// },
|
||||
// });
|
||||
// }
|
||||
|
||||
initMarksAndValidators() {
|
||||
this.asyncLoyaltyCardValidatorFn = [this.checkLoyalityCardValidator];
|
||||
this.birthDateValidatorFns = [Validators.required, this.minBirthDateValidator()];
|
||||
if (this._customerType === 'webshop') {
|
||||
this.emailRequiredMark = true;
|
||||
this.emailValidatorFn = [Validators.required, Validators.email, validateEmail];
|
||||
this.asyncEmailVlaidtorFn = [this.emailExistsValidator];
|
||||
this.addressRequiredMarks = this.shippingAddressRequiredMarks;
|
||||
this.addressValidatorFns = this.shippingAddressValidators;
|
||||
} else {
|
||||
this.emailRequiredMark = false;
|
||||
this.emailValidatorFn = [Validators.email, validateEmail];
|
||||
}
|
||||
}
|
||||
// initMarksAndValidators() {
|
||||
// this.asyncLoyaltyCardValidatorFn = [this.checkLoyalityCardValidator];
|
||||
// this.birthDateValidatorFns = [Validators.required, this.minBirthDateValidator()];
|
||||
// if (this._customerType === 'webshop') {
|
||||
// this.emailRequiredMark = true;
|
||||
// this.emailValidatorFn = [Validators.required, Validators.email, validateEmail];
|
||||
// this.asyncEmailVlaidtorFn = [this.emailExistsValidator];
|
||||
// this.addressRequiredMarks = this.shippingAddressRequiredMarks;
|
||||
// this.addressValidatorFns = this.shippingAddressValidators;
|
||||
// } else {
|
||||
// this.emailRequiredMark = false;
|
||||
// this.emailValidatorFn = [Validators.email, validateEmail];
|
||||
// }
|
||||
// }
|
||||
|
||||
fetchCustomerInfo(): Observable<CustomerDTO | null> {
|
||||
const email = this.formData.email;
|
||||
return this.customerService.getOnlineCustomerByEmail(email).pipe(
|
||||
map((result) => {
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}),
|
||||
catchError((err) => {
|
||||
this.modal.open({
|
||||
content: UiErrorModalComponent,
|
||||
data: err,
|
||||
});
|
||||
return [null];
|
||||
}),
|
||||
);
|
||||
}
|
||||
// fetchCustomerInfo(): Observable<CustomerDTO | null> {
|
||||
// const email = this.formData.email;
|
||||
// return this.customerService.getOnlineCustomerByEmail(email).pipe(
|
||||
// map((result) => {
|
||||
// if (result) {
|
||||
// return result;
|
||||
// }
|
||||
// return null;
|
||||
// }),
|
||||
// catchError((err) => {
|
||||
// this.modal.open({
|
||||
// content: UiErrorModalComponent,
|
||||
// data: err,
|
||||
// });
|
||||
// return [null];
|
||||
// }),
|
||||
// );
|
||||
// }
|
||||
|
||||
getInterests(): KeyValueDTOOfStringAndString[] {
|
||||
const interests: KeyValueDTOOfStringAndString[] = [];
|
||||
// getInterests(): KeyValueDTOOfStringAndString[] {
|
||||
// const interests: KeyValueDTOOfStringAndString[] = [];
|
||||
|
||||
for (const key in this.formData.interests) {
|
||||
if (this.formData.interests[key]) {
|
||||
interests.push({ key, group: 'KUBI_INTERESSEN' });
|
||||
}
|
||||
}
|
||||
// for (const key in this.formData.interests) {
|
||||
// if (this.formData.interests[key]) {
|
||||
// interests.push({ key, group: 'KUBI_INTERESSEN' });
|
||||
// }
|
||||
// }
|
||||
|
||||
return interests;
|
||||
}
|
||||
// return interests;
|
||||
// }
|
||||
|
||||
getNewsletter(): KeyValueDTOOfStringAndString | undefined {
|
||||
if (this.formData.newsletter) {
|
||||
return { key: 'kubi_newsletter', group: 'KUBI_NEWSLETTER' };
|
||||
}
|
||||
}
|
||||
// getNewsletter(): KeyValueDTOOfStringAndString | undefined {
|
||||
// if (this.formData.newsletter) {
|
||||
// return { key: 'kubi_newsletter', group: 'KUBI_NEWSLETTER' };
|
||||
// }
|
||||
// }
|
||||
|
||||
static MapCustomerInfoDtoToCustomerDto(customerInfoDto: CustomerInfoDTO): CustomerDTO {
|
||||
return {
|
||||
address: customerInfoDto.address,
|
||||
agentComment: customerInfoDto.agentComment,
|
||||
bonusCard: customerInfoDto.bonusCard,
|
||||
campaignCode: customerInfoDto.campaignCode,
|
||||
communicationDetails: customerInfoDto.communicationDetails,
|
||||
createdInBranch: customerInfoDto.createdInBranch,
|
||||
customerGroup: customerInfoDto.customerGroup,
|
||||
customerNumber: customerInfoDto.customerNumber,
|
||||
customerStatus: customerInfoDto.customerStatus,
|
||||
customerType: customerInfoDto.customerType,
|
||||
dateOfBirth: customerInfoDto.dateOfBirth,
|
||||
features: customerInfoDto.features,
|
||||
firstName: customerInfoDto.firstName,
|
||||
lastName: customerInfoDto.lastName,
|
||||
gender: customerInfoDto.gender,
|
||||
hasOnlineAccount: customerInfoDto.hasOnlineAccount,
|
||||
isGuestAccount: customerInfoDto.isGuestAccount,
|
||||
label: customerInfoDto.label,
|
||||
notificationChannels: customerInfoDto.notificationChannels,
|
||||
organisation: customerInfoDto.organisation,
|
||||
title: customerInfoDto.title,
|
||||
id: customerInfoDto.id,
|
||||
pId: customerInfoDto.pId,
|
||||
};
|
||||
}
|
||||
// static MapCustomerInfoDtoToCustomerDto(customerInfoDto: CustomerInfoDTO): CustomerDTO {
|
||||
// return {
|
||||
// address: customerInfoDto.address,
|
||||
// agentComment: customerInfoDto.agentComment,
|
||||
// bonusCard: customerInfoDto.bonusCard,
|
||||
// campaignCode: customerInfoDto.campaignCode,
|
||||
// communicationDetails: customerInfoDto.communicationDetails,
|
||||
// createdInBranch: customerInfoDto.createdInBranch,
|
||||
// customerGroup: customerInfoDto.customerGroup,
|
||||
// customerNumber: customerInfoDto.customerNumber,
|
||||
// customerStatus: customerInfoDto.customerStatus,
|
||||
// customerType: customerInfoDto.customerType,
|
||||
// dateOfBirth: customerInfoDto.dateOfBirth,
|
||||
// features: customerInfoDto.features,
|
||||
// firstName: customerInfoDto.firstName,
|
||||
// lastName: customerInfoDto.lastName,
|
||||
// gender: customerInfoDto.gender,
|
||||
// hasOnlineAccount: customerInfoDto.hasOnlineAccount,
|
||||
// isGuestAccount: customerInfoDto.isGuestAccount,
|
||||
// label: customerInfoDto.label,
|
||||
// notificationChannels: customerInfoDto.notificationChannels,
|
||||
// organisation: customerInfoDto.organisation,
|
||||
// title: customerInfoDto.title,
|
||||
// id: customerInfoDto.id,
|
||||
// pId: customerInfoDto.pId,
|
||||
// };
|
||||
// }
|
||||
|
||||
async saveCustomer(customer: CustomerDTO): Promise<CustomerDTO> {
|
||||
const isWebshop = this._customerType === 'webshop';
|
||||
let res: Result<CustomerDTO>;
|
||||
// async saveCustomer(customer: CustomerDTO): Promise<CustomerDTO> {
|
||||
// const isWebshop = this._customerType === 'webshop';
|
||||
// let res: Result<CustomerDTO>;
|
||||
|
||||
const { customerDto, customerInfoDto } = this.formData?._meta ?? {};
|
||||
// const { customerDto, customerInfoDto } = this.formData?._meta ?? {};
|
||||
|
||||
if (customerDto) {
|
||||
customer = { ...customerDto, ...customer };
|
||||
} else if (customerInfoDto) {
|
||||
customer = { ...CreateP4MCustomerComponent.MapCustomerInfoDtoToCustomerDto(customerInfoDto), ...customer };
|
||||
}
|
||||
// if (customerDto) {
|
||||
// customer = { ...customerDto, ...customer };
|
||||
// } else if (customerInfoDto) {
|
||||
// customer = { ...CreateP4MCustomerComponent.MapCustomerInfoDtoToCustomerDto(customerInfoDto), ...customer };
|
||||
// }
|
||||
|
||||
const p4mFeature = customer.features?.find((attr) => attr.key === 'p4mUser');
|
||||
if (p4mFeature) {
|
||||
p4mFeature.value = this.formData.p4m;
|
||||
} else {
|
||||
customer.features.push({
|
||||
key: 'p4mUser',
|
||||
value: this.formData.p4m,
|
||||
});
|
||||
}
|
||||
// const p4mFeature = customer.features?.find((attr) => attr.key === 'p4mUser');
|
||||
// if (p4mFeature) {
|
||||
// p4mFeature.value = this.formData.p4m;
|
||||
// } else {
|
||||
// customer.features.push({
|
||||
// key: 'p4mUser',
|
||||
// value: this.formData.p4m,
|
||||
// });
|
||||
// }
|
||||
|
||||
const interests = this.getInterests();
|
||||
// const interests = this.getInterests();
|
||||
|
||||
if (interests.length > 0) {
|
||||
customer.features?.push(...interests);
|
||||
// TODO: Klärung wie Interessen zukünftig gespeichert werden
|
||||
// await this._loyaltyCardService
|
||||
// .LoyaltyCardSaveInteressen({
|
||||
// customerId: res.result.id,
|
||||
// interessen: this.getInterests(),
|
||||
// })
|
||||
// .toPromise();
|
||||
}
|
||||
// if (interests.length > 0) {
|
||||
// customer.features?.push(...interests);
|
||||
// // TODO: Klärung wie Interessen zukünftig gespeichert werden
|
||||
// // await this._loyaltyCardService
|
||||
// // .LoyaltyCardSaveInteressen({
|
||||
// // customerId: res.result.id,
|
||||
// // interessen: this.getInterests(),
|
||||
// // })
|
||||
// // .toPromise();
|
||||
// }
|
||||
|
||||
const newsletter = this.getNewsletter();
|
||||
// const newsletter = this.getNewsletter();
|
||||
|
||||
if (newsletter) {
|
||||
customer.features.push(newsletter);
|
||||
} else {
|
||||
customer.features = customer.features.filter(
|
||||
(feature) => feature.key !== 'kubi_newsletter' && feature.group !== 'KUBI_NEWSLETTER',
|
||||
);
|
||||
}
|
||||
// if (newsletter) {
|
||||
// customer.features.push(newsletter);
|
||||
// } else {
|
||||
// customer.features = customer.features.filter(
|
||||
// (feature) => feature.key !== 'kubi_newsletter' && feature.group !== 'KUBI_NEWSLETTER',
|
||||
// );
|
||||
// }
|
||||
|
||||
if (isWebshop) {
|
||||
if (customer.id > 0) {
|
||||
if (this.formData?._meta?.hasLocalityCard) {
|
||||
res = await this.customerService.updateStoreP4MToWebshopP4M(customer);
|
||||
} else {
|
||||
res = await this.customerService.updateToP4MOnlineCustomer(customer);
|
||||
}
|
||||
} else {
|
||||
res = await this.customerService.createOnlineCustomer(customer).toPromise();
|
||||
}
|
||||
} else {
|
||||
res = await this.customerService.createStoreCustomer(customer).toPromise();
|
||||
}
|
||||
// if (isWebshop) {
|
||||
// if (customer.id > 0) {
|
||||
// if (this.formData?._meta?.hasLocalityCard) {
|
||||
// res = await this.customerService.updateStoreP4MToWebshopP4M(customer);
|
||||
// } else {
|
||||
// res = await this.customerService.updateToP4MOnlineCustomer(customer);
|
||||
// }
|
||||
// } else {
|
||||
// res = await this.customerService.createOnlineCustomer(customer).toPromise();
|
||||
// }
|
||||
// } else {
|
||||
// res = await this.customerService.createStoreCustomer(customer).toPromise();
|
||||
// }
|
||||
|
||||
return res.result;
|
||||
}
|
||||
}
|
||||
// return res.result;
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -1,45 +1,45 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
// import { NgModule } from '@angular/core';
|
||||
// import { CommonModule } from '@angular/common';
|
||||
|
||||
import { CreateP4MCustomerComponent } from './create-p4m-customer.component';
|
||||
import {
|
||||
AddressFormBlockModule,
|
||||
BirthDateFormBlockModule,
|
||||
InterestsFormBlockModule,
|
||||
NameFormBlockModule,
|
||||
OrganisationFormBlockModule,
|
||||
P4mNumberFormBlockModule,
|
||||
NewsletterFormBlockModule,
|
||||
DeviatingAddressFormBlockComponentModule,
|
||||
AcceptAGBFormBlockModule,
|
||||
EmailFormBlockModule,
|
||||
PhoneNumbersFormBlockModule,
|
||||
} from '../../components/form-blocks';
|
||||
import { CustomerTypeSelectorModule } from '../../components/customer-type-selector';
|
||||
import { UiSpinnerModule } from '@ui/spinner';
|
||||
import { UiIconModule } from '@ui/icon';
|
||||
import { RouterModule } from '@angular/router';
|
||||
// import { CreateP4MCustomerComponent } from './create-p4m-customer.component';
|
||||
// import {
|
||||
// AddressFormBlockModule,
|
||||
// BirthDateFormBlockModule,
|
||||
// InterestsFormBlockModule,
|
||||
// NameFormBlockModule,
|
||||
// OrganisationFormBlockModule,
|
||||
// P4mNumberFormBlockModule,
|
||||
// NewsletterFormBlockModule,
|
||||
// DeviatingAddressFormBlockComponentModule,
|
||||
// AcceptAGBFormBlockModule,
|
||||
// EmailFormBlockModule,
|
||||
// PhoneNumbersFormBlockModule,
|
||||
// } from '../../components/form-blocks';
|
||||
// import { CustomerTypeSelectorModule } from '../../components/customer-type-selector';
|
||||
// import { UiSpinnerModule } from '@ui/spinner';
|
||||
// import { UiIconModule } from '@ui/icon';
|
||||
// import { RouterModule } from '@angular/router';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
CustomerTypeSelectorModule,
|
||||
AddressFormBlockModule,
|
||||
BirthDateFormBlockModule,
|
||||
InterestsFormBlockModule,
|
||||
NameFormBlockModule,
|
||||
OrganisationFormBlockModule,
|
||||
P4mNumberFormBlockModule,
|
||||
NewsletterFormBlockModule,
|
||||
DeviatingAddressFormBlockComponentModule,
|
||||
AcceptAGBFormBlockModule,
|
||||
EmailFormBlockModule,
|
||||
PhoneNumbersFormBlockModule,
|
||||
UiSpinnerModule,
|
||||
UiIconModule,
|
||||
RouterModule,
|
||||
],
|
||||
exports: [CreateP4MCustomerComponent],
|
||||
declarations: [CreateP4MCustomerComponent],
|
||||
})
|
||||
export class CreateP4MCustomerModule {}
|
||||
// @NgModule({
|
||||
// imports: [
|
||||
// CommonModule,
|
||||
// CustomerTypeSelectorModule,
|
||||
// AddressFormBlockModule,
|
||||
// BirthDateFormBlockModule,
|
||||
// InterestsFormBlockModule,
|
||||
// NameFormBlockModule,
|
||||
// OrganisationFormBlockModule,
|
||||
// P4mNumberFormBlockModule,
|
||||
// NewsletterFormBlockModule,
|
||||
// DeviatingAddressFormBlockComponentModule,
|
||||
// AcceptAGBFormBlockModule,
|
||||
// EmailFormBlockModule,
|
||||
// PhoneNumbersFormBlockModule,
|
||||
// UiSpinnerModule,
|
||||
// UiIconModule,
|
||||
// RouterModule,
|
||||
// ],
|
||||
// exports: [CreateP4MCustomerComponent],
|
||||
// declarations: [CreateP4MCustomerComponent],
|
||||
// })
|
||||
// export class CreateP4MCustomerModule {}
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export * from './create-p4m-customer.component';
|
||||
export * from './create-p4m-customer.module';
|
||||
// export * from './create-p4m-customer.component';
|
||||
// export * from './create-p4m-customer.module';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Component, ChangeDetectionStrategy, ViewChild } from '@angular/core';
|
||||
import { ValidatorFn, Validators } from '@angular/forms';
|
||||
import { CustomerDTO } from '@generated/swagger/crm-api';
|
||||
import { CustomerDTO, CustomerInfoDTO } from '@generated/swagger/crm-api';
|
||||
import { map } from 'rxjs/operators';
|
||||
import {
|
||||
AddressFormBlockComponent,
|
||||
@@ -10,13 +10,16 @@ import {
|
||||
import { NameFormBlockData } from '../../components/form-blocks/name/name-form-block-data';
|
||||
import { validateEmail } from '../../validators/email-validator';
|
||||
import { AbstractCreateCustomer } from '../abstract-create-customer';
|
||||
import { CreateP4MCustomerComponent } from '../create-p4m-customer';
|
||||
// import { CreateP4MCustomerComponent } from '../create-p4m-customer';
|
||||
import { zipCodeValidator } from '../../validators/zip-code-validator';
|
||||
|
||||
@Component({
|
||||
selector: 'app-create-webshop-customer',
|
||||
templateUrl: 'create-webshop-customer.component.html',
|
||||
styleUrls: ['../create-customer.scss', 'create-webshop-customer.component.scss'],
|
||||
styleUrls: [
|
||||
'../create-customer.scss',
|
||||
'create-webshop-customer.component.scss',
|
||||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: false,
|
||||
})
|
||||
@@ -26,7 +29,11 @@ export class CreateWebshopCustomerComponent extends AbstractCreateCustomer {
|
||||
validateAddress = true;
|
||||
validateShippingAddress = true;
|
||||
|
||||
nameRequiredMarks: (keyof NameFormBlockData)[] = ['gender', 'firstName', 'lastName'];
|
||||
nameRequiredMarks: (keyof NameFormBlockData)[] = [
|
||||
'gender',
|
||||
'firstName',
|
||||
'lastName',
|
||||
];
|
||||
|
||||
nameValidationFns: Record<keyof NameFormBlockData, ValidatorFn[]> = {
|
||||
firstName: [Validators.required],
|
||||
@@ -35,7 +42,13 @@ export class CreateWebshopCustomerComponent extends AbstractCreateCustomer {
|
||||
title: [],
|
||||
};
|
||||
|
||||
addressRequiredMarks: (keyof AddressFormBlockData)[] = ['street', 'streetNumber', 'zipCode', 'city', 'country'];
|
||||
addressRequiredMarks: (keyof AddressFormBlockData)[] = [
|
||||
'street',
|
||||
'streetNumber',
|
||||
'zipCode',
|
||||
'city',
|
||||
'country',
|
||||
];
|
||||
|
||||
addressValidators: Record<string, ValidatorFn[]> = {
|
||||
street: [Validators.required],
|
||||
@@ -68,7 +81,11 @@ export class CreateWebshopCustomerComponent extends AbstractCreateCustomer {
|
||||
if (customerDto) {
|
||||
customer = { ...customerDto, ...customer };
|
||||
} else if (customerInfoDto) {
|
||||
customer = { ...CreateP4MCustomerComponent.MapCustomerInfoDtoToCustomerDto(customerInfoDto), ...customer };
|
||||
customer = {
|
||||
// ...CreateP4MCustomerComponent.MapCustomerInfoDtoToCustomerDto(customerInfoDto),
|
||||
...this.mapCustomerInfoDtoToCustomerDto(customerInfoDto),
|
||||
...customer,
|
||||
};
|
||||
}
|
||||
|
||||
const res = await this.customerService.updateToOnlineCustomer(customer);
|
||||
@@ -80,4 +97,34 @@ export class CreateWebshopCustomerComponent extends AbstractCreateCustomer {
|
||||
.toPromise();
|
||||
}
|
||||
}
|
||||
|
||||
mapCustomerInfoDtoToCustomerDto(
|
||||
customerInfoDto: CustomerInfoDTO,
|
||||
): CustomerDTO {
|
||||
return {
|
||||
address: customerInfoDto.address,
|
||||
agentComment: customerInfoDto.agentComment,
|
||||
bonusCard: customerInfoDto.bonusCard,
|
||||
campaignCode: customerInfoDto.campaignCode,
|
||||
communicationDetails: customerInfoDto.communicationDetails,
|
||||
createdInBranch: customerInfoDto.createdInBranch,
|
||||
customerGroup: customerInfoDto.customerGroup,
|
||||
customerNumber: customerInfoDto.customerNumber,
|
||||
customerStatus: customerInfoDto.customerStatus,
|
||||
customerType: customerInfoDto.customerType,
|
||||
dateOfBirth: customerInfoDto.dateOfBirth,
|
||||
features: customerInfoDto.features,
|
||||
firstName: customerInfoDto.firstName,
|
||||
lastName: customerInfoDto.lastName,
|
||||
gender: customerInfoDto.gender,
|
||||
hasOnlineAccount: customerInfoDto.hasOnlineAccount,
|
||||
isGuestAccount: customerInfoDto.isGuestAccount,
|
||||
label: customerInfoDto.label,
|
||||
notificationChannels: customerInfoDto.notificationChannels,
|
||||
organisation: customerInfoDto.organisation,
|
||||
title: customerInfoDto.title,
|
||||
id: customerInfoDto.id,
|
||||
pId: customerInfoDto.pId,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { CustomerDTO, Gender } from '@generated/swagger/crm-api';
|
||||
|
||||
export interface CreateCustomerQueryParams {
|
||||
p4mNumber?: string;
|
||||
// p4mNumber?: string;
|
||||
customerId?: number;
|
||||
gender?: Gender;
|
||||
title?: string;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export * from './create-b2b-customer';
|
||||
export * from './create-guest-customer';
|
||||
export * from './create-p4m-customer';
|
||||
// export * from './create-p4m-customer';
|
||||
export * from './create-store-customer';
|
||||
export * from './create-webshop-customer';
|
||||
export * from './defs';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@if (formData$ | async; as data) {
|
||||
<!-- @if (formData$ | async; as data) {
|
||||
<form (keydown.enter)="$event.preventDefault()">
|
||||
<h1 class="title flex flex-row items-center justify-center">Kundenkartendaten erfasen</h1>
|
||||
<p class="description">Bitte erfassen Sie die Kundenkarte</p>
|
||||
@@ -106,4 +106,4 @@
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
} -->
|
||||
|
||||
@@ -1,156 +1,156 @@
|
||||
import { Component, ChangeDetectionStrategy, OnInit } from '@angular/core';
|
||||
import { AsyncValidatorFn, ValidatorFn, Validators } from '@angular/forms';
|
||||
import { Result } from '@domain/defs';
|
||||
import { CustomerDTO, KeyValueDTOOfStringAndString, PayerDTO } from '@generated/swagger/crm-api';
|
||||
import { AddressFormBlockData } from '../../components/form-blocks';
|
||||
import { NameFormBlockData } from '../../components/form-blocks/name/name-form-block-data';
|
||||
import { AbstractCreateCustomer } from '../abstract-create-customer';
|
||||
import { CreateP4MCustomerComponent } from '../create-p4m-customer';
|
||||
// import { Component, ChangeDetectionStrategy, OnInit } from '@angular/core';
|
||||
// import { AsyncValidatorFn, ValidatorFn, Validators } from '@angular/forms';
|
||||
// import { Result } from '@domain/defs';
|
||||
// import { CustomerDTO, KeyValueDTOOfStringAndString, PayerDTO } from '@generated/swagger/crm-api';
|
||||
// import { AddressFormBlockData } from '../../components/form-blocks';
|
||||
// import { NameFormBlockData } from '../../components/form-blocks/name/name-form-block-data';
|
||||
// import { AbstractCreateCustomer } from '../abstract-create-customer';
|
||||
// import { CreateP4MCustomerComponent } from '../create-p4m-customer';
|
||||
|
||||
@Component({
|
||||
selector: 'page-update-p4m-webshop-customer',
|
||||
templateUrl: 'update-p4m-webshop-customer.component.html',
|
||||
styleUrls: ['../create-customer.scss', 'update-p4m-webshop-customer.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: false,
|
||||
})
|
||||
export class UpdateP4MWebshopCustomerComponent extends AbstractCreateCustomer implements OnInit {
|
||||
customerType = 'webshop-p4m/update';
|
||||
// @Component({
|
||||
// selector: 'page-update-p4m-webshop-customer',
|
||||
// templateUrl: 'update-p4m-webshop-customer.component.html',
|
||||
// styleUrls: ['../create-customer.scss', 'update-p4m-webshop-customer.component.scss'],
|
||||
// changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
// standalone: false,
|
||||
// })
|
||||
// export class UpdateP4MWebshopCustomerComponent extends AbstractCreateCustomer implements OnInit {
|
||||
// customerType = 'webshop-p4m/update';
|
||||
|
||||
validateAddress = true;
|
||||
// validateAddress = true;
|
||||
|
||||
validateShippingAddress = true;
|
||||
// validateShippingAddress = true;
|
||||
|
||||
agbValidatorFns = [Validators.requiredTrue];
|
||||
// agbValidatorFns = [Validators.requiredTrue];
|
||||
|
||||
birthDateValidatorFns = [Validators.required, this.minBirthDateValidator()];
|
||||
// birthDateValidatorFns = [Validators.required, this.minBirthDateValidator()];
|
||||
|
||||
nameRequiredMarks: (keyof NameFormBlockData)[] = ['gender', 'firstName', 'lastName'];
|
||||
// nameRequiredMarks: (keyof NameFormBlockData)[] = ['gender', 'firstName', 'lastName'];
|
||||
|
||||
nameValidationFns: Record<keyof NameFormBlockData, ValidatorFn[]> = {
|
||||
firstName: [Validators.required],
|
||||
lastName: [Validators.required],
|
||||
gender: [Validators.required],
|
||||
title: [],
|
||||
};
|
||||
// nameValidationFns: Record<keyof NameFormBlockData, ValidatorFn[]> = {
|
||||
// firstName: [Validators.required],
|
||||
// lastName: [Validators.required],
|
||||
// gender: [Validators.required],
|
||||
// title: [],
|
||||
// };
|
||||
|
||||
addressRequiredMarks: (keyof AddressFormBlockData)[] = ['street', 'streetNumber', 'zipCode', 'city', 'country'];
|
||||
// addressRequiredMarks: (keyof AddressFormBlockData)[] = ['street', 'streetNumber', 'zipCode', 'city', 'country'];
|
||||
|
||||
addressValidatorFns: Record<string, ValidatorFn[]> = {
|
||||
street: [Validators.required],
|
||||
streetNumber: [Validators.required],
|
||||
zipCode: [Validators.required],
|
||||
city: [Validators.required],
|
||||
country: [Validators.required],
|
||||
};
|
||||
// addressValidatorFns: Record<string, ValidatorFn[]> = {
|
||||
// street: [Validators.required],
|
||||
// streetNumber: [Validators.required],
|
||||
// zipCode: [Validators.required],
|
||||
// city: [Validators.required],
|
||||
// country: [Validators.required],
|
||||
// };
|
||||
|
||||
asyncLoyaltyCardValidatorFn: AsyncValidatorFn[] = [this.checkLoyalityCardValidator];
|
||||
// asyncLoyaltyCardValidatorFn: AsyncValidatorFn[] = [this.checkLoyalityCardValidator];
|
||||
|
||||
get billingAddress(): PayerDTO | undefined {
|
||||
const payers = this.formData?._meta?.customerDto?.payers;
|
||||
// get billingAddress(): PayerDTO | undefined {
|
||||
// const payers = this.formData?._meta?.customerDto?.payers;
|
||||
|
||||
if (!payers || payers.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
// if (!payers || payers.length === 0) {
|
||||
// return undefined;
|
||||
// }
|
||||
|
||||
// the default payer is the payer with the latest isDefault(Date) value
|
||||
const defaultPayer = payers.reduce((prev, curr) =>
|
||||
new Date(prev.isDefault) > new Date(curr.isDefault) ? prev : curr,
|
||||
);
|
||||
// // the default payer is the payer with the latest isDefault(Date) value
|
||||
// const defaultPayer = payers.reduce((prev, curr) =>
|
||||
// new Date(prev.isDefault) > new Date(curr.isDefault) ? prev : curr,
|
||||
// );
|
||||
|
||||
return defaultPayer.payer.data;
|
||||
}
|
||||
// return defaultPayer.payer.data;
|
||||
// }
|
||||
|
||||
get shippingAddress() {
|
||||
const shippingAddresses = this.formData?._meta?.customerDto?.shippingAddresses;
|
||||
// get shippingAddress() {
|
||||
// const shippingAddresses = this.formData?._meta?.customerDto?.shippingAddresses;
|
||||
|
||||
if (!shippingAddresses || shippingAddresses.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
// if (!shippingAddresses || shippingAddresses.length === 0) {
|
||||
// return undefined;
|
||||
// }
|
||||
|
||||
// the default shipping address is the shipping address with the latest isDefault(Date) value
|
||||
const defaultShippingAddress = shippingAddresses.reduce((prev, curr) =>
|
||||
new Date(prev.data.isDefault) > new Date(curr.data.isDefault) ? prev : curr,
|
||||
);
|
||||
// // the default shipping address is the shipping address with the latest isDefault(Date) value
|
||||
// const defaultShippingAddress = shippingAddresses.reduce((prev, curr) =>
|
||||
// new Date(prev.data.isDefault) > new Date(curr.data.isDefault) ? prev : curr,
|
||||
// );
|
||||
|
||||
return defaultShippingAddress.data;
|
||||
}
|
||||
// return defaultShippingAddress.data;
|
||||
// }
|
||||
|
||||
ngOnInit() {
|
||||
super.ngOnInit();
|
||||
}
|
||||
// ngOnInit() {
|
||||
// super.ngOnInit();
|
||||
// }
|
||||
|
||||
getInterests(): KeyValueDTOOfStringAndString[] {
|
||||
const interests: KeyValueDTOOfStringAndString[] = [];
|
||||
// getInterests(): KeyValueDTOOfStringAndString[] {
|
||||
// const interests: KeyValueDTOOfStringAndString[] = [];
|
||||
|
||||
for (const key in this.formData.interests) {
|
||||
if (this.formData.interests[key]) {
|
||||
interests.push({ key, group: 'KUBI_INTERESSEN' });
|
||||
}
|
||||
}
|
||||
// for (const key in this.formData.interests) {
|
||||
// if (this.formData.interests[key]) {
|
||||
// interests.push({ key, group: 'KUBI_INTERESSEN' });
|
||||
// }
|
||||
// }
|
||||
|
||||
return interests;
|
||||
}
|
||||
// return interests;
|
||||
// }
|
||||
|
||||
getNewsletter(): KeyValueDTOOfStringAndString | undefined {
|
||||
if (this.formData.newsletter) {
|
||||
return { key: 'kubi_newsletter', group: 'KUBI_NEWSLETTER' };
|
||||
}
|
||||
}
|
||||
// getNewsletter(): KeyValueDTOOfStringAndString | undefined {
|
||||
// if (this.formData.newsletter) {
|
||||
// return { key: 'kubi_newsletter', group: 'KUBI_NEWSLETTER' };
|
||||
// }
|
||||
// }
|
||||
|
||||
async saveCustomer(customer: CustomerDTO): Promise<CustomerDTO> {
|
||||
let res: Result<CustomerDTO>;
|
||||
// async saveCustomer(customer: CustomerDTO): Promise<CustomerDTO> {
|
||||
// let res: Result<CustomerDTO>;
|
||||
|
||||
const { customerDto, customerInfoDto } = this.formData?._meta ?? {};
|
||||
// const { customerDto, customerInfoDto } = this.formData?._meta ?? {};
|
||||
|
||||
if (customerDto) {
|
||||
customer = { ...customerDto, shippingAddresses: [], payers: [], ...customer };
|
||||
// if (customerDto) {
|
||||
// customer = { ...customerDto, shippingAddresses: [], payers: [], ...customer };
|
||||
|
||||
if (customerDto.shippingAddresses?.length) {
|
||||
customer.shippingAddresses.unshift(...customerDto.shippingAddresses);
|
||||
}
|
||||
if (customerDto.payers?.length) {
|
||||
customer.payers.unshift(...customerDto.payers);
|
||||
}
|
||||
} else if (customerInfoDto) {
|
||||
customer = { ...CreateP4MCustomerComponent.MapCustomerInfoDtoToCustomerDto(customerInfoDto), ...customer };
|
||||
}
|
||||
// if (customerDto.shippingAddresses?.length) {
|
||||
// customer.shippingAddresses.unshift(...customerDto.shippingAddresses);
|
||||
// }
|
||||
// if (customerDto.payers?.length) {
|
||||
// customer.payers.unshift(...customerDto.payers);
|
||||
// }
|
||||
// } else if (customerInfoDto) {
|
||||
// customer = { ...CreateP4MCustomerComponent.MapCustomerInfoDtoToCustomerDto(customerInfoDto), ...customer };
|
||||
// }
|
||||
|
||||
const p4mFeature = customer.features?.find((attr) => attr.key === 'p4mUser');
|
||||
if (p4mFeature) {
|
||||
p4mFeature.value = this.formData.p4m;
|
||||
} else {
|
||||
customer.features.push({
|
||||
key: 'p4mUser',
|
||||
value: this.formData.p4m,
|
||||
});
|
||||
}
|
||||
// const p4mFeature = customer.features?.find((attr) => attr.key === 'p4mUser');
|
||||
// if (p4mFeature) {
|
||||
// p4mFeature.value = this.formData.p4m;
|
||||
// } else {
|
||||
// customer.features.push({
|
||||
// key: 'p4mUser',
|
||||
// value: this.formData.p4m,
|
||||
// });
|
||||
// }
|
||||
|
||||
const interests = this.getInterests();
|
||||
// const interests = this.getInterests();
|
||||
|
||||
if (interests.length > 0) {
|
||||
customer.features?.push(...interests);
|
||||
// TODO: Klärung wie Interessen zukünftig gespeichert werden
|
||||
// await this._loyaltyCardService
|
||||
// .LoyaltyCardSaveInteressen({
|
||||
// customerId: res.result.id,
|
||||
// interessen: this.getInterests(),
|
||||
// })
|
||||
// .toPromise();
|
||||
}
|
||||
// if (interests.length > 0) {
|
||||
// customer.features?.push(...interests);
|
||||
// // TODO: Klärung wie Interessen zukünftig gespeichert werden
|
||||
// // await this._loyaltyCardService
|
||||
// // .LoyaltyCardSaveInteressen({
|
||||
// // customerId: res.result.id,
|
||||
// // interessen: this.getInterests(),
|
||||
// // })
|
||||
// // .toPromise();
|
||||
// }
|
||||
|
||||
const newsletter = this.getNewsletter();
|
||||
// const newsletter = this.getNewsletter();
|
||||
|
||||
if (newsletter) {
|
||||
customer.features.push(newsletter);
|
||||
} else {
|
||||
customer.features = customer.features.filter(
|
||||
(feature) => feature.key !== 'kubi_newsletter' && feature.group !== 'KUBI_NEWSLETTER',
|
||||
);
|
||||
}
|
||||
// if (newsletter) {
|
||||
// customer.features.push(newsletter);
|
||||
// } else {
|
||||
// customer.features = customer.features.filter(
|
||||
// (feature) => feature.key !== 'kubi_newsletter' && feature.group !== 'KUBI_NEWSLETTER',
|
||||
// );
|
||||
// }
|
||||
|
||||
res = await this.customerService.updateToP4MOnlineCustomer(customer);
|
||||
// res = await this.customerService.updateToP4MOnlineCustomer(customer);
|
||||
|
||||
return res.result;
|
||||
}
|
||||
}
|
||||
// return res.result;
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -1,48 +1,48 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
// import { NgModule } from '@angular/core';
|
||||
// import { CommonModule } from '@angular/common';
|
||||
|
||||
import { UpdateP4MWebshopCustomerComponent } from './update-p4m-webshop-customer.component';
|
||||
// import { UpdateP4MWebshopCustomerComponent } from './update-p4m-webshop-customer.component';
|
||||
|
||||
import {
|
||||
AddressFormBlockModule,
|
||||
BirthDateFormBlockModule,
|
||||
InterestsFormBlockModule,
|
||||
NameFormBlockModule,
|
||||
OrganisationFormBlockModule,
|
||||
P4mNumberFormBlockModule,
|
||||
NewsletterFormBlockModule,
|
||||
DeviatingAddressFormBlockComponentModule,
|
||||
AcceptAGBFormBlockModule,
|
||||
EmailFormBlockModule,
|
||||
PhoneNumbersFormBlockModule,
|
||||
} from '../../components/form-blocks';
|
||||
import { UiFormControlModule } from '@ui/form-control';
|
||||
import { UiInputModule } from '@ui/input';
|
||||
import { CustomerPipesModule } from '@shared/pipes/customer';
|
||||
import { CustomerTypeSelectorModule } from '../../components/customer-type-selector';
|
||||
import { UiSpinnerModule } from '@ui/spinner';
|
||||
// import {
|
||||
// AddressFormBlockModule,
|
||||
// BirthDateFormBlockModule,
|
||||
// InterestsFormBlockModule,
|
||||
// NameFormBlockModule,
|
||||
// OrganisationFormBlockModule,
|
||||
// P4mNumberFormBlockModule,
|
||||
// NewsletterFormBlockModule,
|
||||
// DeviatingAddressFormBlockComponentModule,
|
||||
// AcceptAGBFormBlockModule,
|
||||
// EmailFormBlockModule,
|
||||
// PhoneNumbersFormBlockModule,
|
||||
// } from '../../components/form-blocks';
|
||||
// import { UiFormControlModule } from '@ui/form-control';
|
||||
// import { UiInputModule } from '@ui/input';
|
||||
// import { CustomerPipesModule } from '@shared/pipes/customer';
|
||||
// import { CustomerTypeSelectorModule } from '../../components/customer-type-selector';
|
||||
// import { UiSpinnerModule } from '@ui/spinner';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
CustomerTypeSelectorModule,
|
||||
AddressFormBlockModule,
|
||||
BirthDateFormBlockModule,
|
||||
InterestsFormBlockModule,
|
||||
NameFormBlockModule,
|
||||
OrganisationFormBlockModule,
|
||||
P4mNumberFormBlockModule,
|
||||
NewsletterFormBlockModule,
|
||||
DeviatingAddressFormBlockComponentModule,
|
||||
AcceptAGBFormBlockModule,
|
||||
EmailFormBlockModule,
|
||||
PhoneNumbersFormBlockModule,
|
||||
UiFormControlModule,
|
||||
UiInputModule,
|
||||
CustomerPipesModule,
|
||||
UiSpinnerModule,
|
||||
],
|
||||
exports: [UpdateP4MWebshopCustomerComponent],
|
||||
declarations: [UpdateP4MWebshopCustomerComponent],
|
||||
})
|
||||
export class UpdateP4MWebshopCustomerModule {}
|
||||
// @NgModule({
|
||||
// imports: [
|
||||
// CommonModule,
|
||||
// CustomerTypeSelectorModule,
|
||||
// AddressFormBlockModule,
|
||||
// BirthDateFormBlockModule,
|
||||
// InterestsFormBlockModule,
|
||||
// NameFormBlockModule,
|
||||
// OrganisationFormBlockModule,
|
||||
// P4mNumberFormBlockModule,
|
||||
// NewsletterFormBlockModule,
|
||||
// DeviatingAddressFormBlockComponentModule,
|
||||
// AcceptAGBFormBlockModule,
|
||||
// EmailFormBlockModule,
|
||||
// PhoneNumbersFormBlockModule,
|
||||
// UiFormControlModule,
|
||||
// UiInputModule,
|
||||
// CustomerPipesModule,
|
||||
// UiSpinnerModule,
|
||||
// ],
|
||||
// exports: [UpdateP4MWebshopCustomerComponent],
|
||||
// declarations: [UpdateP4MWebshopCustomerComponent],
|
||||
// })
|
||||
// export class UpdateP4MWebshopCustomerModule {}
|
||||
|
||||
@@ -1,15 +1,33 @@
|
||||
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, inject, effect, untracked } from '@angular/core';
|
||||
import {
|
||||
Component,
|
||||
ChangeDetectionStrategy,
|
||||
OnInit,
|
||||
OnDestroy,
|
||||
inject,
|
||||
effect,
|
||||
untracked,
|
||||
} from '@angular/core';
|
||||
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
|
||||
import { BehaviorSubject, Subject, Subscription, fromEvent } from 'rxjs';
|
||||
import {
|
||||
BehaviorSubject,
|
||||
Subject,
|
||||
Subscription,
|
||||
firstValueFrom,
|
||||
fromEvent,
|
||||
} from 'rxjs';
|
||||
import { CustomerSearchStore } from './store/customer-search.store';
|
||||
import { provideComponentStore } from '@ngrx/component-store';
|
||||
import { Breadcrumb, BreadcrumbService } from '@core/breadcrumb';
|
||||
import { delay, filter, first, switchMap, takeUntil } from 'rxjs/operators';
|
||||
import { CustomerCreateNavigation, CustomerSearchNavigation } from '@shared/services/navigation';
|
||||
import {
|
||||
CustomerCreateNavigation,
|
||||
CustomerSearchNavigation,
|
||||
} from '@shared/services/navigation';
|
||||
import { CustomerSearchMainAutocompleteProvider } from './providers/customer-search-main-autocomplete.provider';
|
||||
import { FilterAutocompleteProvider } from '@shared/components/filter';
|
||||
import { toSignal } from '@angular/core/rxjs-interop';
|
||||
import { provideCancelSearchSubject } from '@shared/services/cancel-subject';
|
||||
import { injectFeedbackErrorDialog } from '@isa/ui/dialog';
|
||||
|
||||
@Component({
|
||||
selector: 'page-customer-search',
|
||||
@@ -28,6 +46,7 @@ import { provideCancelSearchSubject } from '@shared/services/cancel-subject';
|
||||
standalone: false,
|
||||
})
|
||||
export class CustomerSearchComponent implements OnInit, OnDestroy {
|
||||
#errorFeedbackDialog = injectFeedbackErrorDialog();
|
||||
private _store = inject(CustomerSearchStore);
|
||||
private _activatedRoute = inject(ActivatedRoute);
|
||||
private _router = inject(Router);
|
||||
@@ -37,7 +56,11 @@ export class CustomerSearchComponent implements OnInit, OnDestroy {
|
||||
|
||||
private searchStore = inject(CustomerSearchStore);
|
||||
|
||||
keyEscPressed = toSignal(fromEvent(document, 'keydown').pipe(filter((e: KeyboardEvent) => e.key === 'Escape')));
|
||||
keyEscPressed = toSignal(
|
||||
fromEvent(document, 'keydown').pipe(
|
||||
filter((e: KeyboardEvent) => e.key === 'Escape'),
|
||||
),
|
||||
);
|
||||
|
||||
get breadcrumb() {
|
||||
let breadcrumb: string;
|
||||
@@ -53,7 +76,9 @@ export class CustomerSearchComponent implements OnInit, OnDestroy {
|
||||
|
||||
private _breadcrumbs$ = this._store.processId$.pipe(
|
||||
filter((id) => !!id),
|
||||
switchMap((id) => this._breadcrumbService.getBreadcrumbsByKeyAndTag$(id, 'customer')),
|
||||
switchMap((id) =>
|
||||
this._breadcrumbService.getBreadcrumbsByKeyAndTag$(id, 'customer'),
|
||||
),
|
||||
);
|
||||
|
||||
side$ = new BehaviorSubject<string | undefined>(undefined);
|
||||
@@ -97,53 +122,77 @@ export class CustomerSearchComponent implements OnInit, OnDestroy {
|
||||
this.checkDetailsBreadcrumb();
|
||||
});
|
||||
|
||||
this._eventsSubscription = this._router.events.pipe(takeUntil(this._onDestroy$)).subscribe((event) => {
|
||||
if (event instanceof NavigationEnd) {
|
||||
this.checkAndUpdateProcessId();
|
||||
this.checkAndUpdateSide();
|
||||
this.checkAndUpdateCustomerId();
|
||||
this.checkBreadcrumbs();
|
||||
}
|
||||
});
|
||||
|
||||
this._store.customerListResponse$
|
||||
this._eventsSubscription = this._router.events
|
||||
.pipe(takeUntil(this._onDestroy$))
|
||||
.subscribe(async ([response, filter, processId, restored, skipNavigation]) => {
|
||||
if (this._store.processId === processId) {
|
||||
if (skipNavigation) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.hits === 1) {
|
||||
// Navigate to details page
|
||||
const customer = response.result[0];
|
||||
|
||||
if (customer.id < 0) {
|
||||
// navigate to create customer
|
||||
const route = this._createNavigation.upgradeCustomerRoute({ processId, customerInfo: customer });
|
||||
await this._router.navigate(route.path, { queryParams: route.queryParams });
|
||||
return;
|
||||
} else {
|
||||
const route = this._navigation.detailsRoute({ processId, customerId: customer.id });
|
||||
await this._router.navigate(route.path, { queryParams: filter.getQueryParams() });
|
||||
}
|
||||
} else if (response.hits > 1) {
|
||||
const route = this._navigation.listRoute({ processId, filter });
|
||||
|
||||
if (
|
||||
(['details'].includes(this.breadcrumb) &&
|
||||
response?.result?.some((c) => c.id === this._store.customerId)) ||
|
||||
restored
|
||||
) {
|
||||
await this._router.navigate([], { queryParams: route.queryParams });
|
||||
} else {
|
||||
await this._router.navigate(route.path, { queryParams: route.queryParams });
|
||||
}
|
||||
}
|
||||
|
||||
.subscribe((event) => {
|
||||
if (event instanceof NavigationEnd) {
|
||||
this.checkAndUpdateProcessId();
|
||||
this.checkAndUpdateSide();
|
||||
this.checkAndUpdateCustomerId();
|
||||
this.checkBreadcrumbs();
|
||||
}
|
||||
});
|
||||
|
||||
this._store.customerListResponse$
|
||||
.pipe(takeUntil(this._onDestroy$))
|
||||
.subscribe(
|
||||
async ([response, filter, processId, restored, skipNavigation]) => {
|
||||
if (this._store.processId === processId) {
|
||||
if (skipNavigation) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.hits === 1) {
|
||||
// Navigate to details page
|
||||
const customer = response.result[0];
|
||||
|
||||
if (customer.id < 0) {
|
||||
// #5375 - Zusätzlich soll bei Kunden bei denen ein Upgrade möglich ist ein Dialog angezeigt werden, dass Kundenneuanlage mit Kundenkarte nicht möglich ist
|
||||
await firstValueFrom(
|
||||
this.#errorFeedbackDialog({
|
||||
data: {
|
||||
errorMessage:
|
||||
'Kundenneuanlage mit Kundenkarte nicht möglich',
|
||||
},
|
||||
}).closed,
|
||||
);
|
||||
// navigate to create customer
|
||||
// const route = this._createNavigation.upgradeCustomerRoute({ processId, customerInfo: customer });
|
||||
// await this._router.navigate(route.path, { queryParams: route.queryParams });
|
||||
return;
|
||||
} else {
|
||||
const route = this._navigation.detailsRoute({
|
||||
processId,
|
||||
customerId: customer.id,
|
||||
});
|
||||
await this._router.navigate(route.path, {
|
||||
queryParams: filter.getQueryParams(),
|
||||
});
|
||||
}
|
||||
} else if (response.hits > 1) {
|
||||
const route = this._navigation.listRoute({ processId, filter });
|
||||
|
||||
if (
|
||||
(['details'].includes(this.breadcrumb) &&
|
||||
response?.result?.some(
|
||||
(c) => c.id === this._store.customerId,
|
||||
)) ||
|
||||
restored
|
||||
) {
|
||||
await this._router.navigate([], {
|
||||
queryParams: route.queryParams,
|
||||
});
|
||||
} else {
|
||||
await this._router.navigate(route.path, {
|
||||
queryParams: route.queryParams,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.checkBreadcrumbs();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
@@ -169,7 +218,11 @@ export class CustomerSearchComponent implements OnInit, OnDestroy {
|
||||
this._store.setProcessId(processId);
|
||||
this._store.reset(this._activatedRoute.snapshot.queryParams);
|
||||
if (!['main', 'filter'].some((s) => s === this.breadcrumb)) {
|
||||
const skipNavigation = ['orders', 'order-details', 'order-details-history'].includes(this.breadcrumb);
|
||||
const skipNavigation = [
|
||||
'orders',
|
||||
'order-details',
|
||||
'order-details-history',
|
||||
].includes(this.breadcrumb);
|
||||
this._store.search({ skipNavigation });
|
||||
}
|
||||
}
|
||||
@@ -229,7 +282,9 @@ export class CustomerSearchComponent implements OnInit, OnDestroy {
|
||||
const mainBreadcrumb = await this.getMainBreadcrumb();
|
||||
|
||||
if (!mainBreadcrumb) {
|
||||
const navigation = this._navigation.defaultRoute({ processId: this._store.processId });
|
||||
const navigation = this._navigation.defaultRoute({
|
||||
processId: this._store.processId,
|
||||
});
|
||||
const breadcrumb: Breadcrumb = {
|
||||
key: this._store.processId,
|
||||
tags: ['customer', 'search', 'main'],
|
||||
@@ -242,14 +297,19 @@ export class CustomerSearchComponent implements OnInit, OnDestroy {
|
||||
this._breadcrumbService.addBreadcrumb(breadcrumb);
|
||||
} else {
|
||||
this._breadcrumbService.patchBreadcrumb(mainBreadcrumb.id, {
|
||||
params: { ...this.snapshot.queryParams, ...(mainBreadcrumb.params ?? {}) },
|
||||
params: {
|
||||
...this.snapshot.queryParams,
|
||||
...(mainBreadcrumb.params ?? {}),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async getCreateCustomerBreadcrumb(): Promise<Breadcrumb | undefined> {
|
||||
const breadcrumbs = await this.getBreadcrumbs();
|
||||
return breadcrumbs.find((b) => b.tags.includes('create') && b.tags.includes('customer'));
|
||||
return breadcrumbs.find(
|
||||
(b) => b.tags.includes('create') && b.tags.includes('customer'),
|
||||
);
|
||||
}
|
||||
|
||||
async checkCreateCustomerBreadcrumb() {
|
||||
@@ -262,7 +322,9 @@ export class CustomerSearchComponent implements OnInit, OnDestroy {
|
||||
|
||||
async getSearchBreadcrumb(): Promise<Breadcrumb | undefined> {
|
||||
const breadcrumbs = await this.getBreadcrumbs();
|
||||
return breadcrumbs.find((b) => b.tags.includes('list') && b.tags.includes('search'));
|
||||
return breadcrumbs.find(
|
||||
(b) => b.tags.includes('list') && b.tags.includes('search'),
|
||||
);
|
||||
}
|
||||
|
||||
async checkSearchBreadcrumb() {
|
||||
@@ -288,7 +350,9 @@ export class CustomerSearchComponent implements OnInit, OnDestroy {
|
||||
const name = this._store.queryParams?.main_qs || 'Suche';
|
||||
|
||||
if (!searchBreadcrumb) {
|
||||
const navigation = this._navigation.listRoute({ processId: this._store.processId });
|
||||
const navigation = this._navigation.listRoute({
|
||||
processId: this._store.processId,
|
||||
});
|
||||
const breadcrumb: Breadcrumb = {
|
||||
key: this._store.processId,
|
||||
tags: ['customer', 'search', 'list'],
|
||||
@@ -300,7 +364,10 @@ export class CustomerSearchComponent implements OnInit, OnDestroy {
|
||||
|
||||
this._breadcrumbService.addBreadcrumb(breadcrumb);
|
||||
} else {
|
||||
this._breadcrumbService.patchBreadcrumb(searchBreadcrumb.id, { params: this.snapshot.queryParams, name });
|
||||
this._breadcrumbService.patchBreadcrumb(searchBreadcrumb.id, {
|
||||
params: this.snapshot.queryParams,
|
||||
name,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (searchBreadcrumb) {
|
||||
@@ -311,7 +378,9 @@ export class CustomerSearchComponent implements OnInit, OnDestroy {
|
||||
|
||||
async getDetailsBreadcrumb(): Promise<Breadcrumb | undefined> {
|
||||
const breadcrumbs = await this.getBreadcrumbs();
|
||||
return breadcrumbs.find((b) => b.tags.includes('details') && b.tags.includes('search'));
|
||||
return breadcrumbs.find(
|
||||
(b) => b.tags.includes('details') && b.tags.includes('search'),
|
||||
);
|
||||
}
|
||||
|
||||
async checkDetailsBreadcrumb() {
|
||||
@@ -333,7 +402,8 @@ export class CustomerSearchComponent implements OnInit, OnDestroy {
|
||||
].includes(this.breadcrumb)
|
||||
) {
|
||||
const customer = this._store.customer;
|
||||
const fullName = `${customer?.firstName ?? ''} ${customer?.lastName ?? ''}`.trim();
|
||||
const fullName =
|
||||
`${customer?.firstName ?? ''} ${customer?.lastName ?? ''}`.trim();
|
||||
|
||||
if (!detailsBreadcrumb) {
|
||||
const navigation = this._navigation.detailsRoute({
|
||||
@@ -515,7 +585,10 @@ export class CustomerSearchComponent implements OnInit, OnDestroy {
|
||||
async checkOrderDetailsBreadcrumb() {
|
||||
const orderDetailsBreadcrumb = await this.getOrderDetailsBreadcrumb();
|
||||
|
||||
if (this.breadcrumb === 'order-details' || this.breadcrumb === 'order-details-history') {
|
||||
if (
|
||||
this.breadcrumb === 'order-details' ||
|
||||
this.breadcrumb === 'order-details-history'
|
||||
) {
|
||||
if (!orderDetailsBreadcrumb) {
|
||||
const navigation = this._navigation.orderDetialsRoute({
|
||||
processId: this._store.processId,
|
||||
@@ -546,7 +619,8 @@ export class CustomerSearchComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
async checkOrderDetailsHistoryBreadcrumb() {
|
||||
const orderDetailsHistoryBreadcrumb = await this.getOrderDetailsHistoryBreadcrumb();
|
||||
const orderDetailsHistoryBreadcrumb =
|
||||
await this.getOrderDetailsHistoryBreadcrumb();
|
||||
|
||||
if (this.breadcrumb === 'order-details-history') {
|
||||
if (!orderDetailsHistoryBreadcrumb) {
|
||||
@@ -569,7 +643,9 @@ export class CustomerSearchComponent implements OnInit, OnDestroy {
|
||||
this._breadcrumbService.addBreadcrumb(breadcrumb);
|
||||
}
|
||||
} else if (orderDetailsHistoryBreadcrumb) {
|
||||
this._breadcrumbService.removeBreadcrumb(orderDetailsHistoryBreadcrumb.id);
|
||||
this._breadcrumbService.removeBreadcrumb(
|
||||
orderDetailsHistoryBreadcrumb.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import { UiSelectModule } from '@ui/select';
|
||||
import { UiIconModule } from '@ui/icon';
|
||||
import { FormGroup, ReactiveFormsModule } from '@angular/forms';
|
||||
import { RouterLink } from '@angular/router';
|
||||
import { UiDateInputDirective } from '@ui/input';
|
||||
import { validateCompanyOrPersonalInfoRequired } from '../../validators/gender-b2b-validator';
|
||||
|
||||
@Component({
|
||||
@@ -23,7 +22,6 @@ import { validateCompanyOrPersonalInfoRequired } from '../../validators/gender-b
|
||||
UiIconModule,
|
||||
ReactiveFormsModule,
|
||||
RouterLink,
|
||||
UiDateInputDirective,
|
||||
],
|
||||
})
|
||||
export class CustomerDataEditB2BComponent extends CustomerDataEditComponent {
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, inject } from '@angular/core';
|
||||
import { CustomerSearchStore } from '../store';
|
||||
import { ActivatedRoute, RouterLink } from '@angular/router';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Subject, combineLatest, of } from 'rxjs';
|
||||
import { catchError, map, share, switchMap } from 'rxjs/operators';
|
||||
import { CrmCustomerService } from '@domain/crm';
|
||||
import { KundenkarteComponent } from '../../components/kundenkarte';
|
||||
import { AsyncPipe } from '@angular/common';
|
||||
import { IconComponent } from '@shared/components/icon';
|
||||
import { CustomerSearchNavigation } from '@shared/services/navigation';
|
||||
import { BonusCardInfoDTO } from '@generated/swagger/crm-api';
|
||||
import { CustomerMenuComponent } from '../../components/customer-menu';
|
||||
@@ -17,7 +16,7 @@ import { CustomerMenuComponent } from '../../components/customer-menu';
|
||||
styleUrls: ['kundenkarte-main-view.component.css'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
host: { class: 'page-customer-kundenkarte-main-view' },
|
||||
imports: [CustomerMenuComponent, KundenkarteComponent, AsyncPipe, IconComponent, RouterLink],
|
||||
imports: [CustomerMenuComponent, KundenkarteComponent, AsyncPipe],
|
||||
})
|
||||
export class KundenkarteMainViewComponent implements OnInit, OnDestroy {
|
||||
private _store = inject(CustomerSearchStore);
|
||||
|
||||
@@ -19,7 +19,7 @@ import { AddressPipe } from '@shared/pipes/customer';
|
||||
import { IconComponent } from '@shared/components/icon';
|
||||
import { CustomerSearchNavigation } from '@shared/services/navigation';
|
||||
import { CustomerOrderItemListItemComponent } from './order-item-list-item/order-item-list-item.component';
|
||||
import { GroupByPipe, groupBy } from '@ui/common';
|
||||
import { groupBy } from '@ui/common';
|
||||
import { EnvironmentService } from '@core/environment';
|
||||
|
||||
@Component({
|
||||
@@ -40,7 +40,6 @@ import { EnvironmentService } from '@core/environment';
|
||||
RouterLink,
|
||||
CustomerOrderItemListItemComponent,
|
||||
NgFor,
|
||||
GroupByPipe,
|
||||
NgSwitch,
|
||||
NgSwitchCase,
|
||||
NgSwitchDefault,
|
||||
|
||||
@@ -2,9 +2,7 @@ import { AsyncPipe, CurrencyPipe, DatePipe } from '@angular/common';
|
||||
import { Component, ChangeDetectionStrategy, Input, OnDestroy, OnInit, inject } from '@angular/core';
|
||||
import { ActivatedRoute, RouterLink } from '@angular/router';
|
||||
import { ProductImagePipe } from '@cdn/product-image';
|
||||
import { IconComponent } from '@shared/components/icon';
|
||||
import { BranchNamePipe, ResolveBranchPipe } from '@shared/pipes/branch';
|
||||
import { OrderItemProcessingStatusPipe, OrderProcessingStatusPipe } from '@shared/pipes/order';
|
||||
import { OrderItemProcessingStatusPipe } from '@shared/pipes/order';
|
||||
import { OrderItemDTO } from '@generated/swagger/oms-api';
|
||||
import { BehaviorSubject, Subject, combineLatest } from 'rxjs';
|
||||
import { map, takeUntil } from 'rxjs/operators';
|
||||
@@ -21,16 +19,12 @@ import { PaymentTypePipe } from '@shared/pipes/customer';
|
||||
imports: [
|
||||
AsyncPipe,
|
||||
DatePipe,
|
||||
OrderProcessingStatusPipe,
|
||||
ProductImagePipe,
|
||||
ResolveBranchPipe,
|
||||
BranchNamePipe,
|
||||
CurrencyPipe,
|
||||
IconComponent,
|
||||
RouterLink,
|
||||
PaymentTypePipe,
|
||||
OrderItemProcessingStatusPipe
|
||||
],
|
||||
],
|
||||
})
|
||||
export class CustomerOrderItemListItemComponent implements OnInit, OnDestroy {
|
||||
private _activatedRoute = inject(ActivatedRoute);
|
||||
|
||||
@@ -4,8 +4,6 @@ import { map } from 'rxjs/operators';
|
||||
import { CustomerNamePipe } from '@shared/pipes/customer';
|
||||
import { AsyncPipe } from '@angular/common';
|
||||
import { ProductImagePipe } from '@cdn/product-image';
|
||||
import { CustomerSearchNavigation } from '@shared/services/navigation';
|
||||
import { RouterLink, RouterLinkActive } from '@angular/router';
|
||||
import { OrderItemProcessingStatusPipe } from '@shared/pipes/order';
|
||||
|
||||
@Component({
|
||||
@@ -18,8 +16,6 @@ import { OrderItemProcessingStatusPipe } from '@shared/pipes/order';
|
||||
CustomerNamePipe,
|
||||
AsyncPipe,
|
||||
ProductImagePipe,
|
||||
RouterLink,
|
||||
RouterLinkActive,
|
||||
OrderItemProcessingStatusPipe
|
||||
],
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { AsyncPipe, CurrencyPipe, DatePipe } from '@angular/common';
|
||||
import { Component, ChangeDetectionStrategy, Input, inject } from '@angular/core';
|
||||
import { OrderDestinationPipe, OrderProcessingStatusPipe } from '@shared/pipes/order';
|
||||
import { OrderProcessingStatusPipe } from '@shared/pipes/order';
|
||||
import { AddressPipe } from '@shared/pipes/customer';
|
||||
|
||||
import { OrderListItemDTO } from '@generated/swagger/oms-api';
|
||||
@@ -19,7 +19,6 @@ import { RouterLink } from '@angular/router';
|
||||
imports: [
|
||||
DatePipe,
|
||||
OrderProcessingStatusPipe,
|
||||
OrderDestinationPipe,
|
||||
CurrencyPipe,
|
||||
AddressPipe,
|
||||
AsyncPipe,
|
||||
|
||||
@@ -4,8 +4,6 @@ import { Subject, combineLatest } from 'rxjs';
|
||||
import { distinctUntilChanged, map, takeUntil } from 'rxjs/operators';
|
||||
import { AsyncPipe } from '@angular/common';
|
||||
import { CustomerSearchNavigation } from '@shared/services/navigation';
|
||||
import { RouterLink } from '@angular/router';
|
||||
import { IconComponent } from '@shared/components/icon';
|
||||
import { LoaderComponent } from '@shared/components/loader';
|
||||
import { CustomerOrderListItemComponent } from './order-list-item/order-list-item.component';
|
||||
import { CustomerMenuComponent } from '../../components/customer-menu';
|
||||
@@ -19,8 +17,6 @@ import { CustomerMenuComponent } from '../../components/customer-menu';
|
||||
imports: [
|
||||
CustomerMenuComponent,
|
||||
AsyncPipe,
|
||||
RouterLink,
|
||||
IconComponent,
|
||||
LoaderComponent,
|
||||
CustomerOrderListItemComponent
|
||||
],
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, Params, Router, RouterStateSnapshot } from '@angular/router';
|
||||
import {
|
||||
ActivatedRouteSnapshot,
|
||||
Params,
|
||||
Router,
|
||||
RouterStateSnapshot,
|
||||
} from '@angular/router';
|
||||
import { DomainCheckoutService } from '@domain/checkout';
|
||||
import { CustomerCreateFormData, decodeFormData } from '../create-customer';
|
||||
import { CustomerCreateNavigation } from '@shared/services/navigation';
|
||||
@@ -9,7 +14,10 @@ export class CustomerCreateGuard {
|
||||
private checkoutService = inject(DomainCheckoutService);
|
||||
private customerCreateNavigation = inject(CustomerCreateNavigation);
|
||||
|
||||
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
|
||||
async canActivate(
|
||||
route: ActivatedRouteSnapshot,
|
||||
state: RouterStateSnapshot,
|
||||
): Promise<boolean> {
|
||||
// exit with true if canActivateChild will be called
|
||||
if (route.firstChild) {
|
||||
return true;
|
||||
@@ -19,10 +27,15 @@ export class CustomerCreateGuard {
|
||||
|
||||
const processId = this.getProcessId(route);
|
||||
const formData = this.getFormData(route);
|
||||
const canActivateCustomerType = await this.setableCustomerTypes(processId, formData);
|
||||
const canActivateCustomerType = await this.setableCustomerTypes(
|
||||
processId,
|
||||
formData,
|
||||
);
|
||||
|
||||
if (canActivateCustomerType[customerType] !== true) {
|
||||
customerType = Object.keys(canActivateCustomerType).find((key) => canActivateCustomerType[key]);
|
||||
customerType = Object.keys(canActivateCustomerType).find(
|
||||
(key) => canActivateCustomerType[key],
|
||||
);
|
||||
}
|
||||
|
||||
await this.navigate(processId, customerType, route.queryParams);
|
||||
@@ -30,9 +43,14 @@ export class CustomerCreateGuard {
|
||||
return true;
|
||||
}
|
||||
|
||||
async canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
|
||||
async canActivateChild(
|
||||
route: ActivatedRouteSnapshot,
|
||||
state: RouterStateSnapshot,
|
||||
): Promise<boolean> {
|
||||
const processId = this.getProcessId(route);
|
||||
const customerType = route.routeConfig.path?.replace('create/', '')?.replace('/update', '');
|
||||
const customerType = route.routeConfig.path
|
||||
?.replace('create/', '')
|
||||
?.replace('/update', '');
|
||||
|
||||
if (customerType === 'create-customer-main') {
|
||||
return true;
|
||||
@@ -40,29 +58,39 @@ export class CustomerCreateGuard {
|
||||
|
||||
const formData = this.getFormData(route);
|
||||
|
||||
const canActivateCustomerType = await this.setableCustomerTypes(processId, formData);
|
||||
const canActivateCustomerType = await this.setableCustomerTypes(
|
||||
processId,
|
||||
formData,
|
||||
);
|
||||
|
||||
if (canActivateCustomerType[customerType]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const activatableCustomerType = Object.keys(canActivateCustomerType)?.find((key) => canActivateCustomerType[key]);
|
||||
const activatableCustomerType = Object.keys(canActivateCustomerType)?.find(
|
||||
(key) => canActivateCustomerType[key],
|
||||
);
|
||||
|
||||
await this.navigate(processId, activatableCustomerType, route.queryParams);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
async setableCustomerTypes(processId: number, formData: CustomerCreateFormData): Promise<Record<string, boolean>> {
|
||||
const res = await this.checkoutService.getSetableCustomerTypes(processId).toPromise();
|
||||
async setableCustomerTypes(
|
||||
processId: number,
|
||||
formData: CustomerCreateFormData,
|
||||
): Promise<Record<string, boolean>> {
|
||||
const res = await this.checkoutService
|
||||
.getSetableCustomerTypes(processId)
|
||||
.toPromise();
|
||||
|
||||
if (res.store) {
|
||||
res['store-p4m'] = true;
|
||||
}
|
||||
// if (res.store) {
|
||||
// res['store-p4m'] = true;
|
||||
// }
|
||||
|
||||
if (res.webshop) {
|
||||
res['webshop-p4m'] = true;
|
||||
}
|
||||
// if (res.webshop) {
|
||||
// res['webshop-p4m'] = true;
|
||||
// }
|
||||
|
||||
if (formData?._meta) {
|
||||
const customerType = formData._meta.customerType;
|
||||
@@ -107,7 +135,11 @@ export class CustomerCreateGuard {
|
||||
return {};
|
||||
}
|
||||
|
||||
navigate(processId: number, customerType: string, queryParams: Params): Promise<boolean> {
|
||||
navigate(
|
||||
processId: number,
|
||||
customerType: string,
|
||||
queryParams: Params,
|
||||
): Promise<boolean> {
|
||||
const path = this.customerCreateNavigation.createCustomerRoute({
|
||||
customerType,
|
||||
processId,
|
||||
|
||||
@@ -31,7 +31,9 @@ export class CantAddCustomerToCartModalComponent {
|
||||
get option() {
|
||||
return (
|
||||
this.ref.data.upgradeableTo?.options.values.find((upgradeOption) =>
|
||||
this.ref.data.required.options.values.some((requiredOption) => upgradeOption.key === requiredOption.key),
|
||||
this.ref.data.required.options.values.some(
|
||||
(requiredOption) => upgradeOption.key === requiredOption.key,
|
||||
),
|
||||
) || { value: this.queryParams }
|
||||
);
|
||||
}
|
||||
@@ -39,7 +41,9 @@ export class CantAddCustomerToCartModalComponent {
|
||||
get queryParams() {
|
||||
let option = this.ref.data.required?.options.values.find((f) => f.selected);
|
||||
if (!option) {
|
||||
option = this.ref.data.required?.options.values.find((f) => (isBoolean(f.enabled) ? f.enabled : true));
|
||||
option = this.ref.data.required?.options.values.find((f) =>
|
||||
isBoolean(f.enabled) ? f.enabled : true,
|
||||
);
|
||||
}
|
||||
return option ? { customertype: option.value } : {};
|
||||
}
|
||||
@@ -57,27 +61,29 @@ export class CantAddCustomerToCartModalComponent {
|
||||
const queryParams: Record<string, string> = {};
|
||||
|
||||
if (customer) {
|
||||
queryParams['formData'] = encodeFormData(mapCustomerDtoToCustomerCreateFormData(customer));
|
||||
queryParams['formData'] = encodeFormData(
|
||||
mapCustomerDtoToCustomerCreateFormData(customer),
|
||||
);
|
||||
}
|
||||
|
||||
if (option === 'webshop' && attributes.some((a) => a.key === 'p4mUser')) {
|
||||
const nav = this.customerCreateNavigation.createCustomerRoute({
|
||||
processId: this.applicationService.activatedProcessId,
|
||||
customerType: 'webshop-p4m',
|
||||
});
|
||||
this.router.navigate(nav.path, {
|
||||
queryParams: { ...nav.queryParams, ...queryParams },
|
||||
});
|
||||
} else {
|
||||
const nav = this.customerCreateNavigation.createCustomerRoute({
|
||||
processId: this.applicationService.activatedProcessId,
|
||||
customerType: option as any,
|
||||
});
|
||||
// if (option === 'webshop' && attributes.some((a) => a.key === 'p4mUser')) {
|
||||
// const nav = this.customerCreateNavigation.createCustomerRoute({
|
||||
// processId: this.applicationService.activatedProcessId,
|
||||
// customerType: 'webshop-p4m',
|
||||
// });
|
||||
// this.router.navigate(nav.path, {
|
||||
// queryParams: { ...nav.queryParams, ...queryParams },
|
||||
// });
|
||||
// } else {
|
||||
const nav = this.customerCreateNavigation.createCustomerRoute({
|
||||
processId: this.applicationService.activatedProcessId,
|
||||
customerType: option as any,
|
||||
});
|
||||
|
||||
this.router.navigate(nav.path, {
|
||||
queryParams: { ...nav.queryParams, ...queryParams },
|
||||
});
|
||||
}
|
||||
this.router.navigate(nav.path, {
|
||||
queryParams: { ...nav.queryParams, ...queryParams },
|
||||
});
|
||||
// }
|
||||
|
||||
this.ref.close();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
<div class="font-bold text-center border-t border-b border-solid border-disabled-customer -mx-4 py-4">
|
||||
<div
|
||||
class="font-bold text-center border-t border-b border-solid border-disabled-customer -mx-4 py-4"
|
||||
>
|
||||
{{ customer?.communicationDetails?.email }}
|
||||
</div>
|
||||
<div class="grid grid-flow-row gap-1 text-sm font-bold border-b border-solid border-disabled-customer -mx-4 py-4 px-14">
|
||||
<div
|
||||
class="grid grid-flow-row gap-1 text-sm font-bold border-b border-solid border-disabled-customer -mx-4 py-4 px-14"
|
||||
>
|
||||
@if (customer?.organisation?.name) {
|
||||
<span>{{ customer?.organisation?.name }}</span>
|
||||
}
|
||||
@@ -16,23 +20,26 @@
|
||||
</div>
|
||||
|
||||
<div class="grid grid-flow-col gap-4 justify-around mt-12">
|
||||
<button class="border-2 border-solid border-brand rounded-full font-bold text-brand px-6 py-3 text-lg" (click)="close(false)">
|
||||
<button
|
||||
class="border-2 border-solid border-brand rounded-full font-bold text-brand px-6 py-3 text-lg"
|
||||
(click)="close(false)"
|
||||
>
|
||||
neues Onlinekonto anlegen
|
||||
</button>
|
||||
@if (!isWebshopWithP4M) {
|
||||
<button
|
||||
class="border-2 border-solid border-brand rounded-full font-bold text-white px-6 py-3 text-lg bg-brand"
|
||||
(click)="close(true)"
|
||||
>
|
||||
>
|
||||
Daten übernehmen
|
||||
</button>
|
||||
}
|
||||
@if (isWebshopWithP4M) {
|
||||
<!-- @if (isWebshopWithP4M) {
|
||||
<button
|
||||
class="border-2 border-solid border-brand rounded-full font-bold text-white px-6 py-3 text-lg bg-brand"
|
||||
(click)="selectCustomer()"
|
||||
>
|
||||
Datensatz auswählen
|
||||
</button>
|
||||
}
|
||||
} -->
|
||||
</div>
|
||||
|
||||
@@ -9,11 +9,11 @@ import { CustomerCreateGuard } from './guards/customer-create.guard';
|
||||
import {
|
||||
CreateB2BCustomerComponent,
|
||||
CreateGuestCustomerComponent,
|
||||
CreateP4MCustomerComponent,
|
||||
// CreateP4MCustomerComponent,
|
||||
CreateStoreCustomerComponent,
|
||||
CreateWebshopCustomerComponent,
|
||||
} from './create-customer';
|
||||
import { UpdateP4MWebshopCustomerComponent } from './create-customer/update-p4m-webshop-customer';
|
||||
// import { UpdateP4MWebshopCustomerComponent } from './create-customer/update-p4m-webshop-customer';
|
||||
import { CreateCustomerComponent } from './create-customer/create-customer.component';
|
||||
import { CustomerDataEditB2BComponent } from './customer-search/edit-main-view/customer-data-edit-b2b.component';
|
||||
import { CustomerDataEditB2CComponent } from './customer-search/edit-main-view/customer-data-edit-b2c.component';
|
||||
@@ -40,8 +40,16 @@ export const routes: Routes = [
|
||||
path: '',
|
||||
component: CustomerSearchComponent,
|
||||
children: [
|
||||
{ path: 'search', component: CustomerMainViewComponent, data: { side: 'main', breadcrumb: 'main' } },
|
||||
{ path: 'search/list', component: CustomerResultsMainViewComponent, data: { breadcrumb: 'search' } },
|
||||
{
|
||||
path: 'search',
|
||||
component: CustomerMainViewComponent,
|
||||
data: { side: 'main', breadcrumb: 'main' },
|
||||
},
|
||||
{
|
||||
path: 'search/list',
|
||||
component: CustomerResultsMainViewComponent,
|
||||
data: { breadcrumb: 'search' },
|
||||
},
|
||||
{
|
||||
path: 'search/filter',
|
||||
component: CustomerFilterMainViewComponent,
|
||||
@@ -80,7 +88,10 @@ export const routes: Routes = [
|
||||
{
|
||||
path: 'search/:customerId/orders/:orderId/:orderItemId/history',
|
||||
component: CustomerOrderDetailsHistoryMainViewComponent,
|
||||
data: { side: 'order-details', breadcrumb: 'order-details-history' },
|
||||
data: {
|
||||
side: 'order-details',
|
||||
breadcrumb: 'order-details-history',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'search/:customerId/edit/b2b',
|
||||
@@ -140,13 +151,13 @@ export const routes: Routes = [
|
||||
{ path: 'create/webshop', component: CreateWebshopCustomerComponent },
|
||||
{ path: 'create/b2b', component: CreateB2BCustomerComponent },
|
||||
{ path: 'create/guest', component: CreateGuestCustomerComponent },
|
||||
{ path: 'create/webshop-p4m', component: CreateP4MCustomerComponent, data: { customerType: 'webshop' } },
|
||||
{ path: 'create/store-p4m', component: CreateP4MCustomerComponent, data: { customerType: 'store' } },
|
||||
{
|
||||
path: 'create/webshop-p4m/update',
|
||||
component: UpdateP4MWebshopCustomerComponent,
|
||||
data: { customerType: 'webshop' },
|
||||
},
|
||||
// { path: 'create/webshop-p4m', component: CreateP4MCustomerComponent, data: { customerType: 'webshop' } },
|
||||
// { path: 'create/store-p4m', component: CreateP4MCustomerComponent, data: { customerType: 'store' } },
|
||||
// {
|
||||
// path: 'create/webshop-p4m/update',
|
||||
// component: UpdateP4MWebshopCustomerComponent,
|
||||
// data: { customerType: 'webshop' },
|
||||
// },
|
||||
{
|
||||
path: 'create-customer-main',
|
||||
outlet: 'side',
|
||||
|
||||
@@ -16,13 +16,34 @@
|
||||
[deltaEnd]="150"
|
||||
[itemLength]="itemLength$ | async"
|
||||
[containerHeight]="24.5"
|
||||
>
|
||||
@for (bueryNumberGroup of items$ | async | groupBy: byBuyerNumberFn; track bueryNumberGroup) {
|
||||
>
|
||||
@for (
|
||||
bueryNumberGroup of items$ | async | groupBy: byBuyerNumberFn;
|
||||
track bueryNumberGroup
|
||||
) {
|
||||
<shared-goods-in-out-order-group>
|
||||
@for (orderNumberGroup of bueryNumberGroup.items | groupBy: byOrderNumberFn; track orderNumberGroup; let lastOrderNumber = $last) {
|
||||
@for (processingStatusGroup of orderNumberGroup.items | groupBy: byProcessingStatusFn; track processingStatusGroup; let lastProcessingStatus = $last) {
|
||||
@for (compartmentCodeGroup of processingStatusGroup.items | groupBy: byCompartmentCodeFn; track compartmentCodeGroup; let lastCompartmentCode = $last) {
|
||||
@for (item of compartmentCodeGroup.items; track item; let firstItem = $first) {
|
||||
@for (
|
||||
orderNumberGroup of bueryNumberGroup.items | groupBy: byOrderNumberFn;
|
||||
track orderNumberGroup;
|
||||
let lastOrderNumber = $last
|
||||
) {
|
||||
@for (
|
||||
processingStatusGroup of orderNumberGroup.items
|
||||
| groupBy: byProcessingStatusFn;
|
||||
track processingStatusGroup;
|
||||
let lastProcessingStatus = $last
|
||||
) {
|
||||
@for (
|
||||
compartmentCodeGroup of processingStatusGroup.items
|
||||
| groupBy: byCompartmentCodeFn;
|
||||
track compartmentCodeGroup;
|
||||
let lastCompartmentCode = $last
|
||||
) {
|
||||
@for (
|
||||
item of compartmentCodeGroup.items;
|
||||
track item;
|
||||
let firstItem = $first
|
||||
) {
|
||||
<shared-goods-in-out-order-group-item
|
||||
[item]="item"
|
||||
[showCompartmentCode]="firstItem"
|
||||
@@ -49,7 +70,6 @@
|
||||
<div class="empty-message">Es sind im Moment keine Artikel vorhanden</div>
|
||||
}
|
||||
|
||||
|
||||
<div class="actions">
|
||||
@if (actions$ | async; as actions) {
|
||||
@for (action of actions; track action) {
|
||||
@@ -57,19 +77,27 @@
|
||||
[disabled]="(changeActionLoader$ | async) || (loading$ | async)"
|
||||
class="cta-action cta-action-primary"
|
||||
(click)="handleAction(action)"
|
||||
>
|
||||
<ui-spinner
|
||||
[show]="(changeActionLoader$ | async) || (loading$ | async)"
|
||||
>{{ action.label }}</ui-spinner
|
||||
>
|
||||
<ui-spinner [show]="(changeActionLoader$ | async) || (loading$ | async)">{{ action.label }}</ui-spinner>
|
||||
</button>
|
||||
}
|
||||
}
|
||||
|
||||
@if (listEmpty$ | async) {
|
||||
<a class="cta-action cta-action-secondary" [routerLink]="['/filiale', 'goods', 'in']">
|
||||
<a
|
||||
class="cta-action cta-action-secondary"
|
||||
[routerLink]="['/filiale', 'goods', 'in']"
|
||||
>
|
||||
Zur Bestellpostensuche
|
||||
</a>
|
||||
}
|
||||
|
||||
@if (listEmpty$ | async) {
|
||||
<a class="cta-action cta-action-primary" [routerLink]="['/filiale', 'remission']">Zur Remission</a>
|
||||
<a class="cta-action cta-action-primary" [routerLink]="remissionPath()"
|
||||
>Zur Remission</a
|
||||
>
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,18 @@
|
||||
import { ChangeDetectionStrategy, Component, OnDestroy, OnInit, ViewChild, inject } from '@angular/core';
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
ViewChild,
|
||||
inject,
|
||||
linkedSignal,
|
||||
} from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { KeyValueDTOOfStringAndString, OrderItemListItemDTO } from '@generated/swagger/oms-api';
|
||||
import {
|
||||
KeyValueDTOOfStringAndString,
|
||||
OrderItemListItemDTO,
|
||||
} from '@generated/swagger/oms-api';
|
||||
import { UiErrorModalComponent, UiModalService } from '@ui/modal';
|
||||
import { UiScrollContainerComponent } from '@ui/scroll-container';
|
||||
import { BehaviorSubject, combineLatest, Subject } from 'rxjs';
|
||||
@@ -11,6 +22,7 @@ import { Config } from '@core/config';
|
||||
import { ToasterService } from '@shared/shell';
|
||||
import { PickupShelfInNavigationService } from '@shared/services/navigation';
|
||||
import { CacheService } from '@core/cache';
|
||||
import { TabService } from '@isa/core/tabs';
|
||||
|
||||
@Component({
|
||||
selector: 'page-goods-in-remission-preview',
|
||||
@@ -21,8 +33,12 @@ import { CacheService } from '@core/cache';
|
||||
standalone: false,
|
||||
})
|
||||
export class GoodsInRemissionPreviewComponent implements OnInit, OnDestroy {
|
||||
private _pickupShelfInNavigationService = inject(PickupShelfInNavigationService);
|
||||
@ViewChild(UiScrollContainerComponent) scrollContainer: UiScrollContainerComponent;
|
||||
tabService = inject(TabService);
|
||||
private _pickupShelfInNavigationService = inject(
|
||||
PickupShelfInNavigationService,
|
||||
);
|
||||
@ViewChild(UiScrollContainerComponent)
|
||||
scrollContainer: UiScrollContainerComponent;
|
||||
|
||||
items$ = this._store.results$;
|
||||
|
||||
@@ -50,10 +66,18 @@ export class GoodsInRemissionPreviewComponent implements OnInit, OnDestroy {
|
||||
byProcessingStatusFn = (item: OrderItemListItemDTO) => item.processingStatus;
|
||||
|
||||
byCompartmentCodeFn = (item: OrderItemListItemDTO) =>
|
||||
item.compartmentInfo ? `${item.compartmentCode}_${item.compartmentInfo}` : item.compartmentCode;
|
||||
item.compartmentInfo
|
||||
? `${item.compartmentCode}_${item.compartmentInfo}`
|
||||
: item.compartmentCode;
|
||||
|
||||
private readonly SCROLL_POSITION_TOKEN = 'REMISSION_PREVIEW_SCROLL_POSITION';
|
||||
|
||||
remissionPath = linkedSignal(() => [
|
||||
'/',
|
||||
this.tabService.activatedTab()?.id || this.tabService.nextId(),
|
||||
'remission',
|
||||
]);
|
||||
|
||||
constructor(
|
||||
private _breadcrumb: BreadcrumbService,
|
||||
private _store: GoodsInRemissionPreviewStore,
|
||||
@@ -78,12 +102,18 @@ export class GoodsInRemissionPreviewComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
private _removeScrollPositionFromCache(): void {
|
||||
this._cache.delete({ processId: this._config.get('process.ids.goodsIn'), token: this.SCROLL_POSITION_TOKEN });
|
||||
this._cache.delete({
|
||||
processId: this._config.get('process.ids.goodsIn'),
|
||||
token: this.SCROLL_POSITION_TOKEN,
|
||||
});
|
||||
}
|
||||
|
||||
private _addScrollPositionToCache(): void {
|
||||
this._cache.set<number>(
|
||||
{ processId: this._config.get('process.ids.goodsIn'), token: this.SCROLL_POSITION_TOKEN },
|
||||
{
|
||||
processId: this._config.get('process.ids.goodsIn'),
|
||||
token: this.SCROLL_POSITION_TOKEN,
|
||||
},
|
||||
this.scrollContainer?.scrollPos,
|
||||
);
|
||||
}
|
||||
@@ -108,7 +138,10 @@ export class GoodsInRemissionPreviewComponent implements OnInit, OnDestroy {
|
||||
|
||||
async updateBreadcrumb() {
|
||||
const crumbs = await this._breadcrumb
|
||||
.getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'preview'])
|
||||
.getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), [
|
||||
'goods-in',
|
||||
'preview',
|
||||
])
|
||||
.pipe(first())
|
||||
.toPromise();
|
||||
for (const crumb of crumbs) {
|
||||
@@ -120,12 +153,15 @@ export class GoodsInRemissionPreviewComponent implements OnInit, OnDestroy {
|
||||
|
||||
async removeBreadcrumbs() {
|
||||
let breadcrumbsToDelete = await this._breadcrumb
|
||||
.getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in'])
|
||||
.getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), [
|
||||
'goods-in',
|
||||
])
|
||||
.pipe(first())
|
||||
.toPromise();
|
||||
|
||||
breadcrumbsToDelete = breadcrumbsToDelete.filter(
|
||||
(crumb) => !crumb.tags.includes('preview') && !crumb.tags.includes('main'),
|
||||
(crumb) =>
|
||||
!crumb.tags.includes('preview') && !crumb.tags.includes('main'),
|
||||
);
|
||||
|
||||
breadcrumbsToDelete.forEach((crumb) => {
|
||||
@@ -133,11 +169,17 @@ export class GoodsInRemissionPreviewComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
|
||||
const detailsCrumbs = await this._breadcrumb
|
||||
.getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'details'])
|
||||
.getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), [
|
||||
'goods-in',
|
||||
'details',
|
||||
])
|
||||
.pipe(first())
|
||||
.toPromise();
|
||||
const editCrumbs = await this._breadcrumb
|
||||
.getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'edit'])
|
||||
.getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), [
|
||||
'goods-in',
|
||||
'edit',
|
||||
])
|
||||
.pipe(first())
|
||||
.toPromise();
|
||||
|
||||
@@ -152,32 +194,44 @@ export class GoodsInRemissionPreviewComponent implements OnInit, OnDestroy {
|
||||
|
||||
initInitialSearch() {
|
||||
if (this._store.hits === 0) {
|
||||
this._store.searchResult$.pipe(takeUntil(this._onDestroy$)).subscribe(async (result) => {
|
||||
await this.createBreadcrumb();
|
||||
this._store.searchResult$
|
||||
.pipe(takeUntil(this._onDestroy$))
|
||||
.subscribe(async (result) => {
|
||||
await this.createBreadcrumb();
|
||||
|
||||
this.scrollContainer?.scrollTo((await this._getScrollPositionFromCache()) ?? 0);
|
||||
this._removeScrollPositionFromCache();
|
||||
});
|
||||
this.scrollContainer?.scrollTo(
|
||||
(await this._getScrollPositionFromCache()) ?? 0,
|
||||
);
|
||||
this._removeScrollPositionFromCache();
|
||||
});
|
||||
}
|
||||
|
||||
this._store.search();
|
||||
}
|
||||
|
||||
async navigateToRemission() {
|
||||
await this._router.navigate(['/filiale/remission']);
|
||||
await this._router.navigate(this.remissionPath());
|
||||
}
|
||||
|
||||
navigateToDetails(orderItem: OrderItemListItemDTO) {
|
||||
const nav = this._pickupShelfInNavigationService.detailRoute({ item: orderItem, side: false });
|
||||
const nav = this._pickupShelfInNavigationService.detailRoute({
|
||||
item: orderItem,
|
||||
side: false,
|
||||
});
|
||||
|
||||
this._router.navigate(nav.path, { queryParams: { ...nav.queryParams, view: 'remission' } });
|
||||
this._router.navigate(nav.path, {
|
||||
queryParams: { ...nav.queryParams, view: 'remission' },
|
||||
});
|
||||
}
|
||||
|
||||
async handleAction(action: KeyValueDTOOfStringAndString) {
|
||||
this.changeActionLoader$.next(true);
|
||||
|
||||
try {
|
||||
const response = await this._store.createRemissionFromPreview().pipe(first()).toPromise();
|
||||
const response = await this._store
|
||||
.createRemissionFromPreview()
|
||||
.pipe(first())
|
||||
.toPromise();
|
||||
|
||||
if (!response?.dialog) {
|
||||
this._toast.open({
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CurrencyPipe, DatePipe } from '@angular/common';
|
||||
import { DatePipe } from '@angular/common';
|
||||
import { ChangeDetectionStrategy, Component, ElementRef, Input, inject } from '@angular/core';
|
||||
import { RouterLink, RouterLinkActive } from '@angular/router';
|
||||
import { NavigateOnClickDirective, ProductImageModule } from '@cdn/product-image';
|
||||
@@ -25,7 +25,6 @@ import { MatomoModule } from 'ngx-matomo-client';
|
||||
RouterLinkActive,
|
||||
IconModule,
|
||||
DatePipe,
|
||||
CurrencyPipe,
|
||||
ProductImageModule,
|
||||
UiCommonModule,
|
||||
PickupShelfProcessingStatusPipe,
|
||||
|
||||
@@ -1 +1 @@
|
||||
@import "./components/icon";
|
||||
@use "./components/icon";
|
||||
|
||||
@@ -16,20 +16,20 @@ import {
|
||||
forwardRef,
|
||||
Optional,
|
||||
inject,
|
||||
} from '@angular/core';
|
||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { UiAutocompleteComponent } from '@ui/autocomplete';
|
||||
import { UiFormControlDirective } from '@ui/form-control';
|
||||
import { containsElement } from '@utils/common';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { ScanAdapterService } from '@adapter/scan';
|
||||
import { injectCancelSearch } from '@shared/services/cancel-subject';
|
||||
import { EnvironmentService } from '@core/environment';
|
||||
} from "@angular/core";
|
||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms";
|
||||
import { UiAutocompleteComponent } from "@ui/autocomplete";
|
||||
import { UiFormControlDirective } from "@ui/form-control";
|
||||
import { containsElement } from "@utils/common";
|
||||
import { Subscription } from "rxjs";
|
||||
import { ScanAdapterService } from "@adapter/scan";
|
||||
import { injectCancelSearch } from "@shared/services/cancel-subject";
|
||||
import { EnvironmentService } from "@core/environment";
|
||||
|
||||
@Component({
|
||||
selector: 'shared-searchbox',
|
||||
templateUrl: 'searchbox.component.html',
|
||||
styleUrls: ['searchbox.component.scss'],
|
||||
selector: "shared-searchbox",
|
||||
templateUrl: "searchbox.component.html",
|
||||
styleUrls: ["searchbox.component.scss"],
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
@@ -49,9 +49,9 @@ export class SearchboxComponent
|
||||
cancelSearch = injectCancelSearch({ optional: true });
|
||||
|
||||
disabled: boolean;
|
||||
type = 'text';
|
||||
type = "text";
|
||||
|
||||
@ViewChild('input', { read: ElementRef, static: true })
|
||||
@ViewChild("input", { read: ElementRef, static: true })
|
||||
input: ElementRef;
|
||||
|
||||
@ContentChild(UiAutocompleteComponent)
|
||||
@@ -61,9 +61,9 @@ export class SearchboxComponent
|
||||
focusAfterViewInit = true;
|
||||
|
||||
@Input()
|
||||
placeholder = '';
|
||||
placeholder = "";
|
||||
|
||||
private _query = '';
|
||||
private _query = "";
|
||||
|
||||
@Input()
|
||||
get query() {
|
||||
@@ -94,7 +94,7 @@ export class SearchboxComponent
|
||||
scanner = false;
|
||||
|
||||
@Input()
|
||||
hint = '';
|
||||
hint = "";
|
||||
|
||||
@Input()
|
||||
autocompleteValueSelector: (item: any) => string = (item: any) => item;
|
||||
@@ -104,11 +104,11 @@ export class SearchboxComponent
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.setQuery('');
|
||||
this.setQuery("");
|
||||
this.cancelSearch();
|
||||
}
|
||||
|
||||
@HostBinding('class.autocomplete-opend')
|
||||
@HostBinding("class.autocomplete-opend")
|
||||
get autocompleteOpen() {
|
||||
return this.autocomplete?.opend;
|
||||
}
|
||||
@@ -213,13 +213,13 @@ export class SearchboxComponent
|
||||
}
|
||||
|
||||
clearHint() {
|
||||
this.hint = '';
|
||||
this.hint = "";
|
||||
this.focused.emit(true);
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
|
||||
onKeyup(event: KeyboardEvent) {
|
||||
if (event.key === 'Enter') {
|
||||
if (event.key === "Enter") {
|
||||
if (this.autocomplete?.opend && this.autocomplete?.activeItem) {
|
||||
this.setQuery(this.autocomplete?.activeItem?.item);
|
||||
this.autocomplete?.close();
|
||||
@@ -227,7 +227,7 @@ export class SearchboxComponent
|
||||
this.search.emit(this.query);
|
||||
|
||||
event.preventDefault();
|
||||
} else if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {
|
||||
} else if (event.key === "ArrowUp" || event.key === "ArrowDown") {
|
||||
this.handleArrowUpDownEvent(event);
|
||||
}
|
||||
}
|
||||
@@ -242,7 +242,7 @@ export class SearchboxComponent
|
||||
}
|
||||
}
|
||||
|
||||
@HostListener('window:click', ['$event'])
|
||||
@HostListener("window:click", ["$event"])
|
||||
focusLost(event: MouseEvent) {
|
||||
if (
|
||||
this.autocomplete?.opend &&
|
||||
@@ -256,9 +256,11 @@ export class SearchboxComponent
|
||||
this.search.emit(this.query);
|
||||
}
|
||||
|
||||
@HostListener('focusout', ['$event'])
|
||||
@HostListener("focusout", ["$event"])
|
||||
onBlur() {
|
||||
this.onTouched();
|
||||
if (typeof this.onTouched === "function") {
|
||||
this.onTouched();
|
||||
}
|
||||
this.focused.emit(false);
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
|
||||
@@ -3,7 +3,10 @@ import { Injectable } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { CustomerInfoDTO } from '@generated/swagger/crm-api';
|
||||
import { NavigationRoute } from './defs/navigation-route';
|
||||
import { encodeFormData, mapCustomerInfoDtoToCustomerCreateFormData } from 'apps/isa-app/src/page/customer';
|
||||
import {
|
||||
encodeFormData,
|
||||
mapCustomerInfoDtoToCustomerCreateFormData,
|
||||
} from 'apps/isa-app/src/page/customer';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class CustomerCreateNavigation {
|
||||
@@ -33,7 +36,9 @@ export class CustomerCreateNavigation {
|
||||
|
||||
navigateToDefault(params: { processId: NumberInput }): Promise<boolean> {
|
||||
const route = this.defaultRoute(params);
|
||||
return this._router.navigate(route.path, { queryParams: route.queryParams });
|
||||
return this._router.navigate(route.path, {
|
||||
queryParams: route.queryParams,
|
||||
});
|
||||
}
|
||||
|
||||
createCustomerRoute(params: {
|
||||
@@ -54,7 +59,9 @@ export class CustomerCreateNavigation {
|
||||
];
|
||||
|
||||
let formData = params?.customerInfo
|
||||
? encodeFormData(mapCustomerInfoDtoToCustomerCreateFormData(params.customerInfo))
|
||||
? encodeFormData(
|
||||
mapCustomerInfoDtoToCustomerCreateFormData(params.customerInfo),
|
||||
)
|
||||
: undefined;
|
||||
|
||||
const urlTree = this._router.createUrlTree(path, {
|
||||
@@ -79,7 +86,9 @@ export class CustomerCreateNavigation {
|
||||
processId: NumberInput;
|
||||
customerInfo: CustomerInfoDTO;
|
||||
}): NavigationRoute {
|
||||
const formData = encodeFormData(mapCustomerInfoDtoToCustomerCreateFormData(customerInfo));
|
||||
const formData = encodeFormData(
|
||||
mapCustomerInfoDtoToCustomerCreateFormData(customerInfo),
|
||||
);
|
||||
const path = [
|
||||
'/kunde',
|
||||
coerceNumberProperty(processId),
|
||||
@@ -88,14 +97,16 @@ export class CustomerCreateNavigation {
|
||||
outlets: {
|
||||
primary: [
|
||||
'create',
|
||||
customerInfo?.features?.find((feature) => feature.key === 'webshop') ? 'webshop-p4m' : 'store-p4m',
|
||||
// customerInfo?.features?.find((feature) => feature.key === 'webshop') ? 'webshop-p4m' : 'store-p4m',
|
||||
],
|
||||
side: 'create-customer-main',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const urlTree = this._router.createUrlTree(path, { queryParams: { formData } });
|
||||
const urlTree = this._router.createUrlTree(path, {
|
||||
queryParams: { formData },
|
||||
});
|
||||
|
||||
return {
|
||||
path,
|
||||
|
||||
@@ -1,303 +1,338 @@
|
||||
<div class="side-menu-group">
|
||||
<span class="side-menu-group-label">Kunden</span>
|
||||
<nav class="side-menu-group-nav">
|
||||
<a
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu(); resetBranch(); focusSearchBox()"
|
||||
[routerLink]="productRoutePath$ | async"
|
||||
sharedRegexRouterLinkActive="active"
|
||||
sharedRegexRouterLinkActiveTest="^\/kunde\/\d*\/product"
|
||||
(isActiveChange)="focusSearchBox()"
|
||||
>
|
||||
<div class="side-menu-group-item-icon">
|
||||
<shared-icon icon="import-contacts"></shared-icon>
|
||||
</div>
|
||||
<span class="side-menu-group-item-label">Artikelsuche</span>
|
||||
</a>
|
||||
|
||||
<div class="side-menu-group-sub-item-wrapper">
|
||||
@if (customerSearchRoute$ | async; as customerSearchRoute) {
|
||||
<a
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu(); focusSearchBox()"
|
||||
[routerLink]="customerSearchRoute.path"
|
||||
[queryParams]="customerSearchRoute.queryParams"
|
||||
sharedRegexRouterLinkActive="active"
|
||||
sharedRegexRouterLinkActiveTest="^\/kunde\/\d*\/customer"
|
||||
(isActiveChange)="customerActive($event); focusSearchBox()"
|
||||
>
|
||||
<span class="side-menu-group-item-icon">
|
||||
<shared-icon icon="person"></shared-icon>
|
||||
</span>
|
||||
<span class="side-menu-group-item-label">Kunden</span>
|
||||
<button
|
||||
class="side-menu-group-arrow"
|
||||
[class.side-menu-item-rotate]="customerExpanded"
|
||||
(click)="
|
||||
$event.stopPropagation();
|
||||
$event.preventDefault();
|
||||
customerExpanded = !customerExpanded
|
||||
"
|
||||
>
|
||||
<shared-icon icon="keyboard-arrow-down"></shared-icon>
|
||||
</button>
|
||||
</a>
|
||||
}
|
||||
|
||||
<div class="side-menu-group-sub-items" [class.hidden]="!customerExpanded">
|
||||
@if (customerSearchRoute$ | async; as customerSearchRoute) {
|
||||
<a
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu(); focusSearchBox()"
|
||||
[routerLink]="customerSearchRoute.path"
|
||||
[queryParams]="customerSearchRoute.queryParams"
|
||||
sharedRegexRouterLinkActive="active"
|
||||
sharedRegexRouterLinkActiveTest="^\/kunde\/\d*\/customer\/(\(search|search)"
|
||||
(isActiveChange)="focusSearchBox()"
|
||||
>
|
||||
<span class="side-menu-group-item-icon"></span>
|
||||
<span class="side-menu-group-item-label">Suchen</span>
|
||||
</a>
|
||||
}
|
||||
@if (customerCreateRoute$ | async; as customerCreateRoute) {
|
||||
<a
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu()"
|
||||
[routerLink]="customerCreateRoute.path"
|
||||
[queryParams]="customerCreateRoute.queryParams"
|
||||
sharedRegexRouterLinkActive="active"
|
||||
sharedRegexRouterLinkActiveTest="^\/kunde\/\d*\/customer\/(\(create|create)"
|
||||
>
|
||||
<span class="side-menu-group-item-icon"></span>
|
||||
<span class="side-menu-group-item-label">Erfassen</span>
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a
|
||||
*ifRole="'Store'"
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu(); focusSearchBox()"
|
||||
[routerLink]="pickUpShelfOutRoutePath$ | async"
|
||||
sharedRegexRouterLinkActive="active"
|
||||
sharedRegexRouterLinkActiveTest="^\/kunde\/\d*\/pickup-shelf"
|
||||
(isActiveChange)="focusSearchBox()"
|
||||
>
|
||||
<span class="side-menu-group-item-icon">
|
||||
<shared-icon icon="unarchive"></shared-icon>
|
||||
</span>
|
||||
<span class="side-menu-group-item-label">Warenausgabe</span>
|
||||
</a>
|
||||
<a
|
||||
*ifRole="'Store'"
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu(); focusSearchBox()"
|
||||
[routerLink]="[
|
||||
'/',
|
||||
processService.activatedTab()?.id || processService.nextId(),
|
||||
'return',
|
||||
]"
|
||||
(isActiveChange)="focusSearchBox()"
|
||||
>
|
||||
<span class="side-menu-group-item-icon w-[2.375rem] h-12">
|
||||
<ng-icon name="isaNavigationReturn"></ng-icon>
|
||||
</span>
|
||||
<span class="side-menu-group-item-label">Retoure</span>
|
||||
</a>
|
||||
|
||||
<a
|
||||
*ifRole="'CallCenter'"
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu(); resetBranch(); focusSearchBox()"
|
||||
[routerLink]="customerOrdersRoutePath$ | async"
|
||||
sharedRegexRouterLinkActive="active"
|
||||
sharedRegexRouterLinkActiveTest="^\/kunde\/\d*\/order"
|
||||
(isActiveChange)="focusSearchBox()"
|
||||
>
|
||||
<span class="side-menu-group-item-icon">
|
||||
<shared-icon icon="deployed-code"></shared-icon>
|
||||
</span>
|
||||
<span class="side-menu-group-item-label">Bestellungen</span>
|
||||
</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="side-menu-group" *ifRole="'Store'">
|
||||
<span class="side-menu-group-label">Filiale</span>
|
||||
<nav class="side-menu-group-nav">
|
||||
@if (taskCalenderNavigation$ | async; as taskCalenderNavigation) {
|
||||
<a
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu(); focusSearchBox()"
|
||||
[routerLink]="taskCalenderNavigation.path"
|
||||
[queryParams]="taskCalenderNavigation.queryParams"
|
||||
routerLinkActive="active"
|
||||
(isActiveChange)="focusSearchBox()"
|
||||
>
|
||||
<span class="side-menu-group-item-icon">
|
||||
<shared-icon icon="event-available"></shared-icon>
|
||||
</span>
|
||||
<span class="side-menu-group-item-label">Kalender</span>
|
||||
</a>
|
||||
}
|
||||
@if (assortmentNavigation$ | async; as assortmentNavigation) {
|
||||
<a
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu()"
|
||||
[routerLink]="assortmentNavigation.path"
|
||||
[queryParams]="assortmentNavigation.queryParams"
|
||||
routerLinkActive="active"
|
||||
>
|
||||
<span class="side-menu-group-item-icon">
|
||||
<shared-icon icon="shape-outline"></shared-icon>
|
||||
</span>
|
||||
<span class="side-menu-group-item-label">Sortiment</span>
|
||||
</a>
|
||||
}
|
||||
|
||||
<div class="side-menu-group-sub-item-wrapper">
|
||||
@if (pickUpShelfInRoutePath$ | async; as pickUpShelfInNavigation) {
|
||||
<a
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu(); focusSearchBox()"
|
||||
[routerLink]="pickUpShelfInNavigation.path"
|
||||
[queryParams]="pickUpShelfInNavigation.queryParams"
|
||||
sharedRegexRouterLinkActive="active"
|
||||
sharedRegexRouterLinkActiveTest="^\/filiale\/(pickup-shelf|goods\/in)"
|
||||
(isActiveChange)="shelfActive($event); focusSearchBox()"
|
||||
>
|
||||
<span class="side-menu-group-item-icon">
|
||||
<shared-icon icon="isa-abholfach"></shared-icon>
|
||||
</span>
|
||||
<span class="side-menu-group-item-label">Abholfach</span>
|
||||
<button
|
||||
class="side-menu-group-arrow"
|
||||
[class.side-menu-item-rotate]="shelfExpanded"
|
||||
(click)="
|
||||
$event.stopPropagation();
|
||||
$event.preventDefault();
|
||||
shelfExpanded = !shelfExpanded
|
||||
"
|
||||
>
|
||||
<shared-icon icon="keyboard-arrow-down"></shared-icon>
|
||||
</button>
|
||||
</a>
|
||||
}
|
||||
|
||||
<div class="side-menu-group-sub-items" [class.hidden]="!shelfExpanded">
|
||||
@if (pickUpShelfInRoutePath$ | async; as pickUpShelfInListNavigation) {
|
||||
<a
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu(); focusSearchBox()"
|
||||
[routerLink]="pickUpShelfInListNavigation.path"
|
||||
[queryParams]="pickUpShelfInListNavigation.queryParams"
|
||||
[class.has-child-view]="currentShelfView$ | async"
|
||||
sharedRegexRouterLinkActive="active"
|
||||
[sharedRegexRouterLinkActiveTest]="'^\/filiale\/pickup-shelf'"
|
||||
(isActiveChange)="shelfActive($event); focusSearchBox()"
|
||||
>
|
||||
<span class="side-menu-group-item-icon"></span>
|
||||
<span class="side-menu-group-item-label">Einbuchen</span>
|
||||
</a>
|
||||
}
|
||||
<a
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu()"
|
||||
[routerLink]="['/filiale', 'goods', 'in', 'reservation']"
|
||||
[queryParams]="{ view: 'reservation' }"
|
||||
[class.active-child]="(currentShelfView$ | async) === 'reservation'"
|
||||
routerLinkActive="active"
|
||||
(isActiveChange)="shelfActive($event)"
|
||||
>
|
||||
<span class="side-menu-group-item-icon"></span>
|
||||
<span class="side-menu-group-item-label">Reservierung</span>
|
||||
</a>
|
||||
<a
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu()"
|
||||
[routerLink]="['/filiale', 'goods', 'in', 'cleanup']"
|
||||
[queryParams]="{ view: 'cleanup' }"
|
||||
[class.active-child]="(currentShelfView$ | async) === 'cleanup'"
|
||||
routerLinkActive="active"
|
||||
(isActiveChange)="shelfActive($event)"
|
||||
>
|
||||
<span class="side-menu-group-item-icon"></span>
|
||||
<span class="side-menu-group-item-label">Ausräumen</span>
|
||||
</a>
|
||||
<a
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu()"
|
||||
[routerLink]="['/filiale', 'goods', 'in', 'preview']"
|
||||
[queryParams]="{ view: 'remission' }"
|
||||
[class.active-child]="(currentShelfView$ | async) === 'remission'"
|
||||
routerLinkActive="active"
|
||||
(isActiveChange)="shelfActive($event)"
|
||||
>
|
||||
<span class="side-menu-group-item-icon"></span>
|
||||
<span class="side-menu-group-item-label">Remi-Vorschau</span>
|
||||
</a>
|
||||
<a
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu()"
|
||||
[routerLink]="['/filiale', 'goods', 'in', 'list']"
|
||||
[queryParams]="{ view: 'wareneingangsliste' }"
|
||||
[class.active-child]="
|
||||
(currentShelfView$ | async) === 'wareneingangsliste'
|
||||
"
|
||||
routerLinkActive="active"
|
||||
(isActiveChange)="shelfActive($event)"
|
||||
>
|
||||
<span class="side-menu-group-item-icon"></span>
|
||||
<span class="side-menu-group-item-label">Fehlende</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (remissionNavigation$ | async; as remissionNavigation) {
|
||||
<a
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu()"
|
||||
[routerLink]="remissionNavigation.path"
|
||||
[queryParams]="remissionNavigation.queryParams"
|
||||
routerLinkActive="active"
|
||||
>
|
||||
<span class="side-menu-group-item-icon">
|
||||
<shared-icon icon="assignment-return"></shared-icon>
|
||||
</span>
|
||||
<span class="side-menu-group-item-label">Remission</span>
|
||||
</a>
|
||||
}
|
||||
|
||||
@if (packageInspectionNavigation$ | async; as packageInspectionNavigation) {
|
||||
<a
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu(); fetchAndOpenPackages()"
|
||||
[routerLink]="packageInspectionNavigation.path"
|
||||
[queryParams]="packageInspectionNavigation.queryParams"
|
||||
routerLinkActive="active"
|
||||
>
|
||||
<span class="side-menu-group-item-icon">
|
||||
<shared-icon icon="clipboard-check-outline"></shared-icon>
|
||||
</span>
|
||||
<span class="side-menu-group-item-label">Wareneingang</span>
|
||||
</a>
|
||||
}
|
||||
|
||||
<a
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu(); focusSearchBox()"
|
||||
[routerLink]="[
|
||||
'/',
|
||||
processService.activatedTab()?.id || processService.nextId(),
|
||||
'remission',
|
||||
]"
|
||||
(isActiveChange)="focusSearchBox()"
|
||||
>
|
||||
<span class="side-menu-group-item-icon w-[2.375rem] h-12">
|
||||
<ng-icon name="isaNavigationRemission2"></ng-icon>
|
||||
</span>
|
||||
<span class="side-menu-group-item-label">Remission</span>
|
||||
</a>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="side-menu-group">
|
||||
<span class="side-menu-group-label">Kunden</span>
|
||||
<nav class="side-menu-group-nav">
|
||||
<a
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu(); resetBranch(); focusSearchBox()"
|
||||
[routerLink]="productRoutePath$ | async"
|
||||
sharedRegexRouterLinkActive="active"
|
||||
sharedRegexRouterLinkActiveTest="^\/kunde\/\d*\/product"
|
||||
(isActiveChange)="focusSearchBox()"
|
||||
>
|
||||
<div class="side-menu-group-item-icon">
|
||||
<shared-icon icon="import-contacts"></shared-icon>
|
||||
</div>
|
||||
<span class="side-menu-group-item-label">Artikelsuche</span>
|
||||
</a>
|
||||
|
||||
<div class="side-menu-group-sub-item-wrapper">
|
||||
@if (customerSearchRoute$ | async; as customerSearchRoute) {
|
||||
<a
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu(); focusSearchBox()"
|
||||
[routerLink]="customerSearchRoute.path"
|
||||
[queryParams]="customerSearchRoute.queryParams"
|
||||
sharedRegexRouterLinkActive="active"
|
||||
sharedRegexRouterLinkActiveTest="^\/kunde\/\d*\/customer"
|
||||
(isActiveChange)="customerActive($event); focusSearchBox()"
|
||||
>
|
||||
<span class="side-menu-group-item-icon">
|
||||
<shared-icon icon="person"></shared-icon>
|
||||
</span>
|
||||
<span class="side-menu-group-item-label">Kunden</span>
|
||||
<button
|
||||
class="side-menu-group-arrow"
|
||||
[class.side-menu-item-rotate]="customerExpanded"
|
||||
(click)="
|
||||
$event.stopPropagation();
|
||||
$event.preventDefault();
|
||||
customerExpanded = !customerExpanded
|
||||
"
|
||||
>
|
||||
<shared-icon icon="keyboard-arrow-down"></shared-icon>
|
||||
</button>
|
||||
</a>
|
||||
}
|
||||
|
||||
<div class="side-menu-group-sub-items" [class.hidden]="!customerExpanded">
|
||||
@if (customerSearchRoute$ | async; as customerSearchRoute) {
|
||||
<a
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu(); focusSearchBox()"
|
||||
[routerLink]="customerSearchRoute.path"
|
||||
[queryParams]="customerSearchRoute.queryParams"
|
||||
sharedRegexRouterLinkActive="active"
|
||||
sharedRegexRouterLinkActiveTest="^\/kunde\/\d*\/customer\/(\(search|search)"
|
||||
(isActiveChange)="focusSearchBox()"
|
||||
>
|
||||
<span class="side-menu-group-item-icon"></span>
|
||||
<span class="side-menu-group-item-label">Suchen</span>
|
||||
</a>
|
||||
}
|
||||
@if (customerCreateRoute$ | async; as customerCreateRoute) {
|
||||
<a
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu()"
|
||||
[routerLink]="customerCreateRoute.path"
|
||||
[queryParams]="customerCreateRoute.queryParams"
|
||||
sharedRegexRouterLinkActive="active"
|
||||
sharedRegexRouterLinkActiveTest="^\/kunde\/\d*\/customer\/(\(create|create)"
|
||||
>
|
||||
<span class="side-menu-group-item-icon"></span>
|
||||
<span class="side-menu-group-item-label">Erfassen</span>
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a
|
||||
*ifRole="'Store'"
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu(); focusSearchBox()"
|
||||
[routerLink]="pickUpShelfOutRoutePath$ | async"
|
||||
sharedRegexRouterLinkActive="active"
|
||||
sharedRegexRouterLinkActiveTest="^\/kunde\/\d*\/pickup-shelf"
|
||||
(isActiveChange)="focusSearchBox()"
|
||||
>
|
||||
<span class="side-menu-group-item-icon">
|
||||
<shared-icon icon="unarchive"></shared-icon>
|
||||
</span>
|
||||
<span class="side-menu-group-item-label">Warenausgabe</span>
|
||||
</a>
|
||||
<a
|
||||
*ifRole="'Store'"
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu(); focusSearchBox()"
|
||||
[routerLink]="[
|
||||
'/',
|
||||
processService.activatedTab()?.id || processService.nextId(),
|
||||
'return',
|
||||
]"
|
||||
(isActiveChange)="focusSearchBox()"
|
||||
>
|
||||
<span class="side-menu-group-item-icon w-[2.375rem] h-12">
|
||||
<ng-icon name="isaNavigationReturn"></ng-icon>
|
||||
</span>
|
||||
<span class="side-menu-group-item-label">Retoure</span>
|
||||
</a>
|
||||
|
||||
<a
|
||||
*ifRole="'CallCenter'"
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu(); resetBranch(); focusSearchBox()"
|
||||
[routerLink]="customerOrdersRoutePath$ | async"
|
||||
sharedRegexRouterLinkActive="active"
|
||||
sharedRegexRouterLinkActiveTest="^\/kunde\/\d*\/order"
|
||||
(isActiveChange)="focusSearchBox()"
|
||||
>
|
||||
<span class="side-menu-group-item-icon">
|
||||
<shared-icon icon="deployed-code"></shared-icon>
|
||||
</span>
|
||||
<span class="side-menu-group-item-label">Bestellungen</span>
|
||||
</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="side-menu-group" *ifRole="'Store'">
|
||||
<span class="side-menu-group-label">Filiale</span>
|
||||
<nav class="side-menu-group-nav">
|
||||
@if (taskCalenderNavigation$ | async; as taskCalenderNavigation) {
|
||||
<a
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu(); focusSearchBox()"
|
||||
[routerLink]="taskCalenderNavigation.path"
|
||||
[queryParams]="taskCalenderNavigation.queryParams"
|
||||
routerLinkActive="active"
|
||||
(isActiveChange)="focusSearchBox()"
|
||||
>
|
||||
<span class="side-menu-group-item-icon">
|
||||
<shared-icon icon="event-available"></shared-icon>
|
||||
</span>
|
||||
<span class="side-menu-group-item-label">Kalender</span>
|
||||
</a>
|
||||
}
|
||||
@if (assortmentNavigation$ | async; as assortmentNavigation) {
|
||||
<a
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu()"
|
||||
[routerLink]="assortmentNavigation.path"
|
||||
[queryParams]="assortmentNavigation.queryParams"
|
||||
routerLinkActive="active"
|
||||
>
|
||||
<span class="side-menu-group-item-icon">
|
||||
<shared-icon icon="shape-outline"></shared-icon>
|
||||
</span>
|
||||
<span class="side-menu-group-item-label">Sortiment</span>
|
||||
</a>
|
||||
}
|
||||
|
||||
<div class="side-menu-group-sub-item-wrapper">
|
||||
@if (pickUpShelfInRoutePath$ | async; as pickUpShelfInNavigation) {
|
||||
<a
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu(); focusSearchBox()"
|
||||
[routerLink]="pickUpShelfInNavigation.path"
|
||||
[queryParams]="pickUpShelfInNavigation.queryParams"
|
||||
sharedRegexRouterLinkActive="active"
|
||||
sharedRegexRouterLinkActiveTest="^\/filiale\/(pickup-shelf|goods\/in)"
|
||||
(isActiveChange)="shelfActive($event); focusSearchBox()"
|
||||
>
|
||||
<span class="side-menu-group-item-icon">
|
||||
<shared-icon icon="isa-abholfach"></shared-icon>
|
||||
</span>
|
||||
<span class="side-menu-group-item-label">Abholfach</span>
|
||||
<button
|
||||
class="side-menu-group-arrow"
|
||||
[class.side-menu-item-rotate]="shelfExpanded"
|
||||
(click)="
|
||||
$event.stopPropagation();
|
||||
$event.preventDefault();
|
||||
shelfExpanded = !shelfExpanded
|
||||
"
|
||||
>
|
||||
<shared-icon icon="keyboard-arrow-down"></shared-icon>
|
||||
</button>
|
||||
</a>
|
||||
}
|
||||
|
||||
<div class="side-menu-group-sub-items" [class.hidden]="!shelfExpanded">
|
||||
@if (pickUpShelfInRoutePath$ | async; as pickUpShelfInListNavigation) {
|
||||
<a
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu(); focusSearchBox()"
|
||||
[routerLink]="pickUpShelfInListNavigation.path"
|
||||
[queryParams]="pickUpShelfInListNavigation.queryParams"
|
||||
[class.has-child-view]="currentShelfView$ | async"
|
||||
sharedRegexRouterLinkActive="active"
|
||||
[sharedRegexRouterLinkActiveTest]="'^\/filiale\/pickup-shelf'"
|
||||
(isActiveChange)="shelfActive($event); focusSearchBox()"
|
||||
>
|
||||
<span class="side-menu-group-item-icon"></span>
|
||||
<span class="side-menu-group-item-label">Einbuchen</span>
|
||||
</a>
|
||||
}
|
||||
<a
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu()"
|
||||
[routerLink]="['/filiale', 'goods', 'in', 'reservation']"
|
||||
[queryParams]="{ view: 'reservation' }"
|
||||
[class.active-child]="(currentShelfView$ | async) === 'reservation'"
|
||||
routerLinkActive="active"
|
||||
(isActiveChange)="shelfActive($event)"
|
||||
>
|
||||
<span class="side-menu-group-item-icon"></span>
|
||||
<span class="side-menu-group-item-label">Reservierung</span>
|
||||
</a>
|
||||
<a
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu()"
|
||||
[routerLink]="['/filiale', 'goods', 'in', 'cleanup']"
|
||||
[queryParams]="{ view: 'cleanup' }"
|
||||
[class.active-child]="(currentShelfView$ | async) === 'cleanup'"
|
||||
routerLinkActive="active"
|
||||
(isActiveChange)="shelfActive($event)"
|
||||
>
|
||||
<span class="side-menu-group-item-icon"></span>
|
||||
<span class="side-menu-group-item-label">Ausräumen</span>
|
||||
</a>
|
||||
<a
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu()"
|
||||
[routerLink]="['/filiale', 'goods', 'in', 'preview']"
|
||||
[queryParams]="{ view: 'remission' }"
|
||||
[class.active-child]="(currentShelfView$ | async) === 'remission'"
|
||||
routerLinkActive="active"
|
||||
(isActiveChange)="shelfActive($event)"
|
||||
>
|
||||
<span class="side-menu-group-item-icon"></span>
|
||||
<span class="side-menu-group-item-label">Remi-Vorschau</span>
|
||||
</a>
|
||||
<a
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu()"
|
||||
[routerLink]="['/filiale', 'goods', 'in', 'list']"
|
||||
[queryParams]="{ view: 'wareneingangsliste' }"
|
||||
[class.active-child]="
|
||||
(currentShelfView$ | async) === 'wareneingangsliste'
|
||||
"
|
||||
routerLinkActive="active"
|
||||
(isActiveChange)="shelfActive($event)"
|
||||
>
|
||||
<span class="side-menu-group-item-icon"></span>
|
||||
<span class="side-menu-group-item-label">Fehlende</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="side-menu-group-sub-item-wrapper">
|
||||
<a
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu(); focusSearchBox()"
|
||||
[routerLink]="[
|
||||
'/',
|
||||
processService.activatedTab()?.id || processService.nextId(),
|
||||
'remission',
|
||||
]"
|
||||
(isActiveChange)="focusSearchBox(); remissionExpanded.set($event)"
|
||||
routerLinkActive="active"
|
||||
#rlActive="routerLinkActive"
|
||||
>
|
||||
<span class="side-menu-group-item-icon w-[2.375rem] h-12">
|
||||
<ng-icon name="isaNavigationRemission2" size="1.5rem"></ng-icon>
|
||||
</span>
|
||||
<span class="side-menu-group-item-label">Remission</span>
|
||||
<button
|
||||
class="side-menu-group-arrow"
|
||||
[class.side-menu-item-rotate]="remissionExpanded()"
|
||||
(click)="
|
||||
$event.stopPropagation();
|
||||
$event.preventDefault();
|
||||
remissionExpanded.set(!remissionExpanded())
|
||||
"
|
||||
>
|
||||
<shared-icon icon="keyboard-arrow-down"></shared-icon>
|
||||
</button>
|
||||
</a>
|
||||
@if (remissionExpanded()) {
|
||||
<div class="side-menu-group-sub-items">
|
||||
<a
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu(); focusSearchBox()"
|
||||
[routerLink]="[
|
||||
'/',
|
||||
processService.activatedTab()?.id || processService.nextId(),
|
||||
'remission',
|
||||
]"
|
||||
(isActiveChange)="focusSearchBox()"
|
||||
sharedRegexRouterLinkActive="active"
|
||||
sharedRegexRouterLinkActiveTest="^\/\d*\/remission\/(mandatory|department)"
|
||||
>
|
||||
<span class="side-menu-group-item-icon"> </span>
|
||||
<span class="side-menu-group-item-label">Remission</span>
|
||||
</a>
|
||||
<a
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu(); focusSearchBox()"
|
||||
[routerLink]="[
|
||||
'/',
|
||||
processService.activatedTab()?.id || processService.nextId(),
|
||||
'remission',
|
||||
'return-receipt',
|
||||
]"
|
||||
(isActiveChange)="focusSearchBox()"
|
||||
sharedRegexRouterLinkActive="active"
|
||||
sharedRegexRouterLinkActiveTest="^\/\d*\/remission\/return-receipt"
|
||||
>
|
||||
<span class="side-menu-group-item-icon"> </span>
|
||||
<span class="side-menu-group-item-label">Warenbegleitscheine</span>
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@if (packageInspectionNavigation$ | async; as packageInspectionNavigation) {
|
||||
<a
|
||||
class="side-menu-group-item"
|
||||
(click)="closeSideMenu(); fetchAndOpenPackages()"
|
||||
[routerLink]="packageInspectionNavigation.path"
|
||||
[queryParams]="packageInspectionNavigation.queryParams"
|
||||
routerLinkActive="active"
|
||||
>
|
||||
<span class="side-menu-group-item-icon">
|
||||
<shared-icon icon="clipboard-check-outline"></shared-icon>
|
||||
</span>
|
||||
<span class="side-menu-group-item-label">Wareneingang</span>
|
||||
</a>
|
||||
}
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
@@ -1,435 +1,426 @@
|
||||
import {
|
||||
Component,
|
||||
ChangeDetectionStrategy,
|
||||
Inject,
|
||||
ChangeDetectorRef,
|
||||
inject,
|
||||
DOCUMENT,
|
||||
} from '@angular/core';
|
||||
import { AuthModule, AuthService } from '@core/auth';
|
||||
import { StockService } from '@generated/swagger/wws-api';
|
||||
import { first, map, retry, switchMap, take } from 'rxjs/operators';
|
||||
import { ShellService } from '../shell.service';
|
||||
import { ApplicationProcess, ApplicationService } from '@core/application';
|
||||
import { ActivatedRoute, Router, RouterModule } from '@angular/router';
|
||||
import { EnvironmentService } from '@core/environment';
|
||||
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Config } from '@core/config';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { IconComponent } from '@shared/components/icon';
|
||||
import { RegexRouterLinkActiveDirective } from '@shared/directives/router-link-active';
|
||||
import { WrongDestinationModalService } from '@modal/wrong-destination';
|
||||
import {
|
||||
CustomerCreateNavigation,
|
||||
CustomerOrdersNavigationService,
|
||||
CustomerSearchNavigation,
|
||||
PickupShelfInNavigationService,
|
||||
PickUpShelfOutNavigationService,
|
||||
ProductCatalogNavigationService,
|
||||
} from '@shared/services/navigation';
|
||||
|
||||
import { TabService } from '@isa/core/tabs';
|
||||
import { NgIconComponent, provideIcons } from '@ng-icons/core';
|
||||
import { isaNavigationRemission2, isaNavigationReturn } from '@isa/icons';
|
||||
|
||||
@Component({
|
||||
selector: 'shell-side-menu',
|
||||
templateUrl: 'side-menu.component.html',
|
||||
styleUrls: ['side-menu.component.css'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [
|
||||
CommonModule,
|
||||
NgIconComponent,
|
||||
IconComponent,
|
||||
RouterModule,
|
||||
AuthModule,
|
||||
RegexRouterLinkActiveDirective,
|
||||
],
|
||||
providers: [provideIcons({ isaNavigationReturn, isaNavigationRemission2 })],
|
||||
})
|
||||
export class ShellSideMenuComponent {
|
||||
processService = inject(TabService);
|
||||
|
||||
branchKey$ = this._stockService.StockCurrentBranch().pipe(
|
||||
retry(3),
|
||||
map((x) => x.result.key),
|
||||
);
|
||||
|
||||
section$ = this._app.getSection$();
|
||||
|
||||
processes$ = this.section$.pipe(
|
||||
switchMap((section) => this._app.getProcesses$(section)),
|
||||
);
|
||||
|
||||
processesCount$ = this.processes$.pipe(
|
||||
map((processes) => processes?.length ?? 0),
|
||||
);
|
||||
|
||||
activeProcess$ = this._app.activatedProcessId$.pipe(
|
||||
switchMap((processId) => this._app.getProcessById$(processId)),
|
||||
);
|
||||
|
||||
get isTablet() {
|
||||
return this._environment.matchTablet();
|
||||
}
|
||||
|
||||
customerBasePath$ = this.activeProcess$.pipe(
|
||||
map((process) => {
|
||||
if (
|
||||
!!process &&
|
||||
process.section === 'customer' &&
|
||||
process.type !== 'cart-checkout'
|
||||
) {
|
||||
// Übernehme aktiven Prozess
|
||||
return `/kunde/${process.id}`;
|
||||
} else {
|
||||
// Über Guards wird ein neuer Prozess erstellt
|
||||
return '/kunde';
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
customerSearchRoute$ = this.getLastActivatedCustomerProcessId$().pipe(
|
||||
map((processId) => {
|
||||
return this._customerSearchNavigation.defaultRoute({ processId });
|
||||
}),
|
||||
);
|
||||
|
||||
customerCreateRoute$ = this.getLastActivatedCustomerProcessId$().pipe(
|
||||
map((processId) => {
|
||||
return this._customerCreateNavigation.defaultRoute({ processId });
|
||||
}),
|
||||
);
|
||||
|
||||
pickUpShelfOutRoutePath$ = this.getLastActivatedCustomerProcessId$().pipe(
|
||||
map((processId) => {
|
||||
if (processId) {
|
||||
// Übernehme aktiven Prozess
|
||||
return this._pickUpShelfOutNavigation.defaultRoute({ processId }).path;
|
||||
} else {
|
||||
// Über Guards wird ein neuer Prozess erstellt
|
||||
return this._pickUpShelfOutNavigation.defaultRoute({}).path;
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
productRoutePath$ = this.getLastActivatedCustomerProcessId$().pipe(
|
||||
map((processId) => {
|
||||
if (processId) {
|
||||
// Übernehme aktiven Prozess
|
||||
return this._catalogNavigationService.getArticleSearchBasePath(
|
||||
processId,
|
||||
).path;
|
||||
} else {
|
||||
// Über Guards wird ein neuer Prozess erstellt
|
||||
return this._catalogNavigationService.getArticleSearchBasePath().path;
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
customerOrdersRoutePath$ = this.getLastActivatedCustomerProcessId$().pipe(
|
||||
map((processId) => {
|
||||
if (processId) {
|
||||
// Übernehme aktiven Prozess
|
||||
return this._customerOrdersNavigationService.getCustomerOrdersBasePath(
|
||||
processId,
|
||||
).path;
|
||||
} else {
|
||||
// Über Guards wird ein neuer Prozess erstellt
|
||||
return this._customerOrdersNavigationService.getCustomerOrdersBasePath()
|
||||
.path;
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
taskCalenderNavigation$ = this.getLastNavigationByProcessId(
|
||||
this._config.get('process.ids.taskCalendar'),
|
||||
{
|
||||
path: ['/filiale', 'task-calendar'],
|
||||
queryParams: {},
|
||||
},
|
||||
'/filiale/task-calendar',
|
||||
);
|
||||
|
||||
assortmentNavigation$ = this.getLastNavigationByProcessId(
|
||||
this._config.get('process.ids.assortment'),
|
||||
{
|
||||
path: ['/filiale', 'assortment'],
|
||||
queryParams: {},
|
||||
},
|
||||
);
|
||||
|
||||
pickUpShelfInRoutePath$ = this.getLastNavigationByProcessId(
|
||||
this._config.get('process.ids.pickupShelf'),
|
||||
this._pickUpShelfInNavigation.defaultRoute(),
|
||||
'/filiale/pickup-shelf',
|
||||
);
|
||||
|
||||
// #4478 - RD // Abholfach - Routing löst Suche aus
|
||||
// pickUpShelfInListRoutePath$ = this.getLastNavigationByProcessId(
|
||||
// this._config.get('process.ids.pickupShelf'),
|
||||
// this._pickUpShelfInNavigation.listRoute()
|
||||
// );
|
||||
|
||||
remissionNavigation$ = this.getLastNavigationByProcessId(
|
||||
this._config.get('process.ids.remission'),
|
||||
{
|
||||
path: ['/filiale', 'remission'],
|
||||
queryParams: {},
|
||||
},
|
||||
);
|
||||
|
||||
packageInspectionNavigation$ = this.getLastNavigationByProcessId(
|
||||
this._config.get('process.ids.packageInspection'),
|
||||
{
|
||||
path: ['/filiale', 'package-inspection'],
|
||||
queryParams: {},
|
||||
},
|
||||
);
|
||||
|
||||
get currentShelfView$() {
|
||||
return this._route.queryParams.pipe(map((params) => params.view));
|
||||
}
|
||||
|
||||
shelfExpanded = false;
|
||||
customerExpanded = false;
|
||||
|
||||
constructor(
|
||||
private _shellService: ShellService,
|
||||
private _authService: AuthService,
|
||||
private _stockService: StockService,
|
||||
private _app: ApplicationService,
|
||||
private _router: Router,
|
||||
private _route: ActivatedRoute,
|
||||
private readonly _wrongDestinationModalService: WrongDestinationModalService,
|
||||
private _environment: EnvironmentService,
|
||||
private _catalogNavigationService: ProductCatalogNavigationService,
|
||||
private _customerOrdersNavigationService: CustomerOrdersNavigationService,
|
||||
private _config: Config,
|
||||
private _breadcrumbService: BreadcrumbService,
|
||||
private _customerSearchNavigation: CustomerSearchNavigation,
|
||||
private _customerCreateNavigation: CustomerCreateNavigation,
|
||||
private _pickUpShelfOutNavigation: PickUpShelfOutNavigationService,
|
||||
private _pickUpShelfInNavigation: PickupShelfInNavigationService,
|
||||
private _cdr: ChangeDetectorRef,
|
||||
@Inject(DOCUMENT) private readonly _document: Document,
|
||||
) {}
|
||||
|
||||
customerActive(isActive: boolean) {
|
||||
if (isActive) {
|
||||
this.expandCustomer();
|
||||
}
|
||||
}
|
||||
|
||||
shelfActive(isActive: boolean) {
|
||||
if (isActive) {
|
||||
this.expandShelf();
|
||||
}
|
||||
}
|
||||
|
||||
expandCustomer() {
|
||||
this.customerExpanded = true;
|
||||
this._cdr.markForCheck();
|
||||
}
|
||||
|
||||
expandShelf() {
|
||||
this.shelfExpanded = true;
|
||||
this._cdr.markForCheck();
|
||||
}
|
||||
|
||||
getLastNavigationByProcessId(
|
||||
id: number,
|
||||
fallback?: { path: string[]; queryParams: unknown },
|
||||
pathContainsString?: string,
|
||||
) {
|
||||
return this._breadcrumbService.getBreadcrumbByKey$(id)?.pipe(
|
||||
map((breadcrumbs) => {
|
||||
const lastCrumb = breadcrumbs
|
||||
.filter((breadcrumb) => {
|
||||
/**
|
||||
* #4532 - Der optionale Filter wurde hinzugefügt Breadcrumbs mit fehlerhaften Pfad auszuschließen.
|
||||
* Dieser Filter kann entfernt werden, sobald die Breadcrumbs korrekt gesetzt werden. Jedoch konnte man bisher nicht feststellen,
|
||||
* woher die fehlerhaften Breadcrumbs kommen.
|
||||
*/
|
||||
if (!pathContainsString) {
|
||||
// Wenn kein Filter gesetzt ist, dann wird der letzte Breadcrumb zurückgegeben
|
||||
return true;
|
||||
}
|
||||
|
||||
const pathStr = Array.isArray(breadcrumb.path)
|
||||
? breadcrumb.path.join('/')
|
||||
: breadcrumb.path;
|
||||
return pathStr.includes(pathContainsString);
|
||||
})
|
||||
// eslint-disable-next-line no-prototype-builtins
|
||||
.filter((breadcrumb) => !breadcrumb?.params?.hasOwnProperty('view'))
|
||||
.filter((breadcrumb) => !breadcrumb?.tags?.includes('reservation'))
|
||||
.filter((breadcrumb) => !breadcrumb?.tags?.includes('cleanup'))
|
||||
.filter(
|
||||
(breadcrumb) => !breadcrumb?.tags?.includes('wareneingangsliste'),
|
||||
)
|
||||
.filter((breadcrumb) => !breadcrumb?.tags?.includes('preview'))
|
||||
.reduce((last, current) => {
|
||||
if (!last) return current;
|
||||
|
||||
if (last.changed > current.changed) {
|
||||
return last;
|
||||
} else {
|
||||
return current;
|
||||
}
|
||||
}, undefined);
|
||||
|
||||
if (!lastCrumb) {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
// #4692 Return Fallback if Values contain undefined or null values, regardless if path is from type string or array
|
||||
if (typeof lastCrumb?.path === 'string') {
|
||||
if (
|
||||
lastCrumb?.path?.includes('undefined') ||
|
||||
lastCrumb?.path?.includes('null')
|
||||
) {
|
||||
return fallback;
|
||||
}
|
||||
} else {
|
||||
const valuesToCheck = [];
|
||||
|
||||
// eslint-disable-next-line no-unsafe-optional-chaining
|
||||
for (const value of lastCrumb?.path) {
|
||||
if (
|
||||
value?.outlets &&
|
||||
value?.outlets?.primary &&
|
||||
value?.outlets?.side
|
||||
) {
|
||||
valuesToCheck.push(
|
||||
...Object.values(value?.outlets?.primary),
|
||||
...Object.values(value?.outlets?.side),
|
||||
);
|
||||
} else {
|
||||
valuesToCheck.push(value);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.checkIfArrayContainsUndefinedOrNull(valuesToCheck)) {
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
|
||||
return { path: lastCrumb.path, queryParams: lastCrumb.params };
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
checkIfArrayContainsUndefinedOrNull(array: unknown[]) {
|
||||
return (
|
||||
array?.includes(undefined) ||
|
||||
array?.includes('undefined') ||
|
||||
array?.includes(null) ||
|
||||
array?.includes('null')
|
||||
);
|
||||
}
|
||||
|
||||
getLastActivatedCustomerProcessId$() {
|
||||
return this._app.getProcesses$('customer').pipe(
|
||||
map((processes) => {
|
||||
const lastCustomerProcess = processes
|
||||
.filter((process) => process.type === 'cart')
|
||||
.reduce((last, current) => {
|
||||
if (!last) return current;
|
||||
|
||||
if (last.activated > current.activated) {
|
||||
return last;
|
||||
} else {
|
||||
return current;
|
||||
}
|
||||
}, undefined);
|
||||
|
||||
return lastCustomerProcess?.id ?? Date.now();
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
closeSideMenu() {
|
||||
this._shellService.closeSideMenu();
|
||||
}
|
||||
|
||||
logout() {
|
||||
this._authService.logout();
|
||||
}
|
||||
|
||||
async resetBranch() {
|
||||
const process = await this.activeProcess$.pipe(first()).toPromise();
|
||||
if (process?.id) {
|
||||
this._app.patchProcessData(process.id, { selectedBranch: undefined });
|
||||
}
|
||||
}
|
||||
|
||||
focusSearchBox() {
|
||||
setTimeout(() => this._document.getElementById('searchbox')?.focus(), 0);
|
||||
}
|
||||
|
||||
async createProcess() {
|
||||
const process = await this.createCartProcess();
|
||||
this.navigateToCatalog(process);
|
||||
}
|
||||
|
||||
async createCartProcess() {
|
||||
const nextProcessName = await this.getNextProcessName();
|
||||
|
||||
const process: ApplicationProcess = {
|
||||
id: this.getNextProcessId(),
|
||||
type: 'cart',
|
||||
name: nextProcessName,
|
||||
section: 'customer',
|
||||
closeable: true,
|
||||
};
|
||||
|
||||
this._app.createProcess(process);
|
||||
|
||||
return process;
|
||||
}
|
||||
|
||||
async getNextProcessName() {
|
||||
let processes = await this._app
|
||||
.getProcesses$('customer')
|
||||
.pipe(first())
|
||||
.toPromise();
|
||||
|
||||
processes = processes.filter(
|
||||
(x) => x.type === 'cart' && x.name.startsWith('Vorgang '),
|
||||
);
|
||||
|
||||
const maxProcessNumber = processes.reduce((max, process) => {
|
||||
const number = parseInt(process.name.replace('Vorgang ', ''), 10);
|
||||
return number > max ? number : max;
|
||||
}, 0);
|
||||
|
||||
return `Vorgang ${maxProcessNumber + 1}`;
|
||||
}
|
||||
|
||||
getNextProcessId() {
|
||||
return Date.now();
|
||||
}
|
||||
|
||||
async navigateToCatalog(process: ApplicationProcess) {
|
||||
await this._catalogNavigationService
|
||||
.getArticleSearchBasePath(process.id)
|
||||
.navigate();
|
||||
}
|
||||
|
||||
navigateToDashboard() {
|
||||
this._router.navigate(['/kunde', 'dashboard']);
|
||||
}
|
||||
|
||||
async closeAllProcesses() {
|
||||
const processes = await this.processes$.pipe(take(1)).toPromise();
|
||||
|
||||
processes.forEach((process) => this._app.removeProcess(process.id));
|
||||
|
||||
this.navigateToDashboard();
|
||||
}
|
||||
|
||||
fetchAndOpenPackages = () =>
|
||||
this._wrongDestinationModalService.fetchAndOpen();
|
||||
}
|
||||
import {
|
||||
Component,
|
||||
ChangeDetectionStrategy,
|
||||
Inject,
|
||||
ChangeDetectorRef,
|
||||
inject,
|
||||
DOCUMENT,
|
||||
signal,
|
||||
} from '@angular/core';
|
||||
import { AuthModule, AuthService } from '@core/auth';
|
||||
import { StockService } from '@generated/swagger/wws-api';
|
||||
import { first, map, retry, switchMap, take } from 'rxjs/operators';
|
||||
import { ShellService } from '../shell.service';
|
||||
import { ApplicationProcess, ApplicationService } from '@core/application';
|
||||
import { ActivatedRoute, Router, RouterModule } from '@angular/router';
|
||||
import { EnvironmentService } from '@core/environment';
|
||||
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Config } from '@core/config';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { IconComponent } from '@shared/components/icon';
|
||||
import { RegexRouterLinkActiveDirective } from '@shared/directives/router-link-active';
|
||||
import { WrongDestinationModalService } from '@modal/wrong-destination';
|
||||
import {
|
||||
CustomerCreateNavigation,
|
||||
CustomerOrdersNavigationService,
|
||||
CustomerSearchNavigation,
|
||||
PickupShelfInNavigationService,
|
||||
PickUpShelfOutNavigationService,
|
||||
ProductCatalogNavigationService,
|
||||
} from '@shared/services/navigation';
|
||||
|
||||
import { TabService } from '@isa/core/tabs';
|
||||
import { NgIconComponent, provideIcons } from '@ng-icons/core';
|
||||
import { isaNavigationRemission2, isaNavigationReturn } from '@isa/icons';
|
||||
|
||||
@Component({
|
||||
selector: 'shell-side-menu',
|
||||
templateUrl: 'side-menu.component.html',
|
||||
styleUrls: ['side-menu.component.css'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [
|
||||
CommonModule,
|
||||
NgIconComponent,
|
||||
IconComponent,
|
||||
RouterModule,
|
||||
AuthModule,
|
||||
RegexRouterLinkActiveDirective,
|
||||
],
|
||||
providers: [provideIcons({ isaNavigationReturn, isaNavigationRemission2 })],
|
||||
})
|
||||
export class ShellSideMenuComponent {
|
||||
#shellService = inject(ShellService);
|
||||
#authService = inject(AuthService);
|
||||
#stockService = inject(StockService);
|
||||
#app = inject(ApplicationService);
|
||||
#router = inject(Router);
|
||||
#route = inject(ActivatedRoute);
|
||||
#wrongDestinationModalService = inject(WrongDestinationModalService);
|
||||
#environment = inject(EnvironmentService);
|
||||
#catalogNavigationService = inject(ProductCatalogNavigationService);
|
||||
#customerOrdersNavigationService = inject(CustomerOrdersNavigationService);
|
||||
#config = inject(Config);
|
||||
#breadcrumbService = inject(BreadcrumbService);
|
||||
#customerSearchNavigation = inject(CustomerSearchNavigation);
|
||||
#customerCreateNavigation = inject(CustomerCreateNavigation);
|
||||
#pickUpShelfOutNavigation = inject(PickUpShelfOutNavigationService);
|
||||
#pickUpShelfInNavigation = inject(PickupShelfInNavigationService);
|
||||
#cdr = inject(ChangeDetectorRef);
|
||||
#document = inject(DOCUMENT);
|
||||
processService = inject(TabService);
|
||||
|
||||
branchKey$ = this.#stockService.StockCurrentBranch().pipe(
|
||||
retry(3),
|
||||
map((x) => x.result.key),
|
||||
);
|
||||
|
||||
section$ = this.#app.getSection$();
|
||||
|
||||
processes$ = this.section$.pipe(
|
||||
switchMap((section) => this.#app.getProcesses$(section)),
|
||||
);
|
||||
|
||||
processesCount$ = this.processes$.pipe(
|
||||
map((processes) => processes?.length ?? 0),
|
||||
);
|
||||
|
||||
activeProcess$ = this.#app.activatedProcessId$.pipe(
|
||||
switchMap((processId) => this.#app.getProcessById$(processId)),
|
||||
);
|
||||
|
||||
get isTablet() {
|
||||
return this.#environment.matchTablet();
|
||||
}
|
||||
|
||||
customerBasePath$ = this.activeProcess$.pipe(
|
||||
map((process) => {
|
||||
if (
|
||||
!!process &&
|
||||
process.section === 'customer' &&
|
||||
process.type !== 'cart-checkout'
|
||||
) {
|
||||
// Übernehme aktiven Prozess
|
||||
return `/kunde/${process.id}`;
|
||||
} else {
|
||||
// Über Guards wird ein neuer Prozess erstellt
|
||||
return '/kunde';
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
customerSearchRoute$ = this.getLastActivatedCustomerProcessId$().pipe(
|
||||
map((processId) => {
|
||||
return this.#customerSearchNavigation.defaultRoute({ processId });
|
||||
}),
|
||||
);
|
||||
|
||||
customerCreateRoute$ = this.getLastActivatedCustomerProcessId$().pipe(
|
||||
map((processId) => {
|
||||
return this.#customerCreateNavigation.defaultRoute({ processId });
|
||||
}),
|
||||
);
|
||||
|
||||
pickUpShelfOutRoutePath$ = this.getLastActivatedCustomerProcessId$().pipe(
|
||||
map((processId) => {
|
||||
if (processId) {
|
||||
// Übernehme aktiven Prozess
|
||||
return this.#pickUpShelfOutNavigation.defaultRoute({ processId }).path;
|
||||
} else {
|
||||
// Über Guards wird ein neuer Prozess erstellt
|
||||
return this.#pickUpShelfOutNavigation.defaultRoute({}).path;
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
productRoutePath$ = this.getLastActivatedCustomerProcessId$().pipe(
|
||||
map((processId) => {
|
||||
if (processId) {
|
||||
// Übernehme aktiven Prozess
|
||||
return this.#catalogNavigationService.getArticleSearchBasePath(
|
||||
processId,
|
||||
).path;
|
||||
} else {
|
||||
// Über Guards wird ein neuer Prozess erstellt
|
||||
return this.#catalogNavigationService.getArticleSearchBasePath().path;
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
customerOrdersRoutePath$ = this.getLastActivatedCustomerProcessId$().pipe(
|
||||
map((processId) => {
|
||||
if (processId) {
|
||||
// Übernehme aktiven Prozess
|
||||
return this.#customerOrdersNavigationService.getCustomerOrdersBasePath(
|
||||
processId,
|
||||
).path;
|
||||
} else {
|
||||
// Über Guards wird ein neuer Prozess erstellt
|
||||
return this.#customerOrdersNavigationService.getCustomerOrdersBasePath()
|
||||
.path;
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
taskCalenderNavigation$ = this.getLastNavigationByProcessId(
|
||||
this.#config.get('process.ids.taskCalendar'),
|
||||
{
|
||||
path: ['/filiale', 'task-calendar'],
|
||||
queryParams: {},
|
||||
},
|
||||
'/filiale/task-calendar',
|
||||
);
|
||||
|
||||
assortmentNavigation$ = this.getLastNavigationByProcessId(
|
||||
this.#config.get('process.ids.assortment'),
|
||||
{
|
||||
path: ['/filiale', 'assortment'],
|
||||
queryParams: {},
|
||||
},
|
||||
);
|
||||
|
||||
pickUpShelfInRoutePath$ = this.getLastNavigationByProcessId(
|
||||
this.#config.get('process.ids.pickupShelf'),
|
||||
this.#pickUpShelfInNavigation.defaultRoute(),
|
||||
'/filiale/pickup-shelf',
|
||||
);
|
||||
|
||||
// #4478 - RD // Abholfach - Routing löst Suche aus
|
||||
// pickUpShelfInListRoutePath$ = this.getLastNavigationByProcessId(
|
||||
// this._config.get('process.ids.pickupShelf'),
|
||||
// this._pickUpShelfInNavigation.listRoute()
|
||||
// );
|
||||
|
||||
packageInspectionNavigation$ = this.getLastNavigationByProcessId(
|
||||
this.#config.get('process.ids.packageInspection'),
|
||||
{
|
||||
path: ['/filiale', 'package-inspection'],
|
||||
queryParams: {},
|
||||
},
|
||||
);
|
||||
|
||||
get currentShelfView$() {
|
||||
return this.#route.queryParams.pipe(map((params) => params.view));
|
||||
}
|
||||
|
||||
shelfExpanded = false;
|
||||
customerExpanded = false;
|
||||
remissionExpanded = signal(false);
|
||||
|
||||
customerActive(isActive: boolean) {
|
||||
if (isActive) {
|
||||
this.expandCustomer();
|
||||
}
|
||||
}
|
||||
|
||||
shelfActive(isActive: boolean) {
|
||||
if (isActive) {
|
||||
this.expandShelf();
|
||||
}
|
||||
}
|
||||
|
||||
expandCustomer() {
|
||||
this.customerExpanded = true;
|
||||
this.#cdr.markForCheck();
|
||||
}
|
||||
|
||||
expandShelf() {
|
||||
this.shelfExpanded = true;
|
||||
this.#cdr.markForCheck();
|
||||
}
|
||||
|
||||
getLastNavigationByProcessId(
|
||||
id: number,
|
||||
fallback?: { path: string[]; queryParams: unknown },
|
||||
pathContainsString?: string,
|
||||
) {
|
||||
return this.#breadcrumbService.getBreadcrumbByKey$(id)?.pipe(
|
||||
map((breadcrumbs) => {
|
||||
const lastCrumb = breadcrumbs
|
||||
.filter((breadcrumb) => {
|
||||
/**
|
||||
* #4532 - Der optionale Filter wurde hinzugefügt Breadcrumbs mit fehlerhaften Pfad auszuschließen.
|
||||
* Dieser Filter kann entfernt werden, sobald die Breadcrumbs korrekt gesetzt werden. Jedoch konnte man bisher nicht feststellen,
|
||||
* woher die fehlerhaften Breadcrumbs kommen.
|
||||
*/
|
||||
if (!pathContainsString) {
|
||||
// Wenn kein Filter gesetzt ist, dann wird der letzte Breadcrumb zurückgegeben
|
||||
return true;
|
||||
}
|
||||
|
||||
const pathStr = Array.isArray(breadcrumb.path)
|
||||
? breadcrumb.path.join('/')
|
||||
: breadcrumb.path;
|
||||
return pathStr.includes(pathContainsString);
|
||||
})
|
||||
// eslint-disable-next-line no-prototype-builtins
|
||||
.filter((breadcrumb) => !breadcrumb?.params?.hasOwnProperty('view'))
|
||||
.filter((breadcrumb) => !breadcrumb?.tags?.includes('reservation'))
|
||||
.filter((breadcrumb) => !breadcrumb?.tags?.includes('cleanup'))
|
||||
.filter(
|
||||
(breadcrumb) => !breadcrumb?.tags?.includes('wareneingangsliste'),
|
||||
)
|
||||
.filter((breadcrumb) => !breadcrumb?.tags?.includes('preview'))
|
||||
.reduce((last, current) => {
|
||||
if (!last) return current;
|
||||
|
||||
if (last.changed > current.changed) {
|
||||
return last;
|
||||
} else {
|
||||
return current;
|
||||
}
|
||||
}, undefined);
|
||||
|
||||
if (!lastCrumb) {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
// #4692 Return Fallback if Values contain undefined or null values, regardless if path is from type string or array
|
||||
if (typeof lastCrumb?.path === 'string') {
|
||||
if (
|
||||
lastCrumb?.path?.includes('undefined') ||
|
||||
lastCrumb?.path?.includes('null')
|
||||
) {
|
||||
return fallback;
|
||||
}
|
||||
} else {
|
||||
const valuesToCheck = [];
|
||||
|
||||
// eslint-disable-next-line no-unsafe-optional-chaining
|
||||
for (const value of lastCrumb?.path) {
|
||||
if (
|
||||
value?.outlets &&
|
||||
value?.outlets?.primary &&
|
||||
value?.outlets?.side
|
||||
) {
|
||||
valuesToCheck.push(
|
||||
...Object.values(value?.outlets?.primary),
|
||||
...Object.values(value?.outlets?.side),
|
||||
);
|
||||
} else {
|
||||
valuesToCheck.push(value);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.checkIfArrayContainsUndefinedOrNull(valuesToCheck)) {
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
|
||||
return { path: lastCrumb.path, queryParams: lastCrumb.params };
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
checkIfArrayContainsUndefinedOrNull(array: unknown[]) {
|
||||
return (
|
||||
array?.includes(undefined) ||
|
||||
array?.includes('undefined') ||
|
||||
array?.includes(null) ||
|
||||
array?.includes('null')
|
||||
);
|
||||
}
|
||||
|
||||
getLastActivatedCustomerProcessId$() {
|
||||
return this.#app.getProcesses$('customer').pipe(
|
||||
map((processes) => {
|
||||
const lastCustomerProcess = processes
|
||||
.filter((process) => process.type === 'cart')
|
||||
.reduce((last, current) => {
|
||||
if (!last) return current;
|
||||
|
||||
if (last.activated > current.activated) {
|
||||
return last;
|
||||
} else {
|
||||
return current;
|
||||
}
|
||||
}, undefined);
|
||||
|
||||
return lastCustomerProcess?.id ?? Date.now();
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
closeSideMenu() {
|
||||
this.#shellService.closeSideMenu();
|
||||
}
|
||||
|
||||
logout() {
|
||||
this.#authService.logout();
|
||||
}
|
||||
|
||||
async resetBranch() {
|
||||
const process = await this.activeProcess$.pipe(first()).toPromise();
|
||||
if (process?.id) {
|
||||
this.#app.patchProcessData(process.id, { selectedBranch: undefined });
|
||||
}
|
||||
}
|
||||
|
||||
focusSearchBox() {
|
||||
setTimeout(() => this.#document.getElementById('searchbox')?.focus(), 0);
|
||||
}
|
||||
|
||||
async createProcess() {
|
||||
const process = await this.createCartProcess();
|
||||
this.navigateToCatalog(process);
|
||||
}
|
||||
|
||||
async createCartProcess() {
|
||||
const nextProcessName = await this.getNextProcessName();
|
||||
|
||||
const process: ApplicationProcess = {
|
||||
id: this.getNextProcessId(),
|
||||
type: 'cart',
|
||||
name: nextProcessName,
|
||||
section: 'customer',
|
||||
closeable: true,
|
||||
};
|
||||
|
||||
this.#app.createProcess(process);
|
||||
|
||||
return process;
|
||||
}
|
||||
|
||||
async getNextProcessName() {
|
||||
let processes = await this.#app
|
||||
.getProcesses$('customer')
|
||||
.pipe(first())
|
||||
.toPromise();
|
||||
|
||||
processes = processes.filter(
|
||||
(x) => x.type === 'cart' && x.name.startsWith('Vorgang '),
|
||||
);
|
||||
|
||||
const maxProcessNumber = processes.reduce((max, process) => {
|
||||
const number = parseInt(process.name.replace('Vorgang ', ''), 10);
|
||||
return number > max ? number : max;
|
||||
}, 0);
|
||||
|
||||
return `Vorgang ${maxProcessNumber + 1}`;
|
||||
}
|
||||
|
||||
getNextProcessId() {
|
||||
return Date.now();
|
||||
}
|
||||
|
||||
async navigateToCatalog(process: ApplicationProcess) {
|
||||
await this.#catalogNavigationService
|
||||
.getArticleSearchBasePath(process.id)
|
||||
.navigate();
|
||||
}
|
||||
|
||||
navigateToDashboard() {
|
||||
this.#router.navigate(['/kunde', 'dashboard']);
|
||||
}
|
||||
|
||||
async closeAllProcesses() {
|
||||
const processes = await this.processes$.pipe(take(1)).toPromise();
|
||||
|
||||
processes.forEach((process) => this.#app.removeProcess(process.id));
|
||||
|
||||
this.navigateToDashboard();
|
||||
}
|
||||
|
||||
fetchAndOpenPackages = () =>
|
||||
this.#wrongDestinationModalService.fetchAndOpen();
|
||||
}
|
||||
|
||||
@@ -1,97 +1,22 @@
|
||||
/* You can add global styles to this file, and also import other style files */
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@import "./scss/components";
|
||||
|
||||
/* Scanner Fullscreen Styles */
|
||||
.full-screen-scanner {
|
||||
max-width: 100vw !important;
|
||||
max-height: 100vh !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
|
||||
.scanner-component {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* Override CDK overlay container styles for scanner */
|
||||
.cdk-overlay-container {
|
||||
.full-screen-scanner {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@import "./scss/root";
|
||||
@import "./scss/customer";
|
||||
@import "./scss/branch";
|
||||
|
||||
@layer base {
|
||||
body {
|
||||
@apply bg-background;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 0; // remove scrollbar space
|
||||
height: 0;
|
||||
background: transparent; // optional: just make scrollbar invisible */
|
||||
}
|
||||
|
||||
.desktop .scroll-bar::-webkit-scrollbar {
|
||||
@apply w-3;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.desktop .scroll-bar::-webkit-scrollbar-track {
|
||||
// @apply my-4;
|
||||
-webkit-box-shadow: inset 0 0 4px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 10px;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.desktop .scroll-bar::-webkit-scrollbar-thumb {
|
||||
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 10px;
|
||||
background-color: var(--scrollbar-color);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes load {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
30% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.skeleton {
|
||||
@apply block bg-gray-300 h-6;
|
||||
animation: load 1s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@layer components {
|
||||
.input-control {
|
||||
@apply rounded border border-solid border-[#AEB7C1] px-4 py-[1.125rem] outline-none;
|
||||
}
|
||||
|
||||
// .input-control:focus,
|
||||
// .input-control:not(:placeholder-shown) {
|
||||
// @apply bg-white;
|
||||
// }
|
||||
|
||||
.input-control.ng-touched.ng-invalid {
|
||||
@apply border-brand;
|
||||
}
|
||||
}
|
||||
.full-screen-scanner {
|
||||
max-width: 100vw !important;
|
||||
max-height: 100vh !important;
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
|
||||
.scanner-component {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.cdk-overlay-container {
|
||||
.full-screen-scanner {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
76
apps/isa-app/src/tailwind.scss
Normal file
76
apps/isa-app/src/tailwind.scss
Normal file
@@ -0,0 +1,76 @@
|
||||
@use "./scss/components";
|
||||
@use "./scss/root";
|
||||
@use "./scss/customer";
|
||||
@use "./scss/branch";
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer components {
|
||||
@import "../../../libs/ui/buttons/src/buttons.scss";
|
||||
@import "../../../libs/ui/bullet-list/src/bullet-list.scss";
|
||||
@import "../../../libs/ui/datepicker/src/datepicker.scss";
|
||||
@import "../../../libs/ui/dialog/src/dialog.scss";
|
||||
@import "../../../libs/ui/input-controls/src/input-controls.scss";
|
||||
@import "../../../libs/ui/menu/src/menu.scss";
|
||||
@import "../../../libs/ui/progress-bar/src/lib/progress-bar.scss";
|
||||
@import "../../../libs/ui/search-bar/src/search-bar.scss";
|
||||
@import "../../../libs/ui/skeleton-loader/src/skeleton-loader.scss";
|
||||
@import "../../../libs/ui/tooltip/src/tooltip.scss";
|
||||
@import "../../../libs/ui/label/src/label.scss";
|
||||
|
||||
.input-control {
|
||||
@apply rounded border border-solid border-[#AEB7C1] px-4 py-[1.125rem] outline-none;
|
||||
}
|
||||
|
||||
.input-control.ng-touched.ng-invalid {
|
||||
@apply border-brand;
|
||||
}
|
||||
|
||||
@keyframes load {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
30% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.skeleton {
|
||||
@apply block bg-gray-300 h-6;
|
||||
animation: load 1s ease-in-out infinite;
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
body {
|
||||
@apply bg-background;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 0;
|
||||
height: 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.desktop .scroll-bar::-webkit-scrollbar {
|
||||
@apply w-3;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.desktop .scroll-bar::-webkit-scrollbar-track {
|
||||
-webkit-box-shadow: inset 0 0 4px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 10px;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.desktop .scroll-bar::-webkit-scrollbar-thumb {
|
||||
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 10px;
|
||||
background-color: var(--scrollbar-color);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
@use "../../../libs/ui/buttons/src/buttons.scss";
|
||||
@use "../../../libs/ui/datepicker/src/datepicker.scss";
|
||||
@use "../../../libs/ui/dialog/src/dialog.scss";
|
||||
@use "../../../libs/ui/input-controls/src/input-controls.scss";
|
||||
@use "../../../libs/ui/menu/src/menu.scss";
|
||||
@use "../../../libs/ui/progress-bar/src/lib/progress-bar.scss";
|
||||
@use "../../../libs/ui/search-bar/src/search-bar.scss";
|
||||
@use "../../../libs/ui/skeleton-loader/src/skeleton-loader.scss";
|
||||
@use "../../../libs/ui/tooltip/src/tooltip.scss";
|
||||
@@ -34,6 +34,7 @@
|
||||
bottom: 30px !important;
|
||||
right: 12px;
|
||||
box-shadow: 0px 0px 20px 0px rgba(89, 100, 112, 0.5);
|
||||
transition: all 100ms linear;
|
||||
|
||||
@screen desktop {
|
||||
margin-left: 600px;
|
||||
@@ -44,7 +45,6 @@
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
transition: all 100ms linear;
|
||||
&.up {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
@@ -16,20 +16,20 @@ import {
|
||||
forwardRef,
|
||||
Optional,
|
||||
inject,
|
||||
} from '@angular/core';
|
||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { UiAutocompleteComponent } from '@ui/autocomplete';
|
||||
import { UiFormControlDirective } from '@ui/form-control';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { ScanAdapterService } from '@adapter/scan';
|
||||
import { injectCancelSearch } from '@shared/services/cancel-subject';
|
||||
import { containsElement } from '@utils/common';
|
||||
import { EnvironmentService } from '@core/environment';
|
||||
} from "@angular/core";
|
||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms";
|
||||
import { UiAutocompleteComponent } from "@ui/autocomplete";
|
||||
import { UiFormControlDirective } from "@ui/form-control";
|
||||
import { Subscription } from "rxjs";
|
||||
import { ScanAdapterService } from "@adapter/scan";
|
||||
import { injectCancelSearch } from "@shared/services/cancel-subject";
|
||||
import { containsElement } from "@utils/common";
|
||||
import { EnvironmentService } from "@core/environment";
|
||||
|
||||
@Component({
|
||||
selector: 'ui-searchbox',
|
||||
templateUrl: 'searchbox.component.html',
|
||||
styleUrls: ['searchbox.component.scss'],
|
||||
selector: "ui-searchbox",
|
||||
templateUrl: "searchbox.component.html",
|
||||
styleUrls: ["searchbox.component.scss"],
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
@@ -49,9 +49,9 @@ export class UiSearchboxNextComponent
|
||||
private readonly _cancelSearch = injectCancelSearch({ optional: true });
|
||||
|
||||
disabled: boolean;
|
||||
type = 'text';
|
||||
type = "text";
|
||||
|
||||
@ViewChild('input', { read: ElementRef, static: true })
|
||||
@ViewChild("input", { read: ElementRef, static: true })
|
||||
input: ElementRef;
|
||||
|
||||
@ContentChild(UiAutocompleteComponent)
|
||||
@@ -61,9 +61,9 @@ export class UiSearchboxNextComponent
|
||||
focusAfterViewInit: boolean = true;
|
||||
|
||||
@Input()
|
||||
placeholder: string = '';
|
||||
placeholder: string = "";
|
||||
|
||||
private _query = '';
|
||||
private _query = "";
|
||||
|
||||
@Input()
|
||||
get query() {
|
||||
@@ -94,7 +94,7 @@ export class UiSearchboxNextComponent
|
||||
scanner = false;
|
||||
|
||||
@Input()
|
||||
hint: string = '';
|
||||
hint: string = "";
|
||||
|
||||
@Output()
|
||||
hintCleared = new EventEmitter<void>();
|
||||
@@ -107,11 +107,11 @@ export class UiSearchboxNextComponent
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.setQuery('');
|
||||
this.setQuery("");
|
||||
this._cancelSearch();
|
||||
}
|
||||
|
||||
@HostBinding('class.autocomplete-opend')
|
||||
@HostBinding("class.autocomplete-opend")
|
||||
get autocompleteOpen() {
|
||||
return this.autocomplete?.opend;
|
||||
}
|
||||
@@ -212,14 +212,14 @@ export class UiSearchboxNextComponent
|
||||
}
|
||||
|
||||
clearHint() {
|
||||
this.hint = '';
|
||||
this.hint = "";
|
||||
this.focused.emit(true);
|
||||
this.hintCleared.emit();
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
|
||||
onKeyup(event: KeyboardEvent) {
|
||||
if (event.key === 'Enter') {
|
||||
if (event.key === "Enter") {
|
||||
if (this.autocomplete?.opend && this.autocomplete?.activeItem) {
|
||||
this.setQuery(this.autocomplete?.activeItem?.item);
|
||||
this.autocomplete?.close();
|
||||
@@ -227,7 +227,7 @@ export class UiSearchboxNextComponent
|
||||
this.search.emit(this.query);
|
||||
|
||||
event.preventDefault();
|
||||
} else if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {
|
||||
} else if (event.key === "ArrowUp" || event.key === "ArrowDown") {
|
||||
this.handleArrowUpDownEvent(event);
|
||||
}
|
||||
}
|
||||
@@ -235,12 +235,14 @@ export class UiSearchboxNextComponent
|
||||
handleArrowUpDownEvent(event: KeyboardEvent) {
|
||||
this.autocomplete?.handleKeyboardEvent(event);
|
||||
if (this.autocomplete?.activeItem) {
|
||||
const query = this.autocompleteValueSelector(this.autocomplete.activeItem.item);
|
||||
const query = this.autocompleteValueSelector(
|
||||
this.autocomplete.activeItem.item,
|
||||
);
|
||||
this.setQuery(query, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
@HostListener('window:click', ['$event'])
|
||||
@HostListener("window:click", ["$event"])
|
||||
focusLost(event: MouseEvent) {
|
||||
if (
|
||||
this.autocomplete?.opend &&
|
||||
@@ -254,9 +256,11 @@ export class UiSearchboxNextComponent
|
||||
this.search.emit(this.query);
|
||||
}
|
||||
|
||||
@HostListener('focusout', ['$event'])
|
||||
@HostListener("focusout", ["$event"])
|
||||
onBlur() {
|
||||
this.onTouched();
|
||||
if (typeof this.onTouched === "function") {
|
||||
this.onTouched();
|
||||
}
|
||||
this.focused.emit(false);
|
||||
this.cdr.markForCheck();
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
}
|
||||
|
||||
.ui-tooltip-panel {
|
||||
@apply pointer-events-auto;
|
||||
|
||||
.triangle {
|
||||
width: 30px;
|
||||
polygon {
|
||||
|
||||
@@ -17,6 +17,7 @@ import { provideRouter } from '@angular/router';
|
||||
type ProductInfoInputs = {
|
||||
item: ProductInfoItem;
|
||||
orientation: ProductInfoOrientation;
|
||||
innerGridClass: string;
|
||||
};
|
||||
|
||||
const meta: Meta<ProductInfoInputs> = {
|
||||
@@ -50,8 +51,10 @@ const meta: Meta<ProductInfoInputs> = {
|
||||
value: 19.99,
|
||||
},
|
||||
},
|
||||
tag: 'Prio 2',
|
||||
},
|
||||
orientation: 'horizontal',
|
||||
innerGridClass: 'grid-cols-[minmax(20rem,1fr),auto]',
|
||||
},
|
||||
argTypes: {
|
||||
item: {
|
||||
@@ -68,6 +71,16 @@ const meta: Meta<ProductInfoInputs> = {
|
||||
},
|
||||
},
|
||||
},
|
||||
innerGridClass: {
|
||||
control: 'text',
|
||||
description:
|
||||
'Custom CSS classes for the inner grid layout. (Applies on vertical layout only)',
|
||||
table: {
|
||||
defaultValue: {
|
||||
summary: 'grid-cols-[minmax(20rem,1fr),auto]',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
render: (args) => ({
|
||||
props: args,
|
||||
@@ -95,6 +108,7 @@ export const Default: Story = {
|
||||
value: 29.99,
|
||||
},
|
||||
},
|
||||
tag: 'Prio 2',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
import { type Meta, type StoryObj, argsToTemplate } from '@storybook/angular';
|
||||
import { ProductShelfMetaInfoComponent } from '@isa/remission/shared/product';
|
||||
|
||||
const meta: Meta<ProductShelfMetaInfoComponent> = {
|
||||
component: ProductShelfMetaInfoComponent,
|
||||
title: 'remission/shared/product/ProductShelfMetaInfoComponent',
|
||||
args: {
|
||||
department: 'Reise',
|
||||
shelfLabel: 'Europa',
|
||||
productGroupKey: '311',
|
||||
productGroupValue: 'Romane TB',
|
||||
assortment: 'Basissortiment|BPrämienartikel|n',
|
||||
returnReason: 'Beschädigt',
|
||||
},
|
||||
argTypes: {
|
||||
department: {
|
||||
control: { type: 'text' },
|
||||
description: 'The department of the product.',
|
||||
defaultValue: undefined,
|
||||
},
|
||||
shelfLabel: {
|
||||
control: { type: 'text' },
|
||||
description: 'The shelf label of the product.',
|
||||
defaultValue: undefined,
|
||||
},
|
||||
productGroupKey: {
|
||||
control: { type: 'text' },
|
||||
description: 'The key of the product group.',
|
||||
defaultValue: undefined,
|
||||
},
|
||||
productGroupValue: {
|
||||
control: { type: 'text' },
|
||||
description: 'The value of the product group.',
|
||||
defaultValue: undefined,
|
||||
},
|
||||
assortment: {
|
||||
control: { type: 'text' },
|
||||
description: 'The assortment of the product.',
|
||||
defaultValue: undefined,
|
||||
},
|
||||
returnReason: {
|
||||
control: { type: 'text' },
|
||||
description: 'The reason for the return of the product.',
|
||||
defaultValue: undefined,
|
||||
},
|
||||
},
|
||||
render: (args) => ({
|
||||
props: args,
|
||||
template: `<remi-product-shelf-meta-info ${argsToTemplate(args)}></remi-product-shelf-meta-info>`,
|
||||
}),
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<ProductShelfMetaInfoComponent>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
department: 'Reise',
|
||||
shelfLabel: 'Europa',
|
||||
productGroupKey: '311',
|
||||
productGroupValue: 'Romane TB',
|
||||
assortment: 'Basissortiment|BPrämienartikel|n',
|
||||
returnReason: 'Beschädigt',
|
||||
},
|
||||
};
|
||||
@@ -1,50 +1,64 @@
|
||||
import {
|
||||
type Meta,
|
||||
type StoryObj,
|
||||
argsToTemplate,
|
||||
moduleMetadata,
|
||||
} from '@storybook/angular';
|
||||
import { ProductStockInfoComponent } from '@isa/remission/shared/product';
|
||||
import { provideProductImageUrl } from '@isa/shared/product-image';
|
||||
import { provideProductRouterLinkBuilder } from '@isa/shared/product-router-link';
|
||||
|
||||
const meta: Meta<ProductStockInfoComponent> = {
|
||||
component: ProductStockInfoComponent,
|
||||
title: 'remission/shared/product/ProductStockInfoComponent',
|
||||
decorators: [
|
||||
moduleMetadata({
|
||||
providers: [
|
||||
provideProductImageUrl('https://produktbilder-test.paragon-data.net'),
|
||||
provideProductRouterLinkBuilder((ean: string) => ean),
|
||||
],
|
||||
}),
|
||||
],
|
||||
args: {
|
||||
stock: 100,
|
||||
stockToRemit: 20,
|
||||
},
|
||||
argTypes: {
|
||||
stock: {
|
||||
control: { type: 'number' },
|
||||
description: 'The current stock of the product.',
|
||||
defaultValue: undefined,
|
||||
},
|
||||
stockToRemit: {
|
||||
control: { type: 'number' },
|
||||
description: 'The amount of stock to remit.',
|
||||
defaultValue: undefined,
|
||||
},
|
||||
},
|
||||
render: (args) => ({
|
||||
props: args,
|
||||
template: `<remi-product-stock-info ${argsToTemplate({ info: args })}></remi-product-stock-info>`,
|
||||
}),
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<ProductStockInfoComponent>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {},
|
||||
};
|
||||
import {
|
||||
type Meta,
|
||||
type StoryObj,
|
||||
argsToTemplate,
|
||||
moduleMetadata,
|
||||
} from '@storybook/angular';
|
||||
import { ProductStockInfoComponent } from '@isa/remission/shared/product';
|
||||
import { provideProductImageUrl } from '@isa/shared/product-image';
|
||||
import { provideProductRouterLinkBuilder } from '@isa/shared/product-router-link';
|
||||
|
||||
const meta: Meta<ProductStockInfoComponent> = {
|
||||
component: ProductStockInfoComponent,
|
||||
title: 'remission/shared/product/ProductStockInfoComponent',
|
||||
decorators: [
|
||||
moduleMetadata({
|
||||
providers: [
|
||||
provideProductImageUrl('https://produktbilder-test.paragon-data.net'),
|
||||
provideProductRouterLinkBuilder((ean: string) => ean),
|
||||
],
|
||||
}),
|
||||
],
|
||||
args: {
|
||||
availableStock: 92,
|
||||
stockToRemit: 91,
|
||||
targetStock: 1,
|
||||
zob: 0,
|
||||
},
|
||||
argTypes: {
|
||||
availableStock: {
|
||||
control: { type: 'number' },
|
||||
description: 'Total available stock for the product.',
|
||||
},
|
||||
stockToRemit: {
|
||||
control: { type: 'number' },
|
||||
description: 'Stock quantity to remit.',
|
||||
},
|
||||
targetStock: {
|
||||
control: { type: 'number' },
|
||||
description: 'Target stock level after remittance.',
|
||||
},
|
||||
zob: {
|
||||
control: { type: 'number' },
|
||||
description: 'Min Stock Category Management Information.',
|
||||
defaultValue: 0,
|
||||
},
|
||||
},
|
||||
render: (args) => ({
|
||||
props: args,
|
||||
template: `<remi-product-stock-info ${argsToTemplate(args)}></remi-product-stock-info>`,
|
||||
}),
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<ProductStockInfoComponent>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
availableStock: 92,
|
||||
stockToRemit: 91,
|
||||
targetStock: 1,
|
||||
zob: 0,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
import { argsToTemplate, type Meta, type StoryObj, moduleMetadata } from '@storybook/angular';
|
||||
import { EmptyStateComponent } from '@isa/ui/empty-state';
|
||||
import {
|
||||
argsToTemplate,
|
||||
type Meta,
|
||||
type StoryObj,
|
||||
moduleMetadata,
|
||||
} from '@storybook/angular';
|
||||
import { EmptyStateAppearance, EmptyStateComponent } from '@isa/ui/empty-state';
|
||||
import { ButtonComponent } from '@isa/ui/buttons';
|
||||
|
||||
type EmptyStateComponentInputs = {
|
||||
title: string;
|
||||
description: string;
|
||||
appearance: EmptyStateAppearance;
|
||||
};
|
||||
|
||||
const meta: Meta<EmptyStateComponentInputs> = {
|
||||
@@ -22,6 +28,10 @@ const meta: Meta<EmptyStateComponentInputs> = {
|
||||
description: {
|
||||
control: 'text',
|
||||
},
|
||||
appearance: {
|
||||
control: 'select',
|
||||
options: Object.values(EmptyStateAppearance),
|
||||
},
|
||||
},
|
||||
render: (args) => ({
|
||||
props: args,
|
||||
@@ -40,5 +50,6 @@ export const Default: Story = {
|
||||
args: {
|
||||
title: 'Keine Suchergebnisse',
|
||||
description: 'Suchen Sie nach einer Rechnungsnummer oder Kundennamen.',
|
||||
appearance: EmptyStateAppearance.NoResults,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -31,7 +31,7 @@ const meta: Meta<ClientRowComponent> = {
|
||||
</ui-item-row-data-value>
|
||||
</ui-item-row-data-row>
|
||||
<ui-item-row-data-row>
|
||||
<ui-item-row-data-label>Rechnugsnr.</ui-item-row-data-label>
|
||||
<ui-item-row-data-label>Beleg-Nr.</ui-item-row-data-label>
|
||||
<ui-item-row-data-value>
|
||||
<span class="isa-text-body-2-bold">
|
||||
1234567890
|
||||
|
||||
39
apps/isa-app/stories/ui/label/ui-label.stories.ts
Normal file
39
apps/isa-app/stories/ui/label/ui-label.stories.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { argsToTemplate, type Meta, type StoryObj } from '@storybook/angular';
|
||||
import { Labeltype, LabelPriority, LabelComponent } from '@isa/ui/label';
|
||||
|
||||
type UiLabelInputs = {
|
||||
type: Labeltype;
|
||||
priority: LabelPriority;
|
||||
};
|
||||
|
||||
const meta: Meta<UiLabelInputs> = {
|
||||
component: LabelComponent,
|
||||
title: 'ui/label/Label',
|
||||
argTypes: {
|
||||
type: {
|
||||
control: { type: 'select' },
|
||||
options: Object.values(Labeltype),
|
||||
description: 'Determines the label type',
|
||||
},
|
||||
priority: {
|
||||
control: { type: 'select' },
|
||||
options: Object.values(LabelPriority),
|
||||
description: 'Determines the label priority',
|
||||
},
|
||||
},
|
||||
args: {
|
||||
type: 'tag',
|
||||
priority: 'high',
|
||||
},
|
||||
render: (args) => ({
|
||||
props: args,
|
||||
template: `<ui-label ${argsToTemplate(args)}>Prio 1</ui-label>`,
|
||||
}),
|
||||
};
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<LabelComponent>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {},
|
||||
};
|
||||
@@ -2,7 +2,8 @@
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"types": []
|
||||
"types": [],
|
||||
"moduleResolution": "bundler"
|
||||
},
|
||||
"files": ["src/main.ts"],
|
||||
"include": ["src/**/*.d.ts"],
|
||||
|
||||
@@ -9,10 +9,10 @@ trigger:
|
||||
variables:
|
||||
# Major Version einstellen
|
||||
- name: 'Major'
|
||||
value: '3'
|
||||
value: '4'
|
||||
# Minor Version einstellen
|
||||
- name: 'Minor'
|
||||
value: '4'
|
||||
value: '1'
|
||||
- name: 'Patch'
|
||||
value: "$[counter(format('{0}.{1}', variables['Major'], variables['Minor']),0)]"
|
||||
- name: 'BuildUniqueID'
|
||||
@@ -89,7 +89,7 @@ jobs:
|
||||
condition: and(ne(variables['Build.SourceBranch'], 'refs/heads/integration'), ne(variables['Build.SourceBranch'], 'refs/heads/master'), not(startsWith(variables['Build.SourceBranch'], 'refs/heads/hotfix/')), not(startsWith(variables['Build.SourceBranch'], 'refs/heads/release/')))
|
||||
variables:
|
||||
- name: DockerTagSourceBranch
|
||||
value: $[replace(variables['Build.SourceBranch'], '/', '_')]
|
||||
value: $[replace(variables['Build.SourceBranch'], '/', '-')]
|
||||
- name: 'DockerTag'
|
||||
value: |
|
||||
$(Build.BuildNumber)-$(Build.SourceVersion)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,11 @@ module.exports = [
|
||||
...nx.configs['flat/typescript'],
|
||||
...nx.configs['flat/javascript'],
|
||||
{
|
||||
ignores: ['**/dist'],
|
||||
ignores: [
|
||||
'**/dist',
|
||||
'**/vite.config.*.timestamp*',
|
||||
'**/vitest.config.*.timestamp*',
|
||||
],
|
||||
},
|
||||
// {
|
||||
// files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
import nx from '@nx/eslint-plugin';
|
||||
import eslintConfigPrettier from 'eslint-config-prettier/flat';
|
||||
import jsoncEslintParser from 'jsonc-eslint-parser';
|
||||
|
||||
export default [
|
||||
...nx.configs['flat/base'],
|
||||
...nx.configs['flat/typescript'],
|
||||
...nx.configs['flat/javascript'],
|
||||
{
|
||||
ignores: ['**/dist', '**/generated'],
|
||||
},
|
||||
// Bis Module Boundaries gelöst sind, wird das Plugin nicht verwendet
|
||||
// {
|
||||
// files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
||||
// rules: {
|
||||
// '@nx/enforce-module-boundaries': [
|
||||
// 'error',
|
||||
// {
|
||||
// enforceBuildableLibDependency: true,
|
||||
// allow: ['^.*/eslint(\\.base)?\\.config\\.[cm]?js$'],
|
||||
// depConstraints: [
|
||||
// {
|
||||
// sourceTag: '*',
|
||||
// onlyDependOnLibsWithTags: ['*'],
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// },
|
||||
{
|
||||
files: [
|
||||
'**/*.ts',
|
||||
'**/*.tsx',
|
||||
'**/*.js',
|
||||
'**/*.jsx',
|
||||
'**/*.cjs',
|
||||
'**/*.mjs',
|
||||
],
|
||||
// Override or add rules here
|
||||
rules: {},
|
||||
},
|
||||
{
|
||||
files: ['**/*.json'],
|
||||
rules: {
|
||||
'@nx/dependency-checks': [
|
||||
'error',
|
||||
{
|
||||
ignoredFiles: ['{projectRoot}/eslint.config.{js,cjs,mjs}'],
|
||||
},
|
||||
],
|
||||
},
|
||||
languageOptions: {
|
||||
parser: jsoncEslintParser,
|
||||
},
|
||||
},
|
||||
eslintConfigPrettier,
|
||||
];
|
||||
40
generated/swagger/availability-api/eslint.config.js
Normal file
40
generated/swagger/availability-api/eslint.config.js
Normal file
@@ -0,0 +1,40 @@
|
||||
const nx = require('@nx/eslint-plugin');
|
||||
const baseConfig = require('../../../eslint.config.js');
|
||||
|
||||
module.exports = [
|
||||
...baseConfig,
|
||||
...nx.configs['flat/angular'],
|
||||
...nx.configs['flat/angular-template'],
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
rules: {
|
||||
'@angular-eslint/directive-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'attribute',
|
||||
prefix: 'lib',
|
||||
style: 'camelCase',
|
||||
},
|
||||
],
|
||||
'@angular-eslint/component-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'element',
|
||||
prefix: 'lib',
|
||||
style: 'kebab-case',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.html'],
|
||||
// Override or add rules here
|
||||
rules: {},
|
||||
},
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
rules: {
|
||||
'@angular-eslint/prefer-standalone': 'off',
|
||||
},
|
||||
},
|
||||
];
|
||||
40
generated/swagger/cat-search-api/eslint.config.js
Normal file
40
generated/swagger/cat-search-api/eslint.config.js
Normal file
@@ -0,0 +1,40 @@
|
||||
const nx = require('@nx/eslint-plugin');
|
||||
const baseConfig = require('../../../eslint.config.js');
|
||||
|
||||
module.exports = [
|
||||
...baseConfig,
|
||||
...nx.configs['flat/angular'],
|
||||
...nx.configs['flat/angular-template'],
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
rules: {
|
||||
'@angular-eslint/directive-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'attribute',
|
||||
prefix: 'lib',
|
||||
style: 'camelCase',
|
||||
},
|
||||
],
|
||||
'@angular-eslint/component-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'element',
|
||||
prefix: 'lib',
|
||||
style: 'kebab-case',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.html'],
|
||||
// Override or add rules here
|
||||
rules: {},
|
||||
},
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
rules: {
|
||||
'@angular-eslint/prefer-standalone': 'off',
|
||||
},
|
||||
},
|
||||
];
|
||||
40
generated/swagger/checkout-api/eslint.config.js
Normal file
40
generated/swagger/checkout-api/eslint.config.js
Normal file
@@ -0,0 +1,40 @@
|
||||
const nx = require('@nx/eslint-plugin');
|
||||
const baseConfig = require('../../../eslint.config.js');
|
||||
|
||||
module.exports = [
|
||||
...baseConfig,
|
||||
...nx.configs['flat/angular'],
|
||||
...nx.configs['flat/angular-template'],
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
rules: {
|
||||
'@angular-eslint/directive-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'attribute',
|
||||
prefix: 'lib',
|
||||
style: 'camelCase',
|
||||
},
|
||||
],
|
||||
'@angular-eslint/component-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'element',
|
||||
prefix: 'lib',
|
||||
style: 'kebab-case',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.html'],
|
||||
// Override or add rules here
|
||||
rules: {},
|
||||
},
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
rules: {
|
||||
'@angular-eslint/prefer-standalone': 'off',
|
||||
},
|
||||
},
|
||||
];
|
||||
40
generated/swagger/crm-api/eslint.config.js
Normal file
40
generated/swagger/crm-api/eslint.config.js
Normal file
@@ -0,0 +1,40 @@
|
||||
const nx = require('@nx/eslint-plugin');
|
||||
const baseConfig = require('../../../eslint.config.js');
|
||||
|
||||
module.exports = [
|
||||
...baseConfig,
|
||||
...nx.configs['flat/angular'],
|
||||
...nx.configs['flat/angular-template'],
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
rules: {
|
||||
'@angular-eslint/directive-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'attribute',
|
||||
prefix: 'lib',
|
||||
style: 'camelCase',
|
||||
},
|
||||
],
|
||||
'@angular-eslint/component-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'element',
|
||||
prefix: 'lib',
|
||||
style: 'kebab-case',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.html'],
|
||||
// Override or add rules here
|
||||
rules: {},
|
||||
},
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
rules: {
|
||||
'@angular-eslint/prefer-standalone': 'off',
|
||||
},
|
||||
},
|
||||
];
|
||||
40
generated/swagger/eis-api/eslint.config.js
Normal file
40
generated/swagger/eis-api/eslint.config.js
Normal file
@@ -0,0 +1,40 @@
|
||||
const nx = require('@nx/eslint-plugin');
|
||||
const baseConfig = require('../../../eslint.config.js');
|
||||
|
||||
module.exports = [
|
||||
...baseConfig,
|
||||
...nx.configs['flat/angular'],
|
||||
...nx.configs['flat/angular-template'],
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
rules: {
|
||||
'@angular-eslint/directive-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'attribute',
|
||||
prefix: 'lib',
|
||||
style: 'camelCase',
|
||||
},
|
||||
],
|
||||
'@angular-eslint/component-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'element',
|
||||
prefix: 'lib',
|
||||
style: 'kebab-case',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.html'],
|
||||
// Override or add rules here
|
||||
rules: {},
|
||||
},
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
rules: {
|
||||
'@angular-eslint/prefer-standalone': 'off',
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -1,34 +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: 'lib',
|
||||
style: 'camelCase',
|
||||
},
|
||||
],
|
||||
'@angular-eslint/component-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'element',
|
||||
prefix: 'lib',
|
||||
style: 'kebab-case',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.html'],
|
||||
// Override or add rules here
|
||||
rules: {},
|
||||
},
|
||||
];
|
||||
40
generated/swagger/inventory-api/eslint.config.js
Normal file
40
generated/swagger/inventory-api/eslint.config.js
Normal file
@@ -0,0 +1,40 @@
|
||||
const nx = require('@nx/eslint-plugin');
|
||||
const baseConfig = require('../../../eslint.config.js');
|
||||
|
||||
module.exports = [
|
||||
...baseConfig,
|
||||
...nx.configs['flat/angular'],
|
||||
...nx.configs['flat/angular-template'],
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
rules: {
|
||||
'@angular-eslint/directive-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'attribute',
|
||||
prefix: 'lib',
|
||||
style: 'camelCase',
|
||||
},
|
||||
],
|
||||
'@angular-eslint/component-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'element',
|
||||
prefix: 'lib',
|
||||
style: 'kebab-case',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.html'],
|
||||
// Override or add rules here
|
||||
rules: {},
|
||||
},
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
rules: {
|
||||
'@angular-eslint/prefer-standalone': 'off',
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -1,34 +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: 'lib',
|
||||
style: 'camelCase',
|
||||
},
|
||||
],
|
||||
'@angular-eslint/component-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'element',
|
||||
prefix: 'lib',
|
||||
style: 'kebab-case',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.html'],
|
||||
// Override or add rules here
|
||||
rules: {},
|
||||
},
|
||||
];
|
||||
40
generated/swagger/isa-api/eslint.config.js
Normal file
40
generated/swagger/isa-api/eslint.config.js
Normal file
@@ -0,0 +1,40 @@
|
||||
const nx = require('@nx/eslint-plugin');
|
||||
const baseConfig = require('../../../eslint.config.js');
|
||||
|
||||
module.exports = [
|
||||
...baseConfig,
|
||||
...nx.configs['flat/angular'],
|
||||
...nx.configs['flat/angular-template'],
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
rules: {
|
||||
'@angular-eslint/directive-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'attribute',
|
||||
prefix: 'lib',
|
||||
style: 'camelCase',
|
||||
},
|
||||
],
|
||||
'@angular-eslint/component-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'element',
|
||||
prefix: 'lib',
|
||||
style: 'kebab-case',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.html'],
|
||||
// Override or add rules here
|
||||
rules: {},
|
||||
},
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
rules: {
|
||||
'@angular-eslint/prefer-standalone': 'off',
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -1,34 +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: 'lib',
|
||||
style: 'camelCase',
|
||||
},
|
||||
],
|
||||
'@angular-eslint/component-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'element',
|
||||
prefix: 'lib',
|
||||
style: 'kebab-case',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.html'],
|
||||
// Override or add rules here
|
||||
rules: {},
|
||||
},
|
||||
];
|
||||
40
generated/swagger/oms-api/eslint.config.js
Normal file
40
generated/swagger/oms-api/eslint.config.js
Normal file
@@ -0,0 +1,40 @@
|
||||
const nx = require('@nx/eslint-plugin');
|
||||
const baseConfig = require('../../../eslint.config.js');
|
||||
|
||||
module.exports = [
|
||||
...baseConfig,
|
||||
...nx.configs['flat/angular'],
|
||||
...nx.configs['flat/angular-template'],
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
rules: {
|
||||
'@angular-eslint/directive-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'attribute',
|
||||
prefix: 'lib',
|
||||
style: 'camelCase',
|
||||
},
|
||||
],
|
||||
'@angular-eslint/component-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'element',
|
||||
prefix: 'lib',
|
||||
style: 'kebab-case',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.html'],
|
||||
// Override or add rules here
|
||||
rules: {},
|
||||
},
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
rules: {
|
||||
'@angular-eslint/prefer-standalone': 'off',
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -1,34 +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: 'lib',
|
||||
style: 'camelCase',
|
||||
},
|
||||
],
|
||||
'@angular-eslint/component-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'element',
|
||||
prefix: 'lib',
|
||||
style: 'kebab-case',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.html'],
|
||||
// Override or add rules here
|
||||
rules: {},
|
||||
},
|
||||
];
|
||||
40
generated/swagger/print-api/eslint.config.js
Normal file
40
generated/swagger/print-api/eslint.config.js
Normal file
@@ -0,0 +1,40 @@
|
||||
const nx = require('@nx/eslint-plugin');
|
||||
const baseConfig = require('../../../eslint.config.js');
|
||||
|
||||
module.exports = [
|
||||
...baseConfig,
|
||||
...nx.configs['flat/angular'],
|
||||
...nx.configs['flat/angular-template'],
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
rules: {
|
||||
'@angular-eslint/directive-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'attribute',
|
||||
prefix: 'lib',
|
||||
style: 'camelCase',
|
||||
},
|
||||
],
|
||||
'@angular-eslint/component-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'element',
|
||||
prefix: 'lib',
|
||||
style: 'kebab-case',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.html'],
|
||||
// Override or add rules here
|
||||
rules: {},
|
||||
},
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
rules: {
|
||||
'@angular-eslint/prefer-standalone': 'off',
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -1,34 +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: 'lib',
|
||||
style: 'camelCase',
|
||||
},
|
||||
],
|
||||
'@angular-eslint/component-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'element',
|
||||
prefix: 'lib',
|
||||
style: 'kebab-case',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.html'],
|
||||
// Override or add rules here
|
||||
rules: {},
|
||||
},
|
||||
];
|
||||
40
generated/swagger/wws-api/eslint.config.js
Normal file
40
generated/swagger/wws-api/eslint.config.js
Normal file
@@ -0,0 +1,40 @@
|
||||
const nx = require('@nx/eslint-plugin');
|
||||
const baseConfig = require('../../../eslint.config.js');
|
||||
|
||||
module.exports = [
|
||||
...baseConfig,
|
||||
...nx.configs['flat/angular'],
|
||||
...nx.configs['flat/angular-template'],
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
rules: {
|
||||
'@angular-eslint/directive-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'attribute',
|
||||
prefix: 'lib',
|
||||
style: 'camelCase',
|
||||
},
|
||||
],
|
||||
'@angular-eslint/component-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'element',
|
||||
prefix: 'lib',
|
||||
style: 'kebab-case',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.html'],
|
||||
// Override or add rules here
|
||||
rules: {},
|
||||
},
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
rules: {
|
||||
'@angular-eslint/prefer-standalone': 'off',
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -1,34 +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: 'lib',
|
||||
style: 'camelCase',
|
||||
},
|
||||
],
|
||||
'@angular-eslint/component-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'element',
|
||||
prefix: 'lib',
|
||||
style: 'kebab-case',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.html'],
|
||||
// Override or add rules here
|
||||
rules: {},
|
||||
},
|
||||
];
|
||||
40
libs/catalogue/data-access/eslint.config.js
Normal file
40
libs/catalogue/data-access/eslint.config.js
Normal file
@@ -0,0 +1,40 @@
|
||||
const nx = require('@nx/eslint-plugin');
|
||||
const baseConfig = require('../../../eslint.config.js');
|
||||
|
||||
module.exports = [
|
||||
...baseConfig,
|
||||
...nx.configs['flat/angular'],
|
||||
...nx.configs['flat/angular-template'],
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
rules: {
|
||||
'@angular-eslint/directive-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'attribute',
|
||||
prefix: 'lib',
|
||||
style: 'camelCase',
|
||||
},
|
||||
],
|
||||
'@angular-eslint/component-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'element',
|
||||
prefix: 'lib',
|
||||
style: 'kebab-case',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.html'],
|
||||
// Override or add rules here
|
||||
rules: {},
|
||||
},
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
rules: {
|
||||
'@angular-eslint/prefer-standalone': 'off',
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -1,34 +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: 'lib',
|
||||
style: 'camelCase',
|
||||
},
|
||||
],
|
||||
'@angular-eslint/component-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'element',
|
||||
prefix: 'lib',
|
||||
style: 'kebab-case',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.html'],
|
||||
// Override or add rules here
|
||||
rules: {},
|
||||
},
|
||||
];
|
||||
3
libs/catalogue/data-access/src/lib/index.ts
Normal file
3
libs/catalogue/data-access/src/lib/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './models';
|
||||
export * from './schemas';
|
||||
export * from './services';
|
||||
@@ -0,0 +1,222 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { of } from 'rxjs';
|
||||
import { CatalougeSearchService } from './catalouge-search.service';
|
||||
import { SearchService } from '@generated/swagger/cat-search-api';
|
||||
import { Item } from '../models';
|
||||
import { SearchByTermInput } from '../schemas/catalouge-search.schemas';
|
||||
|
||||
describe('CatalougeSearchService', () => {
|
||||
let service: CatalougeSearchService;
|
||||
let searchServiceSpy: jest.Mocked<SearchService>;
|
||||
|
||||
beforeEach(() => {
|
||||
const searchServiceMock = {
|
||||
SearchByEAN: jest.fn(),
|
||||
SearchSearch: jest.fn(),
|
||||
};
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
CatalougeSearchService,
|
||||
{ provide: SearchService, useValue: searchServiceMock },
|
||||
],
|
||||
});
|
||||
|
||||
service = TestBed.inject(CatalougeSearchService);
|
||||
searchServiceSpy = TestBed.inject(SearchService) as jest.Mocked<SearchService>;
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('searchByEans', () => {
|
||||
it('should return items when search is successful', (done) => {
|
||||
// Arrange
|
||||
const mockItems: Item[] = [
|
||||
{ id: 1, product: { name: 'Item 1' }, catalogAvailability: { available: true } } as unknown as Item,
|
||||
{ id: 2, product: { name: 'Item 2' }, catalogAvailability: { available: true } } as unknown as Item,
|
||||
];
|
||||
const mockResponse = {
|
||||
error: false,
|
||||
result: mockItems,
|
||||
};
|
||||
searchServiceSpy.SearchByEAN.mockReturnValue(of(mockResponse));
|
||||
|
||||
// Act
|
||||
service.searchByEans('123456789', '987654321').subscribe({
|
||||
next: (result) => {
|
||||
// Assert
|
||||
expect(result).toEqual(mockItems);
|
||||
expect(searchServiceSpy.SearchByEAN).toHaveBeenCalledWith(['123456789', '987654321']);
|
||||
done();
|
||||
},
|
||||
error: done.fail,
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw error when response has error', (done) => {
|
||||
// Arrange
|
||||
const mockResponse = {
|
||||
error: true,
|
||||
message: 'Search failed',
|
||||
};
|
||||
searchServiceSpy.SearchByEAN.mockReturnValue(of(mockResponse));
|
||||
|
||||
// Act
|
||||
service.searchByEans('123456789').subscribe({
|
||||
next: () => done.fail('Should have thrown error'),
|
||||
error: (error) => {
|
||||
// Assert
|
||||
expect(error).toBeInstanceOf(Error);
|
||||
expect(error.message).toBe('Search failed');
|
||||
done();
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle single EAN', (done) => {
|
||||
// Arrange
|
||||
const mockItems: Item[] = [{ id: 1, product: { name: 'Item 1' }, catalogAvailability: { available: true } } as unknown as Item];
|
||||
const mockResponse = {
|
||||
error: false,
|
||||
result: mockItems,
|
||||
};
|
||||
searchServiceSpy.SearchByEAN.mockReturnValue(of(mockResponse));
|
||||
|
||||
// Act
|
||||
service.searchByEans('123456789').subscribe({
|
||||
next: (result) => {
|
||||
// Assert
|
||||
expect(result).toEqual(mockItems);
|
||||
expect(searchServiceSpy.SearchByEAN).toHaveBeenCalledWith(['123456789']);
|
||||
done();
|
||||
},
|
||||
error: done.fail,
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle empty EAN array', (done) => {
|
||||
// Arrange
|
||||
const mockResponse = {
|
||||
error: false,
|
||||
result: [],
|
||||
};
|
||||
searchServiceSpy.SearchByEAN.mockReturnValue(of(mockResponse));
|
||||
|
||||
// Act
|
||||
service.searchByEans().subscribe({
|
||||
next: (result) => {
|
||||
// Assert
|
||||
expect(result).toEqual([]);
|
||||
expect(searchServiceSpy.SearchByEAN).toHaveBeenCalledWith([]);
|
||||
done();
|
||||
},
|
||||
error: done.fail,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('searchByTerm', () => {
|
||||
it('should return search results when successful', async () => {
|
||||
// Arrange
|
||||
const mockItems: Item[] = [
|
||||
{ id: 1, product: { name: 'Test Item' }, catalogAvailability: { available: true } } as unknown as Item,
|
||||
];
|
||||
const mockResponse = {
|
||||
error: false,
|
||||
result: mockItems,
|
||||
total: 1,
|
||||
};
|
||||
searchServiceSpy.SearchSearch.mockReturnValue(of(mockResponse));
|
||||
|
||||
const params: SearchByTermInput = {
|
||||
searchTerm: 'test',
|
||||
skip: 0,
|
||||
take: 10,
|
||||
};
|
||||
const abortController = new AbortController();
|
||||
|
||||
// Act
|
||||
const result = await service.searchByTerm(params, abortController.signal);
|
||||
|
||||
// Assert
|
||||
expect(result).toEqual(mockResponse);
|
||||
expect(searchServiceSpy.SearchSearch).toHaveBeenCalledWith({
|
||||
input: { qs: 'test' },
|
||||
skip: 0,
|
||||
take: 10,
|
||||
doNotTrack: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw error when response has error', async () => {
|
||||
// Arrange
|
||||
const mockResponse = {
|
||||
error: true,
|
||||
message: 'Search failed',
|
||||
};
|
||||
searchServiceSpy.SearchSearch.mockReturnValue(of(mockResponse));
|
||||
|
||||
const params: SearchByTermInput = {
|
||||
searchTerm: 'test',
|
||||
skip: 0,
|
||||
take: 10,
|
||||
};
|
||||
const abortController = new AbortController();
|
||||
|
||||
// Act & Assert
|
||||
await expect(service.searchByTerm(params, abortController.signal))
|
||||
.rejects
|
||||
.toThrow('Search failed');
|
||||
});
|
||||
|
||||
it('should handle abort signal', async () => {
|
||||
// Arrange
|
||||
const abortController = new AbortController();
|
||||
const mockResponse = {
|
||||
error: false,
|
||||
result: [],
|
||||
};
|
||||
searchServiceSpy.SearchSearch.mockReturnValue(of(mockResponse));
|
||||
|
||||
const params: SearchByTermInput = {
|
||||
searchTerm: 'test',
|
||||
skip: 0,
|
||||
take: 10,
|
||||
};
|
||||
|
||||
// Act
|
||||
const result = await service.searchByTerm(params, abortController.signal);
|
||||
|
||||
// Assert
|
||||
expect(result).toEqual(mockResponse);
|
||||
expect(searchServiceSpy.SearchSearch).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should use default values when not provided', async () => {
|
||||
// Arrange
|
||||
const mockResponse = {
|
||||
error: false,
|
||||
result: [],
|
||||
};
|
||||
searchServiceSpy.SearchSearch.mockReturnValue(of(mockResponse));
|
||||
|
||||
const params: SearchByTermInput = {
|
||||
searchTerm: 'test',
|
||||
};
|
||||
const abortController = new AbortController();
|
||||
|
||||
// Act
|
||||
await service.searchByTerm(params, abortController.signal);
|
||||
|
||||
// Assert
|
||||
expect(searchServiceSpy.SearchSearch).toHaveBeenCalledWith({
|
||||
input: { qs: 'test' },
|
||||
skip: 0,
|
||||
take: 20,
|
||||
doNotTrack: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,52 +1,53 @@
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import { SearchService } from '@generated/swagger/cat-search-api';
|
||||
import { firstValueFrom, map, Observable } from 'rxjs';
|
||||
import { takeUntilAborted } from '@isa/common/data-access';
|
||||
import { Item } from '../models';
|
||||
import {
|
||||
SearchByTermInput,
|
||||
SearchByTermSchema,
|
||||
} from '../schemas/catalouge-search.schemas';
|
||||
import { ListResponseArgs } from '@isa/common/data-access';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class CatalougeSearchService {
|
||||
#searchService = inject(SearchService);
|
||||
|
||||
searchByEans(...ean: string[]): Observable<Item[]> {
|
||||
return this.#searchService.SearchByEAN(ean).pipe(
|
||||
map((res) => {
|
||||
if (res.error) {
|
||||
throw new Error(res.message);
|
||||
}
|
||||
|
||||
return res.result as Item[];
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
async searchByTerm(
|
||||
params: SearchByTermInput,
|
||||
abortSignal: AbortSignal,
|
||||
): Promise<ListResponseArgs<Item>> {
|
||||
const { searchTerm, skip, take } = SearchByTermSchema.parse(params);
|
||||
|
||||
const req$ = this.#searchService
|
||||
.SearchSearch({
|
||||
filter: {
|
||||
qs: searchTerm,
|
||||
},
|
||||
skip,
|
||||
take,
|
||||
})
|
||||
.pipe(takeUntilAborted(abortSignal));
|
||||
|
||||
const res = await firstValueFrom(req$);
|
||||
|
||||
if (res.error) {
|
||||
throw new Error(res.message);
|
||||
}
|
||||
|
||||
return res as ListResponseArgs<Item>;
|
||||
}
|
||||
}
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import { SearchService } from '@generated/swagger/cat-search-api';
|
||||
import { firstValueFrom, map, Observable } from 'rxjs';
|
||||
import { takeUntilAborted } from '@isa/common/data-access';
|
||||
import { Item } from '../models';
|
||||
import {
|
||||
SearchByTermInput,
|
||||
SearchByTermSchema,
|
||||
} from '../schemas/catalouge-search.schemas';
|
||||
import { ListResponseArgs } from '@isa/common/data-access';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class CatalougeSearchService {
|
||||
#searchService = inject(SearchService);
|
||||
|
||||
searchByEans(...ean: string[]): Observable<Item[]> {
|
||||
return this.#searchService.SearchByEAN(ean).pipe(
|
||||
map((res) => {
|
||||
if (res.error) {
|
||||
throw new Error(res.message);
|
||||
}
|
||||
|
||||
return res.result as Item[];
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
async searchByTerm(
|
||||
params: SearchByTermInput,
|
||||
abortSignal: AbortSignal,
|
||||
): Promise<ListResponseArgs<Item>> {
|
||||
const { searchTerm, skip, take } = SearchByTermSchema.parse(params);
|
||||
|
||||
const req$ = this.#searchService
|
||||
.SearchSearch({
|
||||
input: {
|
||||
qs: searchTerm,
|
||||
},
|
||||
skip,
|
||||
take,
|
||||
doNotTrack: true,
|
||||
})
|
||||
.pipe(takeUntilAborted(abortSignal));
|
||||
|
||||
const res = await firstValueFrom(req$);
|
||||
|
||||
if (res.error) {
|
||||
throw new Error(res.message);
|
||||
}
|
||||
|
||||
return res as ListResponseArgs<Item>;
|
||||
}
|
||||
}
|
||||
|
||||
34
libs/common/data-access/eslint.config.js
Normal file
34
libs/common/data-access/eslint.config.js
Normal file
@@ -0,0 +1,34 @@
|
||||
const nx = require('@nx/eslint-plugin');
|
||||
const baseConfig = require('../../../eslint.config.js');
|
||||
|
||||
module.exports = [
|
||||
...baseConfig,
|
||||
...nx.configs['flat/angular'],
|
||||
...nx.configs['flat/angular-template'],
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
rules: {
|
||||
'@angular-eslint/directive-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'attribute',
|
||||
prefix: 'common',
|
||||
style: 'camelCase',
|
||||
},
|
||||
],
|
||||
'@angular-eslint/component-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'element',
|
||||
prefix: 'common',
|
||||
style: 'kebab-case',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.html'],
|
||||
// Override or add rules here
|
||||
rules: {},
|
||||
},
|
||||
];
|
||||
@@ -44,5 +44,8 @@ export class DataAccessError<TCode extends string, TData = void> extends Error {
|
||||
public readonly data: TData,
|
||||
) {
|
||||
super(message);
|
||||
// Set the prototype explicitly to maintain the correct prototype chain
|
||||
Object.setPrototypeOf(this, new.target.prototype);
|
||||
this.name = this.constructor.name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
export * from './errors';
|
||||
export * from './models';
|
||||
export * from './errors';
|
||||
export * from './helpers';
|
||||
export * from './models';
|
||||
export * from './operators';
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user