mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
Compare commits
124 Commits
4.0-hotfix
...
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 | ||
|
|
dd3705f8bc | ||
|
|
514715589b | ||
|
|
0740273dbc | ||
|
|
bbb9c5d39c | ||
|
|
f0bd957a07 | ||
|
|
e4f289c67d | ||
|
|
2af16d92ea | ||
|
|
99e8e7cfe0 | ||
|
|
2e012a124a | ||
|
|
d22e320294 | ||
|
|
a0f24aac17 | ||
|
|
7ae484fc83 | ||
|
|
0dcb31973f | ||
|
|
2dbf7dda37 | ||
|
|
cce15a2137 | ||
|
|
14a5a67a1e | ||
|
|
d7d535c10d | ||
|
|
ad00899b6e | ||
|
|
1e84223076 | ||
|
|
244984b6cf | ||
|
|
b39abe630d | ||
|
|
239ab52890 | ||
|
|
4732656a0f | ||
|
|
0da9800ca0 | ||
|
|
baf4a0dfbc | ||
|
|
da5a42280a | ||
|
|
4d29189c8d | ||
|
|
32bd3e26d2 | ||
|
|
6d26f7f6c0 | ||
|
|
72bcacefb6 | ||
|
|
71e9a6da0e | ||
|
|
b339a6d79f | ||
|
|
0b4aef5f6c | ||
|
|
c5182809ac | ||
|
|
f4b541c7c0 | ||
|
|
3f233f9580 | ||
|
|
4111663d8c | ||
|
|
2beeba5c92 | ||
|
|
edab1322c8 | ||
|
|
59ce736faa | ||
|
|
3cd6f4bd58 | ||
|
|
594acaa5f5 | ||
|
|
76ff54dd3a | ||
|
|
598df7d5ed | ||
|
|
442670bdd0 | ||
|
|
b015e97e1f | ||
|
|
65ab3bfc0a | ||
|
|
40c9d51dfc | ||
|
|
5f74c6ddf8 | ||
|
|
a36d746fb8 | ||
|
|
f6b2b554bb | ||
|
|
465df27858 | ||
|
|
b7e7155577 | ||
|
|
b28c204f23 | ||
|
|
e7a807cfbd | ||
|
|
344dc61a90 | ||
|
|
8d063428fc | ||
|
|
06b0c6264a | ||
|
|
4fe633e973 | ||
|
|
2463a803ea | ||
|
|
1663dcec73 | ||
|
|
827aa565c5 | ||
|
|
39fc4ce1ce | ||
|
|
4f4b072e25 | ||
|
|
9af4a72a76 | ||
|
|
7a44101e90 | ||
|
|
5e6ee35d91 | ||
|
|
15db63aa1a | ||
|
|
998946157a | ||
|
|
11cfa4039f | ||
|
|
26fd5cb389 | ||
|
|
f34f2164fc | ||
|
|
a68f5b5347 | ||
|
|
d53540b8db | ||
|
|
4cf0ce820e | ||
|
|
b21ebac53f | ||
|
|
5a68adc87c | ||
|
|
befdc9fa4d | ||
|
|
e41dbc2870 | ||
|
|
083f75a395 | ||
|
|
7c8aef9a48 | ||
|
|
ee841eba49 | ||
|
|
0560f18de3 | ||
|
|
d8c2ca9bdc | ||
|
|
54664123fb |
@@ -2,6 +2,3 @@ last 1 Chrome version
|
||||
last 1 Firefox version
|
||||
last 2 Edge major versions
|
||||
last 2 iOS major versions
|
||||
safari > 11
|
||||
Firefox ESR
|
||||
not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line.
|
||||
@@ -7,6 +7,7 @@ indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
end_of_line = crlf
|
||||
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
|
||||
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.1 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
|
||||
|
||||
144
.gitignore
vendored
144
.gitignore
vendored
@@ -1,67 +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
|
||||
.prettierrc
|
||||
|
||||
# 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
|
||||
# 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"
|
||||
}
|
||||
|
||||
37
.prettierrc
Normal file
37
.prettierrc
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"semi": true,
|
||||
"trailingComma": "all",
|
||||
"tabWidth": 2,
|
||||
"bracketSpacing": true,
|
||||
"printWidth": 80,
|
||||
"endOfLine": "auto",
|
||||
"arrowParens": "always",
|
||||
"quoteProps": "consistent",
|
||||
"overrides": [
|
||||
{
|
||||
"files": "*.html",
|
||||
"options": {
|
||||
"parser": "html"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": "*.component.html",
|
||||
"options": {
|
||||
"parser": "angular"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": "*.scss",
|
||||
"options": {
|
||||
"singleQuote": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": "*.json",
|
||||
"options": {
|
||||
"printWidth": 80
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
3
.vscode/extensions.json
vendored
3
.vscode/extensions.json
vendored
@@ -5,6 +5,7 @@
|
||||
"angular.ng-template",
|
||||
"nrwl.angular-console",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"firsttris.vscode-jest-runner"
|
||||
"firsttris.vscode-jest-runner",
|
||||
"editorconfig.editorconfig"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -2,8 +2,11 @@ import type { StorybookConfig } from '@storybook/angular';
|
||||
|
||||
const config: StorybookConfig = {
|
||||
stories: ['../stories/**/*.@(mdx|stories.@(js|jsx|ts|tsx))'],
|
||||
addons: ['@storybook/addon-essentials', '@storybook/addon-interactions'],
|
||||
staticDirs: ['../src/assets'],
|
||||
addons: [
|
||||
'@storybook/addon-essentials',
|
||||
'@storybook/addon-interactions',
|
||||
'@storybook/addon-docs',
|
||||
],
|
||||
previewHead: (head) => `
|
||||
${head}
|
||||
<link href="/assets/fonts/fonts.css" rel="stylesheet" />
|
||||
|
||||
@@ -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": []
|
||||
@@ -84,7 +84,8 @@
|
||||
"buildTarget": "isa-app:build:development"
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "development"
|
||||
"defaultConfiguration": "development",
|
||||
"continuous": true
|
||||
},
|
||||
"extract-i18n": {
|
||||
"executor": "@angular-devkit/build-angular:extract-i18n",
|
||||
@@ -117,7 +118,19 @@
|
||||
"configDir": "apps/isa-app/.storybook",
|
||||
"browserTarget": "isa-app:build",
|
||||
"compodoc": false,
|
||||
"styles": ["apps/isa-app/src/ui.scss", "apps/isa-app/src/styles.scss"]
|
||||
"open": false,
|
||||
"assets": [
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "apps/isa-app/src/assets",
|
||||
"output": "/assets"
|
||||
}
|
||||
],
|
||||
"styles": [
|
||||
"@angular/cdk/overlay-prebuilt.css",
|
||||
"apps/isa-app/src/tailwind.scss",
|
||||
"apps/isa-app/src/styles.scss"
|
||||
]
|
||||
},
|
||||
"configurations": {
|
||||
"ci": {
|
||||
@@ -132,33 +145,18 @@
|
||||
"outputDir": "dist/storybook/isa-app",
|
||||
"configDir": "apps/isa-app/.storybook",
|
||||
"browserTarget": "isa-app:build",
|
||||
"compodoc": false
|
||||
"compodoc": false,
|
||||
"styles": [
|
||||
"@angular/cdk/overlay-prebuilt.css",
|
||||
"apps/isa-app/src/tailwind.scss",
|
||||
"apps/isa-app/src/styles.scss"
|
||||
]
|
||||
},
|
||||
"configurations": {
|
||||
"ci": {
|
||||
"quiet": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"test-storybook": {
|
||||
"executor": "nx:run-commands",
|
||||
"options": {
|
||||
"command": "test-storybook -c apps/isa-app/.storybook --url=http://localhost:4400"
|
||||
}
|
||||
},
|
||||
"static-storybook": {
|
||||
"executor": "@nx/web:file-server",
|
||||
"dependsOn": ["build-storybook"],
|
||||
"options": {
|
||||
"buildTarget": "isa-app:build-storybook",
|
||||
"staticFilePath": "dist/storybook/isa-app",
|
||||
"spa": true
|
||||
},
|
||||
"configurations": {
|
||||
"ci": {
|
||||
"buildTarget": "isa-app:build-storybook:ci"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,184 +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 { processResolverFn } from '@isa/core/process';
|
||||
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: ':processId',
|
||||
component: MainComponent,
|
||||
resolve: { process: processResolverFn },
|
||||
canActivate: [IsAuthenticatedGuard],
|
||||
children: [
|
||||
{
|
||||
path: 'return',
|
||||
loadChildren: () => import('@isa/oms/feature/return-search').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 {}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DOCUMENT } from '@angular/common';
|
||||
|
||||
import {
|
||||
Component,
|
||||
effect,
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
Renderer2,
|
||||
signal,
|
||||
untracked,
|
||||
DOCUMENT
|
||||
} from '@angular/core';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { SwUpdate } from '@angular/service-worker';
|
||||
|
||||
@@ -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,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,
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
<p>Vorschläge:</p>
|
||||
|
||||
<ul class="content">
|
||||
<li *ngFor="let item of ref?.data">
|
||||
<span>{{ item.street }} {{ item.streetNumber }}, {{ item.zipCode }} {{ item.city }}</span>
|
||||
<button (click)="ref.close(item)">Übernehmen</button>
|
||||
</li>
|
||||
@for (item of ref?.data; track item) {
|
||||
<li>
|
||||
<span>{{ item.street }} {{ item.streetNumber }}, {{ item.zipCode }} {{ item.city }}</span>
|
||||
<button (click)="ref.close(item)">Übernehmen</button>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
|
||||
<div class="center">
|
||||
|
||||
@@ -6,13 +6,19 @@
|
||||
uiSearchboxSearchButton
|
||||
(click)="filter(input.value)"
|
||||
[disabled]="branchesFetching$ | async"
|
||||
>
|
||||
<ui-icon class="spin" *ngIf="branchesFetching$ | async" icon="spinner" size="32px"></ui-icon>
|
||||
<ui-icon *ngIf="!(branchesFetching$ | async)" icon="search" size="24px"></ui-icon>
|
||||
</button>
|
||||
<button *ngIf="input.value" type="reset" uiSearchboxClearButton (click)="filter(''); cancelSearch(); input.value = ''">
|
||||
<ui-icon icon="close" size="22px"></ui-icon>
|
||||
>
|
||||
@if (branchesFetching$ | async) {
|
||||
<ui-icon class="spin" icon="spinner" size="32px"></ui-icon>
|
||||
}
|
||||
@if (!(branchesFetching$ | async)) {
|
||||
<ui-icon icon="search" size="24px"></ui-icon>
|
||||
}
|
||||
</button>
|
||||
@if (input.value) {
|
||||
<button type="reset" uiSearchboxClearButton (click)="filter(''); cancelSearch(); input.value = ''">
|
||||
<ui-icon icon="close" size="22px"></ui-icon>
|
||||
</button>
|
||||
}
|
||||
</ui-searchbox>
|
||||
|
||||
<p class="subtitle">
|
||||
@@ -25,7 +31,7 @@
|
||||
<hr />
|
||||
|
||||
<div class="branches">
|
||||
<ng-container *ngFor="let branch of filteredBranches$ | async">
|
||||
@for (branch of filteredBranches$ | async; track branch) {
|
||||
<div class="branch">
|
||||
<div class="branch-info">
|
||||
<span class="branch-name">
|
||||
@@ -36,25 +42,23 @@
|
||||
{{ branch.address.city }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="branch-actions">
|
||||
<button
|
||||
*ngIf="(branch.id | stockInfo: (inStock$ | async))?.availableQuantity > 0 && branch?.isShippingEnabled"
|
||||
class="cta-reserve"
|
||||
(click)="reserve(branch)"
|
||||
>
|
||||
Reservieren
|
||||
</button>
|
||||
|
||||
@if ((branch.id | stockInfo: (inStock$ | async))?.availableQuantity > 0 && branch?.isShippingEnabled) {
|
||||
<button
|
||||
class="cta-reserve"
|
||||
(click)="reserve(branch)"
|
||||
>
|
||||
Reservieren
|
||||
</button>
|
||||
}
|
||||
<ui-spinner [show]="stockFetching$ | async">
|
||||
<span class="branch-stock">
|
||||
<ui-icon icon="home" size="22px"></ui-icon>
|
||||
|
||||
<span>{{ branch.id | inStock: (inStock$ | async) }}x</span>
|
||||
</span>
|
||||
</ui-spinner>
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
</ng-container>
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -3,9 +3,11 @@
|
||||
</div>
|
||||
|
||||
<div class="thumbnails-wrapper">
|
||||
<button *ngFor="let image of images" (click)="activeImage = image" [class.selected]="activeImage.url === image.url">
|
||||
<img class="thumbnail" [src]="image.thumbUrl" />
|
||||
</button>
|
||||
@for (image of images; track image) {
|
||||
<button (click)="activeImage = image" [class.selected]="activeImage.url === image.url">
|
||||
<img class="thumbnail" [src]="image.thumbUrl" />
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
|
||||
@@ -18,9 +18,11 @@
|
||||
<ui-quantity-dropdown [quantity]="itemQuantity$ | async" (quantityChange)="onQuantityChange($event)"></ui-quantity-dropdown>
|
||||
</div>
|
||||
<div class="relative">
|
||||
<div *ngIf="stockWarning$ | async" class="text-warning font-bold absolute right-0 top-0 whitespace-nowrap">
|
||||
Es befinden sich {{ availableQuantity$ | async }} Exemplare in der Filiale
|
||||
</div>
|
||||
@if (stockWarning$ | async) {
|
||||
<div class="text-warning font-bold absolute right-0 top-0 whitespace-nowrap">
|
||||
Es befinden sich {{ availableQuantity$ | async }} Exemplare in der Filiale
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -20,18 +20,21 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="overflow-y-auto -mx-4 scroll-bar">
|
||||
<div *ngIf="emptyShoppingCart$ | async" class="h-full grid items-center justify-center">
|
||||
<h3 class="text-xl font-bold text-center text-gray-500">
|
||||
Warenkorb ist leer, bitte suchen oder scannen
|
||||
<br />
|
||||
Sie Artikel um den Warenkob zu füllen.
|
||||
</h3>
|
||||
</div>
|
||||
<shared-kulturpass-order-item
|
||||
class="border-b border-solid border-[#EFF1F5]"
|
||||
*ngFor="let item of items$ | async; trackBy: trackItemById"
|
||||
[item]="item"
|
||||
></shared-kulturpass-order-item>
|
||||
@if (emptyShoppingCart$ | async) {
|
||||
<div class="h-full grid items-center justify-center">
|
||||
<h3 class="text-xl font-bold text-center text-gray-500">
|
||||
Warenkorb ist leer, bitte suchen oder scannen
|
||||
<br />
|
||||
Sie Artikel um den Warenkob zu füllen.
|
||||
</h3>
|
||||
</div>
|
||||
}
|
||||
@for (item of items$ | async; track trackItemById($index, item)) {
|
||||
<shared-kulturpass-order-item
|
||||
class="border-b border-solid border-[#EFF1F5]"
|
||||
[item]="item"
|
||||
></shared-kulturpass-order-item>
|
||||
}
|
||||
</div>
|
||||
<div class="flex flex-row justify-evenly items-stretch border-t border-solid border-[#EFF1F5] py-3 px-4 -mx-4">
|
||||
<div class="grid grid-flow-row text-xl">
|
||||
@@ -49,13 +52,15 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid items-end justify-between">
|
||||
<div *ngIf="negativeBalance$ | async" class="text-xl text-warning font-bold text-center">Der Betrag übersteigt ihr Guthaben</div>
|
||||
@if (negativeBalance$ | async) {
|
||||
<div class="text-xl text-warning font-bold text-center">Der Betrag übersteigt ihr Guthaben</div>
|
||||
}
|
||||
<button
|
||||
type="button"
|
||||
class="bg-brand text-white px-6 py-3 font-bold rounded-full disabled:bg-disabled-branch disabled:text-active-branch"
|
||||
[disabled]="orderButtonDisabled$ | async"
|
||||
(click)="order()"
|
||||
>
|
||||
>
|
||||
<shared-loader [loading]="ordering$ | async" hideContent="true">Kauf abschließen und Rechnung drucken</shared-loader>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -2,19 +2,20 @@
|
||||
{{ message }}
|
||||
</p>
|
||||
<div class="message-modal-actions grid grid-flow-col gap-4 items-center justify-center">
|
||||
<button
|
||||
*ngFor="let action of actions"
|
||||
type="button"
|
||||
class="btn rounded-full font-bold text-p1 border-[2px] border-solid border-brand px-6"
|
||||
[class.bg-brand]="action.primary"
|
||||
[class.hover:bg-brand]="action.primary"
|
||||
[class.text-white]="action.primary"
|
||||
[class.bg-white]="!action.primary"
|
||||
[class.text-brand]="!action.primary"
|
||||
[class.hover:bg-white]="!action.primary"
|
||||
[class.hover:text-brand]="!action.primary"
|
||||
(click)="clickAction(action)"
|
||||
>
|
||||
{{ action.label }}
|
||||
</button>
|
||||
@for (action of actions; track action) {
|
||||
<button
|
||||
type="button"
|
||||
class="btn rounded-full font-bold text-p1 border-[2px] border-solid border-brand px-6"
|
||||
[class.bg-brand]="action.primary"
|
||||
[class.hover:bg-brand]="action.primary"
|
||||
[class.text-white]="action.primary"
|
||||
[class.bg-white]="!action.primary"
|
||||
[class.text-brand]="!action.primary"
|
||||
[class.hover:bg-white]="!action.primary"
|
||||
[class.hover:text-brand]="!action.primary"
|
||||
(click)="clickAction(action)"
|
||||
>
|
||||
{{ action.label }}
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { Component, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { UiModalRef } from '@ui/modal';
|
||||
import { MessageModalData } from './message-modal.data';
|
||||
@@ -10,7 +10,7 @@ import { MessageModalAction } from './message-modal.action';
|
||||
styleUrls: ['message-modal.component.css'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
host: { class: 'shared-message-modal' },
|
||||
imports: [CommonModule],
|
||||
imports: [],
|
||||
})
|
||||
export class MessageModalComponent {
|
||||
get message() {
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
<div class="notification-text">{{ item.text }}</div>
|
||||
</div>
|
||||
<div>
|
||||
<button *ngIf="editButton" class="notification-edit-cta text-brand font-bold text-lg px-4 py-3" (click)="itemSelected.emit(item)">
|
||||
{{ editButtonLabel }}
|
||||
</button>
|
||||
@if (editButton) {
|
||||
<button class="notification-edit-cta text-brand font-bold text-lg px-4 py-3" (click)="itemSelected.emit(item)">
|
||||
{{ editButtonLabel }}
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div class="notification-list scroll-bar">
|
||||
<ng-container *ngFor="let notification of notifications">
|
||||
@for (notification of notifications; track notification) {
|
||||
<modal-notifications-list-item
|
||||
(click)="itemSelected(notification)"
|
||||
[editButtonLabel]="'Packstück-Prüfung'"
|
||||
@@ -7,5 +7,5 @@
|
||||
(itemSelected)="itemSelected($event)"
|
||||
></modal-notifications-list-item>
|
||||
<hr />
|
||||
</ng-container>
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
<div class="notification-list scroll-bar">
|
||||
<ng-container *ngFor="let notification of notifications">
|
||||
<modal-notifications-list-item [item]="notification" (itemSelected)="itemSelected($event)"></modal-notifications-list-item>
|
||||
@for (notification of notifications; track notification) {
|
||||
<modal-notifications-list-item
|
||||
[item]="notification"
|
||||
(itemSelected)="itemSelected($event)"
|
||||
></modal-notifications-list-item>
|
||||
<hr />
|
||||
</ng-container>
|
||||
}
|
||||
</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,8 +1,8 @@
|
||||
<div class="notification-list scroll-bar">
|
||||
<ng-container *ngFor="let notification of notifications">
|
||||
@for (notification of notifications; track notification) {
|
||||
<modal-notifications-list-item [item]="notification" (itemSelected)="itemSelected($event)"></modal-notifications-list-item>
|
||||
<hr />
|
||||
</ng-container>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<div class="notification-list scroll-bar">
|
||||
<ng-container *ngFor="let notification of notifications">
|
||||
@for (notification of notifications; track notification) {
|
||||
<modal-notifications-list-item [item]="notification" (itemSelected)="itemSelected($event)"></modal-notifications-list-item>
|
||||
<hr />
|
||||
</ng-container>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<div class="notification-list scroll-bar">
|
||||
<ng-container *ngFor="let notification of notifications">
|
||||
@for (notification of notifications; track notification) {
|
||||
<div class="notification-headline">
|
||||
<h1>{{ notification.headline }}</h1>
|
||||
</div>
|
||||
<div class="notification-text">{{ notification.text }}</div>
|
||||
<hr />
|
||||
</ng-container>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<h1>Sie haben neue Nachrichten</h1>
|
||||
|
||||
<ng-container *ngFor="let notification of notifications$ | async | keyvalue">
|
||||
@for (notification of notifications$ | async | keyvalue; track notification) {
|
||||
<button type="button" class="notification-card" (click)="selectArea(notification.key)">
|
||||
<div class="notification-icon">
|
||||
<div class="notification-counter">{{ notification.value?.length }}</div>
|
||||
@@ -9,30 +9,37 @@
|
||||
<span>{{ notification.value?.[0]?.category }}</span>
|
||||
</button>
|
||||
<hr class="-mx-4" />
|
||||
<ng-container *ngIf="notification.key === selectedArea" [ngSwitch]="notification.value?.[0]?.category">
|
||||
<modal-notifications-update-group
|
||||
*ngSwitchCase="'ISA-Update'"
|
||||
[notifications]="notifications[selectedArea]"
|
||||
></modal-notifications-update-group>
|
||||
<modal-notifications-reservation-group
|
||||
*ngSwitchCase="'Reservierungsanfragen'"
|
||||
[notifications]="notifications[selectedArea]"
|
||||
(navigated)="close()"
|
||||
></modal-notifications-reservation-group>
|
||||
<modal-notifications-remission-group
|
||||
*ngSwitchCase="'Remission'"
|
||||
[notifications]="notifications[selectedArea]"
|
||||
(navigated)="close()"
|
||||
></modal-notifications-remission-group>
|
||||
<modal-notifications-task-calendar-group
|
||||
*ngSwitchCase="'Tätigkeitskalender'"
|
||||
[notifications]="notifications[selectedArea]"
|
||||
(navigated)="close()"
|
||||
></modal-notifications-task-calendar-group>
|
||||
<modal-notifications-package-inspection-group
|
||||
*ngSwitchCase="'Wareneingang Lagerware'"
|
||||
[notifications]="notifications[selectedArea]"
|
||||
(navigated)="close()"
|
||||
></modal-notifications-package-inspection-group>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
@if (notification.key === selectedArea) {
|
||||
@switch (notification.value?.[0]?.category) {
|
||||
@case ('ISA-Update') {
|
||||
<modal-notifications-update-group
|
||||
[notifications]="notifications[selectedArea]"
|
||||
></modal-notifications-update-group>
|
||||
}
|
||||
@case ('Reservierungsanfragen') {
|
||||
<modal-notifications-reservation-group
|
||||
[notifications]="notifications[selectedArea]"
|
||||
(navigated)="close()"
|
||||
></modal-notifications-reservation-group>
|
||||
}
|
||||
@case ('Remission') {
|
||||
<modal-notifications-remission-group
|
||||
[notifications]="notifications[selectedArea]"
|
||||
(navigated)="close()"
|
||||
></modal-notifications-remission-group>
|
||||
}
|
||||
@case ('Tätigkeitskalender') {
|
||||
<modal-notifications-task-calendar-group
|
||||
[notifications]="notifications[selectedArea]"
|
||||
(navigated)="close()"
|
||||
></modal-notifications-task-calendar-group>
|
||||
}
|
||||
@case ('Wareneingang Lagerware') {
|
||||
<modal-notifications-package-inspection-group
|
||||
[notifications]="notifications[selectedArea]"
|
||||
(navigated)="close()"
|
||||
></modal-notifications-package-inspection-group>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,17 @@
|
||||
<div class="header">
|
||||
<h1>Wählen Sie einen Drucker aus</h1>
|
||||
|
||||
<span *ngIf="error" class="error-message">{{ errorMessage }}</span>
|
||||
@if (error) {
|
||||
<span class="error-message">{{ errorMessage }}</span>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="body">
|
||||
<ui-spinner [show]="!loaded">
|
||||
<ui-select class="select" [(ngModel)]="selectedPrinterKey">
|
||||
<ui-select-option *ngFor="let printer of printers$ | async" [label]="printer.description" [value]="printer.key"></ui-select-option>
|
||||
@for (printer of printers$ | async; track printer) {
|
||||
<ui-select-option [label]="printer.description" [value]="printer.key"></ui-select-option>
|
||||
}
|
||||
</ui-select>
|
||||
</ui-spinner>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
<div class="flex flex-col text-right" [class.hidden]="hideHeader$ | async">
|
||||
<button type="button" class="font-bold text-[#0556B4]" *ngIf="selectButton$ | async" (click)="selectAll()">Alle auswählen</button>
|
||||
<button type="button" class="font-bold text-[#0556B4]" *ngIf="unselectButton$ | async" (click)="unselectAll()">Alle abwählen</button>
|
||||
@if (selectButton$ | async) {
|
||||
<button type="button" class="font-bold text-[#0556B4]" (click)="selectAll()">Alle auswählen</button>
|
||||
}
|
||||
@if (unselectButton$ | async) {
|
||||
<button type="button" class="font-bold text-[#0556B4]" (click)="unselectAll()">Alle abwählen</button>
|
||||
}
|
||||
<span class="mt-2">{{ selectedItemsCount$ | async }} von {{ itemsCount$ | async }} Artikel</span>
|
||||
</div>
|
||||
|
||||
@@ -15,147 +15,171 @@
|
||||
</div>
|
||||
<div class="shared-purchase-options-list-item__manufacturer-and-ean">
|
||||
{{ product?.manufacturer }}
|
||||
<span *ngIf="product?.manufacturer && product?.ean">|</span>
|
||||
@if (product?.manufacturer && product?.ean) {
|
||||
<span>|</span>
|
||||
}
|
||||
{{ product?.ean }}
|
||||
</div>
|
||||
<div class="shared-purchase-options-list-item__volume-and-publication-date">
|
||||
{{ product?.volume }}
|
||||
<span *ngIf="product?.volume && product?.publicationDate">|</span>
|
||||
@if (product?.volume && product?.publicationDate) {
|
||||
<span>|</span>
|
||||
}
|
||||
{{ product?.publicationDate | date: 'dd. MMMM yyyy' }}
|
||||
</div>
|
||||
<div class="shared-purchase-options-list-item__availabilities mt-3 grid grid-flow-row gap-2 justify-start">
|
||||
<div class="whitespace-nowrap self-center" *ngIf="(availabilities$ | async)?.length">Verfügbar als</div>
|
||||
<div *ngFor="let availability of availabilities$ | async" class="grid grid-flow-col gap-4 justify-start">
|
||||
<div
|
||||
[ngSwitch]="availability.purchaseOption"
|
||||
class="shared-purchase-options-list-item__availability grid grid-flow-col gap-2 items-center"
|
||||
[attr.data-option]="availability.purchaseOption"
|
||||
>
|
||||
<ng-container *ngSwitchCase="'delivery'">
|
||||
<shared-icon icon="isa-truck" [size]="22"></shared-icon>
|
||||
{{ availability.data.estimatedDelivery?.start | date: 'EE dd.MM.' }}
|
||||
-
|
||||
{{ availability.data.estimatedDelivery?.stop | date: 'EE dd.MM.' }}
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="'dig-delivery'">
|
||||
<shared-icon icon="isa-truck" [size]="22"></shared-icon>
|
||||
{{ availability.data.estimatedDelivery?.start | date: 'EE dd.MM.' }}
|
||||
-
|
||||
{{ availability.data.estimatedDelivery?.stop | date: 'EE dd.MM.' }}
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="'b2b-delivery'">
|
||||
<shared-icon icon="isa-b2b-truck" [size]="24"></shared-icon>
|
||||
{{ availability.data.estimatedShippingDate | date: 'dd. MMMM yyyy' }}
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="'pickup'">
|
||||
<shared-icon
|
||||
class="cursor-pointer"
|
||||
#uiOverlayTrigger="uiOverlayTrigger"
|
||||
[uiOverlayTrigger]="orderDeadlineTooltip"
|
||||
[class.tooltip-active]="uiOverlayTrigger.opened"
|
||||
icon="isa-box-out"
|
||||
[size]="18"
|
||||
></shared-icon>
|
||||
{{ availability.data.estimatedShippingDate | date: 'dd. MMMM yyyy' }}
|
||||
<ui-tooltip
|
||||
#orderDeadlineTooltip
|
||||
yPosition="above"
|
||||
xPosition="after"
|
||||
[yOffset]="-12"
|
||||
[xOffset]="4"
|
||||
[warning]="true"
|
||||
[closeable]="true"
|
||||
@if ((availabilities$ | async)?.length) {
|
||||
<div class="whitespace-nowrap self-center">Verfügbar als</div>
|
||||
}
|
||||
@for (availability of availabilities$ | async; track availability) {
|
||||
<div class="grid grid-flow-col gap-4 justify-start">
|
||||
<div
|
||||
class="shared-purchase-options-list-item__availability grid grid-flow-col gap-2 items-center"
|
||||
[attr.data-option]="availability.purchaseOption"
|
||||
>
|
||||
<b>{{ availability.data?.orderDeadline | orderDeadline }}</b>
|
||||
</ui-tooltip>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="'in-store'">
|
||||
<shared-icon icon="isa-shopping-bag" [size]="18"></shared-icon>
|
||||
{{ availability.data.inStock }}x
|
||||
<ng-container *ngIf="isEVT; else noEVT">ab {{ isEVT | date: 'dd. MMMM yyyy' }}</ng-container>
|
||||
<ng-template #noEVT>ab sofort</ng-template>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="'download'">
|
||||
<shared-icon icon="isa-download" [size]="22"></shared-icon>
|
||||
Download
|
||||
</ng-container>
|
||||
@switch (availability.purchaseOption) {
|
||||
@case ('delivery') {
|
||||
<shared-icon icon="isa-truck" [size]="22"></shared-icon>
|
||||
{{ availability.data.estimatedDelivery?.start | date: 'EE dd.MM.' }}
|
||||
-
|
||||
{{ availability.data.estimatedDelivery?.stop | date: 'EE dd.MM.' }}
|
||||
}
|
||||
@case ('dig-delivery') {
|
||||
<shared-icon icon="isa-truck" [size]="22"></shared-icon>
|
||||
{{ availability.data.estimatedDelivery?.start | date: 'EE dd.MM.' }}
|
||||
-
|
||||
{{ availability.data.estimatedDelivery?.stop | date: 'EE dd.MM.' }}
|
||||
}
|
||||
@case ('b2b-delivery') {
|
||||
<shared-icon icon="isa-b2b-truck" [size]="24"></shared-icon>
|
||||
{{ availability.data.estimatedShippingDate | date: 'dd. MMMM yyyy' }}
|
||||
}
|
||||
@case ('pickup') {
|
||||
<shared-icon
|
||||
class="cursor-pointer"
|
||||
#uiOverlayTrigger="uiOverlayTrigger"
|
||||
[uiOverlayTrigger]="orderDeadlineTooltip"
|
||||
[class.tooltip-active]="uiOverlayTrigger.opened"
|
||||
icon="isa-box-out"
|
||||
[size]="18"
|
||||
></shared-icon>
|
||||
{{ availability.data.estimatedShippingDate | date: 'dd. MMMM yyyy' }}
|
||||
<ui-tooltip
|
||||
#orderDeadlineTooltip
|
||||
yPosition="above"
|
||||
xPosition="after"
|
||||
[yOffset]="-12"
|
||||
[xOffset]="4"
|
||||
[warning]="true"
|
||||
[closeable]="true"
|
||||
>
|
||||
<b>{{ availability.data?.orderDeadline | orderDeadline }}</b>
|
||||
</ui-tooltip>
|
||||
}
|
||||
@case ('in-store') {
|
||||
<shared-icon icon="isa-shopping-bag" [size]="18"></shared-icon>
|
||||
{{ availability.data.inStock }}x
|
||||
@if (isEVT) {
|
||||
ab {{ isEVT | date: 'dd. MMMM yyyy' }}
|
||||
} @else {
|
||||
ab sofort
|
||||
}
|
||||
}
|
||||
@case ('download') {
|
||||
<shared-icon icon="isa-download" [size]="22"></shared-icon>
|
||||
Download
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="shared-purchase-options-list-item__price text-right ml-4 flex flex-col items-end">
|
||||
<div class="shared-purchase-options-list-item__price-value font-bold text-xl flex flex-row items-center">
|
||||
<div class="relative flex flex-row justify-end items-start">
|
||||
<ui-select
|
||||
*ngIf="canEditVat$ | async"
|
||||
class="w-[6.5rem] min-h-[3.4375rem] p-4 rounded-card border border-solid border-[#AEB7C1] mr-4"
|
||||
tabindex="-1"
|
||||
[formControl]="manualVatFormControl"
|
||||
[defaultLabel]="'MwSt'"
|
||||
>
|
||||
<ui-select-option *ngFor="let vat of vats$ | async" [label]="vat.name + '%'" [value]="vat.vatType"></ui-select-option>
|
||||
</ui-select>
|
||||
<shared-input-control
|
||||
[class.ml-6]="priceFormControl?.invalid && priceFormControl?.dirty"
|
||||
*ngIf="canEditPrice$ | async; else priceTmpl"
|
||||
>
|
||||
<shared-input-control-indicator>
|
||||
<shared-icon *ngIf="priceFormControl?.invalid && priceFormControl?.dirty" icon="mat-info"></shared-icon>
|
||||
</shared-input-control-indicator>
|
||||
<input
|
||||
[uiOverlayTrigger]="giftCardTooltip"
|
||||
triggerOn="none"
|
||||
#quantityInput
|
||||
#priceOverlayTrigger="uiOverlayTrigger"
|
||||
sharedInputControlInput
|
||||
type="string"
|
||||
class="w-24"
|
||||
[formControl]="priceFormControl"
|
||||
placeholder="00,00"
|
||||
(sharedOnInit)="onPriceInputInit(quantityInput, priceOverlayTrigger)"
|
||||
sharedNumberValue
|
||||
/>
|
||||
<shared-input-control-suffix>EUR</shared-input-control-suffix>
|
||||
<shared-input-control-error error="required">Preis ist ungültig</shared-input-control-error>
|
||||
<shared-input-control-error error="pattern">Preis ist ungültig</shared-input-control-error>
|
||||
<shared-input-control-error error="min">Preis ist ungültig</shared-input-control-error>
|
||||
<shared-input-control-error error="max">Preis ist ungültig</shared-input-control-error>
|
||||
</shared-input-control>
|
||||
@if (canEditVat$ | async) {
|
||||
<ui-select
|
||||
class="w-[6.5rem] min-h-[3.4375rem] p-4 rounded-card border border-solid border-[#AEB7C1] mr-4"
|
||||
tabindex="-1"
|
||||
[formControl]="manualVatFormControl"
|
||||
[defaultLabel]="'MwSt'"
|
||||
>
|
||||
@for (vat of vats$ | async; track vat) {
|
||||
<ui-select-option [label]="vat.name + '%'" [value]="vat.vatType"></ui-select-option>
|
||||
}
|
||||
</ui-select>
|
||||
}
|
||||
@if (canEditPrice$ | async) {
|
||||
<shared-input-control
|
||||
[class.ml-6]="priceFormControl?.invalid && priceFormControl?.dirty"
|
||||
>
|
||||
<shared-input-control-indicator>
|
||||
@if (priceFormControl?.invalid && priceFormControl?.dirty) {
|
||||
<shared-icon icon="mat-info"></shared-icon>
|
||||
}
|
||||
</shared-input-control-indicator>
|
||||
<input
|
||||
[uiOverlayTrigger]="giftCardTooltip"
|
||||
triggerOn="none"
|
||||
#quantityInput
|
||||
#priceOverlayTrigger="uiOverlayTrigger"
|
||||
sharedInputControlInput
|
||||
type="string"
|
||||
class="w-24"
|
||||
[formControl]="priceFormControl"
|
||||
placeholder="00,00"
|
||||
(sharedOnInit)="onPriceInputInit(quantityInput, priceOverlayTrigger)"
|
||||
sharedNumberValue
|
||||
/>
|
||||
<shared-input-control-suffix>EUR</shared-input-control-suffix>
|
||||
<shared-input-control-error error="required">Preis ist ungültig</shared-input-control-error>
|
||||
<shared-input-control-error error="pattern">Preis ist ungültig</shared-input-control-error>
|
||||
<shared-input-control-error error="min">Preis ist ungültig</shared-input-control-error>
|
||||
<shared-input-control-error error="max">Preis ist ungültig</shared-input-control-error>
|
||||
</shared-input-control>
|
||||
} @else {
|
||||
{{ priceValue$ | async | currency: 'EUR' : 'code' }}
|
||||
}
|
||||
|
||||
<ui-tooltip [warning]="true" xPosition="after" yPosition="below" [xOffset]="-55" [yOffset]="18" [closeable]="true" #giftCardTooltip>
|
||||
Tragen Sie hier den
|
||||
<br />
|
||||
Gutscheinbetrag ein.
|
||||
</ui-tooltip>
|
||||
<ui-tooltip [warning]="true" xPosition="after" yPosition="below" [xOffset]="-55" [yOffset]="18" [closeable]="true" #giftCardTooltip>
|
||||
Tragen Sie hier den
|
||||
<br />
|
||||
Gutscheinbetrag ein.
|
||||
</ui-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<ui-quantity-dropdown class="mt-2" [formControl]="quantityFormControl" [range]="maxSelectableQuantity$ | async"></ui-quantity-dropdown>
|
||||
<div class="pt-7">
|
||||
@if ((canAddResult$ | async)?.canAdd) {
|
||||
<input
|
||||
class="fancy-checkbox"
|
||||
[class.checked]="selectedFormControl?.value"
|
||||
[formControl]="selectedFormControl"
|
||||
type="checkbox"
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
<ng-template #priceTmpl>
|
||||
{{ priceValue$ | async | currency: 'EUR' : 'code' }}
|
||||
</ng-template>
|
||||
</div>
|
||||
<ui-quantity-dropdown class="mt-2" [formControl]="quantityFormControl" [range]="maxSelectableQuantity$ | async"></ui-quantity-dropdown>
|
||||
<div class="pt-7">
|
||||
<input
|
||||
*ngIf="(canAddResult$ | async)?.canAdd"
|
||||
class="fancy-checkbox"
|
||||
[class.checked]="selectedFormControl?.value"
|
||||
[formControl]="selectedFormControl"
|
||||
type="checkbox"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="canAddResult$ | async; let canAddResult">
|
||||
<span *ngIf="!canAddResult.canAdd" class="inline-block font-bold text-[#BE8100] mt-[14px] max-w-[19rem]">
|
||||
{{ canAddResult.message }}
|
||||
</span>
|
||||
</ng-container>
|
||||
@if (canAddResult$ | async; as canAddResult) {
|
||||
@if (!canAddResult.canAdd) {
|
||||
<span class="inline-block font-bold text-[#BE8100] mt-[14px] max-w-[19rem]">
|
||||
{{ canAddResult.message }}
|
||||
</span>
|
||||
}
|
||||
}
|
||||
|
||||
<span *ngIf="showMaxAvailableQuantity$ | async" class="font-bold text-[#BE8100] mt-[14px]">
|
||||
{{ (availability$ | async)?.inStock }} Exemplare sofort lieferbar
|
||||
</span>
|
||||
<span *ngIf="showNotAvailable$ | async" class="font-bold text-[#BE8100] mt-[14px]">Derzeit nicht bestellbar</span>
|
||||
@if (showMaxAvailableQuantity$ | async) {
|
||||
<span class="font-bold text-[#BE8100] mt-[14px]">
|
||||
{{ (availability$ | async)?.inStock }} Exemplare sofort lieferbar
|
||||
</span>
|
||||
}
|
||||
@if (showNotAvailable$ | async) {
|
||||
<span class="font-bold text-[#BE8100] mt-[14px]">Derzeit nicht bestellbar</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-row">
|
||||
<div class="w-16"></div>
|
||||
<div class="grow shared-purchase-options-list-item__availabilities"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-row">
|
||||
<div class="w-16"></div>
|
||||
<div class="grow shared-purchase-options-list-item__availabilities"></div>
|
||||
</div>
|
||||
|
||||
@@ -1,28 +1,37 @@
|
||||
<h3 class="text-center font-bold text-h3">Lieferung auswählen</h3>
|
||||
<p class="text-center font-2xl mt-4">Wie möchten Sie die Artikel erhalten?</p>
|
||||
<div class="rounded p-4 shadow-card mt-4 grid grid-flow-col gap-4 justify-center items-center relative">
|
||||
<ng-container *ngIf="!(isDownloadOnly$ | async)">
|
||||
<ng-container *ngIf="!(isGiftCardOnly$ | async)">
|
||||
<app-in-store-purchase-options-tile *ngIf="showOption('in-store')"></app-in-store-purchase-options-tile>
|
||||
<app-pickup-purchase-options-tile *ngIf="showOption('pickup')"></app-pickup-purchase-options-tile>
|
||||
</ng-container>
|
||||
<app-delivery-purchase-options-tile *ngIf="showOption('delivery')"></app-delivery-purchase-options-tile>
|
||||
</ng-container>
|
||||
@if (!(isDownloadOnly$ | async)) {
|
||||
@if (!(isGiftCardOnly$ | async)) {
|
||||
@if (showOption('in-store')) {
|
||||
<app-in-store-purchase-options-tile></app-in-store-purchase-options-tile>
|
||||
}
|
||||
@if (showOption('pickup')) {
|
||||
<app-pickup-purchase-options-tile></app-pickup-purchase-options-tile>
|
||||
}
|
||||
}
|
||||
@if (showOption('delivery')) {
|
||||
<app-delivery-purchase-options-tile></app-delivery-purchase-options-tile>
|
||||
}
|
||||
}
|
||||
|
||||
<ng-container *ngIf="hasDownload$ | async">
|
||||
<app-download-purchase-options-tile *ngIf="showOption('download')"></app-download-purchase-options-tile>
|
||||
</ng-container>
|
||||
@if (hasDownload$ | async) {
|
||||
@if (showOption('download')) {
|
||||
<app-download-purchase-options-tile></app-download-purchase-options-tile>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
<shared-purchase-options-list-header></shared-purchase-options-list-header>
|
||||
<div class="shared-purchase-options-modal__items -mx-4">
|
||||
<shared-purchase-options-list-item
|
||||
class="border-t border-gray-200 p-4 border-solid"
|
||||
*ngFor="let item of items$ | async; trackBy: itemTrackBy"
|
||||
[item]="item"
|
||||
></shared-purchase-options-list-item>
|
||||
@for (item of items$ | async; track itemTrackBy($index, item)) {
|
||||
<shared-purchase-options-list-item
|
||||
class="border-t border-gray-200 p-4 border-solid"
|
||||
[item]="item"
|
||||
></shared-purchase-options-list-item>
|
||||
}
|
||||
</div>
|
||||
<div class="text-center -mx-4 border-t border-gray-200 p-4 border-solid">
|
||||
<ng-container *ngIf="type === 'add'">
|
||||
@if (type === 'add') {
|
||||
<button type="button" class="isa-cta-button" [disabled]="!(canContinue$ | async) || saving" (click)="save('continue-shopping')">
|
||||
Weiter einkaufen
|
||||
</button>
|
||||
@@ -31,18 +40,18 @@
|
||||
class="ml-4 isa-cta-button isa-button-primary"
|
||||
[disabled]="!(canContinue$ | async) || saving"
|
||||
(click)="save('continue')"
|
||||
>
|
||||
>
|
||||
Fortfahren
|
||||
</button>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="type === 'update'">
|
||||
}
|
||||
@if (type === 'update') {
|
||||
<button
|
||||
type="button"
|
||||
class="ml-4 isa-cta-button isa-button-primary"
|
||||
[disabled]="!(canContinue$ | async) || saving"
|
||||
(click)="save('continue')"
|
||||
>
|
||||
>
|
||||
Fortfahren
|
||||
</button>
|
||||
</ng-container>
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { Component, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
|
||||
import { PurchaseOptionsStore } from '../store';
|
||||
import { BasePurchaseOptionDirective } from './base-purchase-option.directive';
|
||||
@@ -9,7 +9,7 @@ import { IconComponent } from '@shared/components/icon';
|
||||
templateUrl: 'download-purchase-options-tile.component.html',
|
||||
styleUrls: ['purchase-options-tile.component.css'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [CommonModule, IconComponent],
|
||||
imports: [IconComponent],
|
||||
})
|
||||
export class DownloadPurchaseOptionTileComponent extends BasePurchaseOptionDirective {
|
||||
constructor(
|
||||
|
||||
@@ -2,30 +2,31 @@
|
||||
|
||||
<hr />
|
||||
|
||||
<ng-container *ngIf="orderItem$ | async; let orderItem">
|
||||
@if (orderItem$ | async; as orderItem) {
|
||||
<div class="header">
|
||||
<img
|
||||
class="thumbnail"
|
||||
loading="lazy"
|
||||
*ngIf="orderItem?.product?.ean | productImage; let productImage"
|
||||
[src]="productImage"
|
||||
[alt]="orderItem?.product?.name"
|
||||
/>
|
||||
|
||||
@if (orderItem?.product?.ean | productImage; as productImage) {
|
||||
<img
|
||||
class="thumbnail"
|
||||
loading="lazy"
|
||||
[src]="productImage"
|
||||
[alt]="orderItem?.product?.name"
|
||||
/>
|
||||
}
|
||||
<div class="details">
|
||||
<div class="product-name">{{ orderItem.product?.name }}</div>
|
||||
<div *ngIf="orderItem.product?.format && orderItem.product.formatDetail" class="product-format">
|
||||
<img class="format-icon" [src]="'/assets/images/Icon_' + orderItem.product?.format + '.svg'" alt="format icon" />
|
||||
{{ orderItem.product.formatDetail }}
|
||||
</div>
|
||||
@if (orderItem.product?.format && orderItem.product.formatDetail) {
|
||||
<div class="product-format">
|
||||
<img class="format-icon" [src]="'/assets/images/Icon_' + orderItem.product?.format + '.svg'" alt="format icon" />
|
||||
{{ orderItem.product.formatDetail }}
|
||||
</div>
|
||||
}
|
||||
<div class="product-ean">
|
||||
{{ orderItem.product?.ean }}
|
||||
</div>
|
||||
<div class="quantity">{{ orderItem.quantity }}x</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="availabilities$ | async; let availabilities; else: showLoadingSpinner">
|
||||
@if (availabilities$ | async; as availabilities) {
|
||||
<div class="supplier-grid">
|
||||
<span></span>
|
||||
<span class="number">Bestand</span>
|
||||
@@ -34,53 +35,56 @@
|
||||
<span>vsl. Lieferdatum</span>
|
||||
<span class="number">Preis</span>
|
||||
<span></span>
|
||||
|
||||
<ng-container *ngFor="let availability of availabilities; let i = index">
|
||||
<ng-container *ngIf="availability">
|
||||
@for (availability of availabilities; track availability; let i = $index) {
|
||||
@if (availability) {
|
||||
<span class="first-cell">{{ availability.supplier | supplierName }}</span>
|
||||
<span class="number">{{ availability.qty || 0 }}</span>
|
||||
<span>{{ availability.ssc }}</span>
|
||||
<span>
|
||||
<ui-checkbox *ngIf="availability.supplier !== 'F'" [(ngModel)]="availability.isPrebooked"></ui-checkbox>
|
||||
@if (availability.supplier !== 'F') {
|
||||
<ui-checkbox [(ngModel)]="availability.isPrebooked"></ui-checkbox>
|
||||
}
|
||||
</span>
|
||||
<span>{{ availability.at | date: 'dd.MM.yy' }}</span>
|
||||
<span class="number">{{ availability.price?.value?.value | currency: 'EUR' : 'code' }}</span>
|
||||
<span>
|
||||
<ui-select-bullet
|
||||
*ngIf="availability.supplier !== 'F' || availability.qty > 0"
|
||||
[(ngModel)]="checkedSupplier"
|
||||
[value]="availability.supplier"
|
||||
(ngModelChange)="checked($event, availability)"
|
||||
></ui-select-bullet>
|
||||
@if (availability.supplier !== 'F' || availability.qty > 0) {
|
||||
<ui-select-bullet
|
||||
[(ngModel)]="checkedSupplier"
|
||||
[value]="availability.supplier"
|
||||
(ngModelChange)="checked($event, availability)"
|
||||
></ui-select-bullet>
|
||||
}
|
||||
</span>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="storeAvailabilityError$ | async">
|
||||
@if (storeAvailabilityError$ | async) {
|
||||
<div class="availability-error">Lieferantenbestand nicht verfügbar</div>
|
||||
<hr />
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="takeAwayAvailabilityError$ | async">
|
||||
}
|
||||
@if (takeAwayAvailabilityError$ | async) {
|
||||
<div class="availability-error">Filialbestand nicht verfügbar</div>
|
||||
<hr />
|
||||
</ng-container>
|
||||
|
||||
<div class="reason" *ngIf="showReasons$ | async">
|
||||
<button class="reason-dropdown" [uiOverlayTrigger]="statusDropdown" #dropdown="uiOverlayTrigger">
|
||||
{{ selectedReason || 'Warum wird nachbestellt?' }}
|
||||
<ui-icon [rotate]="dropdown.opened ? '270deg' : '90deg'" icon="arrow_head"></ui-icon>
|
||||
</button>
|
||||
<ui-dropdown #statusDropdown yPosition="above" xPosition="after" [xOffset]="8">
|
||||
<button uiDropdownItem *ngFor="let reason of reorderReasons$ | async" (click)="selectedReason = reason.value; dropdown.close()">
|
||||
{{ reason.value }}
|
||||
}
|
||||
@if (showReasons$ | async) {
|
||||
<div class="reason">
|
||||
<button class="reason-dropdown" [uiOverlayTrigger]="statusDropdown" #dropdown="uiOverlayTrigger">
|
||||
{{ selectedReason || 'Warum wird nachbestellt?' }}
|
||||
<ui-icon [rotate]="dropdown.opened ? '270deg' : '90deg'" icon="arrow_head"></ui-icon>
|
||||
</button>
|
||||
</ui-dropdown>
|
||||
|
||||
<span *ngIf="showReasonError$ | async" class="error">Bitte wählen Sie einen Grund für das nachbestellen</span>
|
||||
</div>
|
||||
|
||||
<ui-dropdown #statusDropdown yPosition="above" xPosition="after" [xOffset]="8">
|
||||
@for (reason of reorderReasons$ | async; track reason) {
|
||||
<button uiDropdownItem (click)="selectedReason = reason.value; dropdown.close()">
|
||||
{{ reason.value }}
|
||||
</button>
|
||||
}
|
||||
</ui-dropdown>
|
||||
@if (showReasonError$ | async) {
|
||||
<span class="error">Bitte wählen Sie einen Grund für das nachbestellen</span>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
<div class="actions">
|
||||
<button class="cta-not-available cta-action-secondary" [disabled]="ctaDisabled$ | async" (click)="notAvailable()">
|
||||
<ui-spinner [show]="ctaDisabled$ | async">Nicht lieferbar</ui-spinner>
|
||||
@@ -89,9 +93,7 @@
|
||||
<ui-spinner [show]="ctaDisabled$ | async">Bestellen</ui-spinner>
|
||||
</button>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-template #showLoadingSpinner>
|
||||
} @else {
|
||||
<ui-spinner class="load-spinner" [show]="true"></ui-spinner>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
<div class="reviews">
|
||||
<hr />
|
||||
<ng-container *ngFor="let review of reviews">
|
||||
@for (review of reviews; track review) {
|
||||
<div class="review">
|
||||
<div class="row">
|
||||
<ui-stars [rating]="review.rating"></ui-stars>
|
||||
@@ -16,21 +16,29 @@
|
||||
</div>
|
||||
<div class="row">
|
||||
<span>
|
||||
<span class="text" *ngIf="expandIds.indexOf(review.id) === -1">{{ review.text | substr: 150 }}</span>
|
||||
<span class="text" *ngIf="expandIds.indexOf(review.id) > -1">{{ review.text }}</span>
|
||||
@if (expandIds.indexOf(review.id) === -1) {
|
||||
<span class="text">{{ review.text | substr: 150 }}</span>
|
||||
}
|
||||
@if (expandIds.indexOf(review.id) > -1) {
|
||||
<span class="text">{{ review.text }}</span>
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
<div class="row right">
|
||||
<button *ngIf="expandIds.indexOf(review.id) === -1" class="btn-expand" (click)="expand(review.id)">
|
||||
Mehr
|
||||
<ui-icon icon="arrow"></ui-icon>
|
||||
</button>
|
||||
<button *ngIf="expandIds.indexOf(review.id) > -1" class="btn-collapse" (click)="expand(review.id)">
|
||||
<ui-icon icon="arrow" rotate="180deg"></ui-icon>
|
||||
Weniger
|
||||
</button>
|
||||
@if (expandIds.indexOf(review.id) === -1) {
|
||||
<button class="btn-expand" (click)="expand(review.id)">
|
||||
Mehr
|
||||
<ui-icon icon="arrow"></ui-icon>
|
||||
</button>
|
||||
}
|
||||
@if (expandIds.indexOf(review.id) > -1) {
|
||||
<button class="btn-collapse" (click)="expand(review.id)">
|
||||
<ui-icon icon="arrow" rotate="180deg"></ui-icon>
|
||||
Weniger
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
</ng-container>
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<div
|
||||
class="page-price-update-item__item-header flex flex-row w-full items-center justify-between bg-[rgba(0,128,121,0.15)] mb-px-2 px-5 h-[53px] rounded-t"
|
||||
>
|
||||
>
|
||||
<p class="page-price-update-item__item-instruction font-bold text-lg">{{ item?.task?.instruction }}</p>
|
||||
<p class="page-price-update-item__item-due-date text-p2">
|
||||
gültig ab
|
||||
@@ -10,13 +10,14 @@
|
||||
|
||||
<div class="page-price-update-item__item-card p-5 bg-white">
|
||||
<div class="page-price-update-item__item-thumbnail text-center mr-4 w-[47px] h-[73px]">
|
||||
<img
|
||||
class="page-price-update-item__item-image w-[47px] max-h-[73px]"
|
||||
loading="lazy"
|
||||
*ngIf="item?.product?.ean | productImage; let productImage"
|
||||
[src]="productImage"
|
||||
[alt]="item?.product?.name"
|
||||
/>
|
||||
@if (item?.product?.ean | productImage; as productImage) {
|
||||
<img
|
||||
class="page-price-update-item__item-image w-[47px] max-h-[73px]"
|
||||
loading="lazy"
|
||||
[src]="productImage"
|
||||
[alt]="item?.product?.name"
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="page-price-update-item__item-details">
|
||||
@@ -31,28 +32,33 @@
|
||||
[class.text-md]="item?.product?.name?.length >= 50"
|
||||
[class.text-p3]="item?.product?.name?.length >= 60"
|
||||
[class.text-xs]="item?.product?.name?.length >= 100"
|
||||
>
|
||||
>
|
||||
{{ item?.product?.name }}
|
||||
</div>
|
||||
|
||||
<div class="page-price-update-item__item-format">
|
||||
<div *ngIf="item?.product?.format && item?.product?.formatDetail" class="font-bold flex flex-row">
|
||||
<img
|
||||
class="mr-3"
|
||||
*ngIf="item?.product?.format !== '--'"
|
||||
loading="lazy"
|
||||
src="assets/images/Icon_{{ item?.product?.format }}.svg"
|
||||
[alt]="item?.product?.formatDetail"
|
||||
/>
|
||||
{{ environment.isTablet() ? (item?.product?.formatDetail | substr: 25) : item?.product?.formatDetail }}
|
||||
</div>
|
||||
@if (item?.product?.format && item?.product?.formatDetail) {
|
||||
<div class="font-bold flex flex-row">
|
||||
@if (item?.product?.format !== '--') {
|
||||
<img
|
||||
class="mr-3"
|
||||
loading="lazy"
|
||||
src="assets/images/Icon_{{ item?.product?.format }}.svg"
|
||||
[alt]="item?.product?.formatDetail"
|
||||
/>
|
||||
}
|
||||
{{ environment.isTablet() ? (item?.product?.formatDetail | substr: 25) : item?.product?.formatDetail }}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="page-price-update-item__item-misc">
|
||||
{{ environment.isTablet() ? (item?.product?.manufacturer | substr: 18) : item?.product?.manufacturer }} | {{ item?.product?.ean }}
|
||||
<br />
|
||||
{{ item?.product?.volume }}
|
||||
<span *ngIf="item?.product?.volume && item?.product?.publicationDate">|</span>
|
||||
@if (item?.product?.volume && item?.product?.publicationDate) {
|
||||
<span>|</span>
|
||||
}
|
||||
{{ publicationDate }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -69,18 +75,21 @@
|
||||
</div>
|
||||
|
||||
<div class="page-price-update-item__item-select-bullet">
|
||||
<input *ngIf="isSelectable" [ngModel]="selected" (ngModelChange)="setSelected()" class="isa-select-bullet" type="checkbox" />
|
||||
@if (isSelectable) {
|
||||
<input [ngModel]="selected" (ngModelChange)="setSelected()" class="isa-select-bullet" type="checkbox" />
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="page-price-update-item__item-stock flex flex-row font-bold">
|
||||
<ui-icon class="mt-px-2 mr-1" icon="home" size="1em"></ui-icon>
|
||||
<span
|
||||
*ngIf="inStock$ | async; let stock"
|
||||
[class.skeleton]="stock?.inStock === undefined"
|
||||
class="min-w-[1rem] text-right inline-block"
|
||||
>
|
||||
{{ stock?.inStock }}
|
||||
</span>
|
||||
@if (inStock$ | async; as stock) {
|
||||
<span
|
||||
[class.skeleton]="stock?.inStock === undefined"
|
||||
class="min-w-[1rem] text-right inline-block"
|
||||
>
|
||||
{{ stock?.inStock }}
|
||||
</span>
|
||||
}
|
||||
x
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,20 +4,26 @@
|
||||
(click)="print()"
|
||||
type="button"
|
||||
class="page-price-update-list__print-cta text-lg font-bold text-[#F70400] pr-5 mb-3"
|
||||
>
|
||||
>
|
||||
Drucken
|
||||
</button>
|
||||
<div class="flex flex-row items-center justify-end">
|
||||
<div *ngIf="getSelectableItems().length > 0" class="text-[#0556B4] font-bold text-p3 mr-5">
|
||||
<ng-container *ngIf="selectedItemUids$ | async; let selectedItems">
|
||||
<button class="page-price-update-list__cta-unselect-all" *ngIf="selectedItems?.length > 0" type="button" (click)="unselectAll()">
|
||||
Alle entfernen ({{ selectedItems?.length }})
|
||||
</button>
|
||||
<button class="page-price-update-list__cta-select-all" type="button" (click)="selectAll()" *ngIf="selectedItems?.length === 0">
|
||||
Alle auswählen ({{ getSelectableItems().length }})
|
||||
</button>
|
||||
</ng-container>
|
||||
</div>
|
||||
@if (getSelectableItems().length > 0) {
|
||||
<div class="text-[#0556B4] font-bold text-p3 mr-5">
|
||||
@if (selectedItemUids$ | async; as selectedItems) {
|
||||
@if (selectedItems?.length > 0) {
|
||||
<button class="page-price-update-list__cta-unselect-all" type="button" (click)="unselectAll()">
|
||||
Alle entfernen ({{ selectedItems?.length }})
|
||||
</button>
|
||||
}
|
||||
@if (selectedItems?.length === 0) {
|
||||
<button class="page-price-update-list__cta-select-all" type="button" (click)="selectAll()">
|
||||
Alle auswählen ({{ getSelectableItems().length }})
|
||||
</button>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
<div class="page-price-update-list__items-count inline-flex flex-row items-center pr-5 text-p3">
|
||||
{{ items?.length ?? 0 }}
|
||||
Titel
|
||||
@@ -32,38 +38,41 @@
|
||||
<div class="items scroll-bar">
|
||||
@for (item of items; track item.uId; let first = $first) {
|
||||
@defer (on viewport) {
|
||||
<page-price-update-item [item]="item" [selected]="isSelected(item)" [class.mt-px-10]="!first"></page-price-update-item>
|
||||
<page-price-update-item [item]="item" [selected]="isSelected(item)" [class.mt-px-10]="!first"></page-price-update-item>
|
||||
} @placeholder {
|
||||
<page-price-update-item-loader></page-price-update-item-loader>
|
||||
}
|
||||
<page-price-update-item-loader></page-price-update-item-loader>
|
||||
}
|
||||
}
|
||||
|
||||
<page-price-update-item-loader *ngIf="fetching"></page-price-update-item-loader>
|
||||
<div class="h-28"></div>
|
||||
@if (fetching) {
|
||||
<page-price-update-item-loader></page-price-update-item-loader>
|
||||
}
|
||||
<div class="h-28"></div>
|
||||
</div>
|
||||
|
||||
<!-- <cdk-virtual-scroll-viewport #scrollContainer [itemSize]="267" minBufferPx="1200" maxBufferPx="1200" class="scroll-bar">
|
||||
<page-price-update-item
|
||||
*cdkVirtualFor="let item of items; let first; trackBy: trackByFn"
|
||||
[item]="item"
|
||||
[selected]="isSelected(item)"
|
||||
[class.mt-px-10]="!first"
|
||||
></page-price-update-item>
|
||||
<page-price-update-item
|
||||
*cdkVirtualFor="let item of items; let first; trackBy: trackByFn"
|
||||
[item]="item"
|
||||
[selected]="isSelected(item)"
|
||||
[class.mt-px-10]="!first"
|
||||
></page-price-update-item>
|
||||
|
||||
<page-price-update-item-loader *ngIf="fetching"> </page-price-update-item-loader>
|
||||
<page-price-update-item-loader *ngIf="fetching"> </page-price-update-item-loader>
|
||||
|
||||
<div class="h-28"></div>
|
||||
<div class="h-28"></div>
|
||||
</cdk-virtual-scroll-viewport> -->
|
||||
|
||||
<div class="page-price-update-list__action-wrapper">
|
||||
<button
|
||||
[@cta]
|
||||
*ngIf="!fetching"
|
||||
[disabled]="(selectedItemUids$ | async).length === 0 || (loading$ | async)"
|
||||
class="page-price-update-list__complete-items isa-button isa-cta-button isa-button-primary px-11"
|
||||
type="button"
|
||||
(click)="onComplete()"
|
||||
>
|
||||
<ui-spinner [show]="loading$ | async">Erledigt</ui-spinner>
|
||||
</button>
|
||||
@if (!fetching) {
|
||||
<button
|
||||
[@cta]
|
||||
[disabled]="(selectedItemUids$ | async).length === 0 || (loading$ | async)"
|
||||
class="page-price-update-list__complete-items isa-button isa-cta-button isa-button-primary px-11"
|
||||
type="button"
|
||||
(click)="onComplete()"
|
||||
>
|
||||
<ui-spinner [show]="loading$ | async">Erledigt</ui-spinner>
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -5,17 +5,20 @@
|
||||
id="asortment-filter-button"
|
||||
class="absolute right-0 top-0 h-14 rounded px-5 text-lg bg-cadet-blue flex flex-row flex-nowrap items-center justify-center"
|
||||
type="button"
|
||||
>
|
||||
>
|
||||
<shared-icon class="mr-2" icon="filter-variant"></shared-icon>
|
||||
Filter
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<page-price-update-list
|
||||
*ngIf="showList$ | async; else noResults"
|
||||
[items]="store.items$ | async"
|
||||
[fetching]="store.fetching$ | async"
|
||||
></page-price-update-list>
|
||||
@if (showList$ | async) {
|
||||
<page-price-update-list
|
||||
[items]="store.items$ | async"
|
||||
[fetching]="store.fetching$ | async"
|
||||
></page-price-update-list>
|
||||
} @else {
|
||||
<div class="bg-white text-h3 text-center pt-10 font-bold rounded-b h-[calc(100vh_-_370px)]">Keine Preisänderungen vorhanden.</div>
|
||||
}
|
||||
|
||||
<shell-filter-overlay #filterOverlay class="relative">
|
||||
<div class="relative">
|
||||
@@ -26,22 +29,23 @@
|
||||
|
||||
<h3 class="text-3xl text-center font-bold mt-8">Filter</h3>
|
||||
|
||||
<ui-filter
|
||||
*ngIf="filterOverlay.isOpen"
|
||||
#filter
|
||||
class="mx-4"
|
||||
[filter]="store.pendingFilter$ | async"
|
||||
(search)="applyFilter()"
|
||||
[loading]="store.fetching$ | async"
|
||||
[hint]="hint$ | async"
|
||||
></ui-filter>
|
||||
@if (filterOverlay.isOpen) {
|
||||
<ui-filter
|
||||
#filter
|
||||
class="mx-4"
|
||||
[filter]="store.pendingFilter$ | async"
|
||||
(search)="applyFilter()"
|
||||
[loading]="store.fetching$ | async"
|
||||
[hint]="hint$ | async"
|
||||
></ui-filter>
|
||||
}
|
||||
|
||||
<div class="absolute bottom-8 left-0 right-0 grid grid-flow-col gap-4 justify-center">
|
||||
<button
|
||||
type="button"
|
||||
class="px-6 py-4 font-bold bg-white text-brand border-2 border-solid border-brand rounded-full"
|
||||
(click)="store.resetPendingFilter()"
|
||||
>
|
||||
>
|
||||
Filter zurücksetzen
|
||||
</button>
|
||||
<button
|
||||
@@ -49,12 +53,9 @@
|
||||
class="px-6 py-4 font-bold bg-brand text-white border-2 border-solid border-brand rounded-full disabled:bg-cadet-blue disabled:cursor-progress disabled:border-cadet-blue"
|
||||
(click)="applyFilter()"
|
||||
[disabled]="store.fetching$ | async"
|
||||
>
|
||||
>
|
||||
<ui-spinner [show]="store.fetching$ | async">Filter anwenden</ui-spinner>
|
||||
</button>
|
||||
</div>
|
||||
</shell-filter-overlay>
|
||||
|
||||
<ng-template #noResults>
|
||||
<div class="bg-white text-h3 text-center pt-10 font-bold rounded-b h-[calc(100vh_-_370px)]">Keine Preisänderungen vorhanden.</div>
|
||||
</ng-template>
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
<ng-container *ngFor="let line of lines">
|
||||
<ng-container [ngSwitch]="line | lineType">
|
||||
<ng-container *ngSwitchCase="'reihe'">
|
||||
<page-article-details-text-link *ngFor="let reihe of getReihen(line)" [route]="reihe | reiheRoute">
|
||||
{{ reihe }}
|
||||
</page-article-details-text-link>
|
||||
@for (line of lines; track line) {
|
||||
@switch (line | lineType) {
|
||||
@case ('reihe') {
|
||||
@for (reihe of getReihen(line); track reihe) {
|
||||
<page-article-details-text-link [route]="reihe | reiheRoute">
|
||||
{{ reihe }}
|
||||
</page-article-details-text-link>
|
||||
}
|
||||
<br />
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchDefault>
|
||||
}
|
||||
@default {
|
||||
{{ line }}
|
||||
<br />
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Component, ChangeDetectionStrategy, Input } from '@angular/core';
|
||||
import { TextDTO } from '@generated/swagger/cat-search-api';
|
||||
import { ArticleDetailsTextLinkComponent } from './article-details-text-link.component';
|
||||
import { NgFor, NgSwitch, NgSwitchCase, NgSwitchDefault } from '@angular/common';
|
||||
|
||||
import { LineTypePipe } from './line-type.pipe';
|
||||
import { ReiheRoutePipe } from './reihe-route.pipe';
|
||||
|
||||
@@ -13,13 +13,9 @@ import { ReiheRoutePipe } from './reihe-route.pipe';
|
||||
host: { class: 'page-article-details-text' },
|
||||
imports: [
|
||||
ArticleDetailsTextLinkComponent,
|
||||
NgFor,
|
||||
NgSwitch,
|
||||
NgSwitchCase,
|
||||
NgSwitchDefault,
|
||||
LineTypePipe,
|
||||
ReiheRoutePipe,
|
||||
],
|
||||
ReiheRoutePipe
|
||||
],
|
||||
})
|
||||
export class ArticleDetailsTextComponent {
|
||||
@Input()
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,37 +1,39 @@
|
||||
<ng-container *ngIf="store.item$ | async; let item">
|
||||
@if (store.item$ | async; as item) {
|
||||
<button
|
||||
class="h-[3.75rem] shadow-[0_-2px_24px_0_#dce2e9] flex flex-row justify-center items-center w-full text-xl bg-white text-[#0556B4] font-bold border-none outline-none rounded-t"
|
||||
(click)="close.emit()"
|
||||
>
|
||||
>
|
||||
{{ item?.product?.name }}
|
||||
</button>
|
||||
<h1>Empfehlungen für Sie</h1>
|
||||
<p>Neben dem Titel "{{ item.product?.name }}" gibt es noch andere Artikel, die Sie interessieren könnten.</p>
|
||||
|
||||
<div class="articles">
|
||||
<span class="label mb-2">
|
||||
<ui-icon icon="recommendation" size="20px"></ui-icon>
|
||||
Artikel
|
||||
</span>
|
||||
|
||||
<ng-container *ngIf="store.recommendations$ | async; let recommendations">
|
||||
<span *ngIf="recommendations.length === 0" class="empty-message">Keine Empfehlungen verfügbar</span>
|
||||
|
||||
<ui-slider *ngIf="recommendations.length > 0" [scrollDistance]="210">
|
||||
<a
|
||||
class="article"
|
||||
*ngFor="let recommendation of store.recommendations$ | async"
|
||||
[routerLink]="getDetailsPath(recommendation.product.ean)"
|
||||
[queryParams]="{ main_qs: recommendation.product.ean, filter_format: '' }"
|
||||
(click)="close.emit()"
|
||||
>
|
||||
<img [src]="recommendation.product?.ean | productImage: 195 : 315 : true" alt="product-image" />
|
||||
<div class="flex flex-col">
|
||||
<span class="format">{{ recommendation.product?.formatDetail }}</span>
|
||||
<span class="price">{{ recommendation.catalogAvailability?.price?.value?.value | currency: ' ' }} EUR</span>
|
||||
</div>
|
||||
</a>
|
||||
</ui-slider>
|
||||
</ng-container>
|
||||
@if (store.recommendations$ | async; as recommendations) {
|
||||
@if (recommendations.length === 0) {
|
||||
<span class="empty-message">Keine Empfehlungen verfügbar</span>
|
||||
}
|
||||
@if (recommendations.length > 0) {
|
||||
<ui-slider [scrollDistance]="210">
|
||||
@for (recommendation of store.recommendations$ | async; track recommendation) {
|
||||
<a
|
||||
class="article"
|
||||
[routerLink]="getDetailsPath(recommendation.product.ean)"
|
||||
[queryParams]="{ main_qs: recommendation.product.ean, filter_format: '' }"
|
||||
(click)="close.emit()"
|
||||
>
|
||||
<img [src]="recommendation.product?.ean | productImage: 195 : 315 : true" alt="product-image" />
|
||||
<div class="flex flex-col">
|
||||
<span class="format">{{ recommendation.product?.formatDetail }}</span>
|
||||
<span class="price">{{ recommendation.catalogAvailability?.price?.value?.value | currency: ' ' }} EUR</span>
|
||||
</div>
|
||||
</a>
|
||||
}
|
||||
</ui-slider>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</ng-container>
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
<div class="hidden desktop-large:block" [class.show-filter]="showFilter">
|
||||
<ng-container *ngIf="filter$ | async; let filter">
|
||||
@if (filter$ | async; as filter) {
|
||||
<div class="catalog-search-filter-content">
|
||||
<div class="w-full flex flex-row justify-end items-center">
|
||||
<button (click)="clearFilter(filter)" class="text-[#0556B4] p-4">Alle Filter entfernen</button>
|
||||
<a
|
||||
*ngIf="showFilterClose$ | async"
|
||||
class="text-black p-4 outline-none border-none bg-transparent"
|
||||
[routerLink]="closeFilterRoute"
|
||||
(click)="showFilter = false"
|
||||
queryParamsHandling="preserve"
|
||||
>
|
||||
<shared-icon icon="close" [size]="25"></shared-icon>
|
||||
</a>
|
||||
@if (showFilterClose$ | async) {
|
||||
<a
|
||||
class="text-black p-4 outline-none border-none bg-transparent"
|
||||
[routerLink]="closeFilterRoute"
|
||||
(click)="showFilter = false"
|
||||
queryParamsHandling="preserve"
|
||||
>
|
||||
<shared-icon icon="close" [size]="25"></shared-icon>
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="catalog-search-filter-content-main -mt-14 desktop-small:-mt-8 desktop-large:-mt-12">
|
||||
<h1 class="text-h3 text-[1.625rem] font-bold text-center pt-6 pb-10">Filter</h1>
|
||||
<shared-filter
|
||||
@@ -24,16 +24,14 @@
|
||||
[scanner]="true"
|
||||
></shared-filter>
|
||||
</div>
|
||||
|
||||
<div class="cta-wrapper">
|
||||
<button class="cta-reset-filter" (click)="resetFilter(filter)" [disabled]="fetching$ | async">Filter zurücksetzen</button>
|
||||
|
||||
<button class="cta-apply-filter" (click)="applyFilter(filter)" [disabled]="(fetching$ | async) || !hasSelectedOptions(filter)">
|
||||
<ui-spinner [show]="fetching$ | async">Filter anwenden</ui-spinner>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
}
|
||||
</div>
|
||||
<div class="desktop-large:hidden" [class.hidden]="showFilter">
|
||||
<page-article-search-main (showFilter)="showFilter = true"></page-article-search-main>
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
<div class="bg-white rounded py-10 px-4 text-center shadow-[0_-2px_24px_0_#dce2e9] h-full">
|
||||
<h1 class="text-h3 text-[1.625rem] font-bold mb-[0.375rem]">Artikelsuche</h1>
|
||||
<p class="text-lg mb-10">Welchen Artikel suchen Sie?</p>
|
||||
<ng-container *ngIf="filter$ | async; let filter">
|
||||
<shared-filter-filter-group-main
|
||||
class="mb-8 w-full"
|
||||
*ngIf="!(isDesktop$ | async)"
|
||||
[inputGroup]="filter?.filter | group: 'main'"
|
||||
></shared-filter-filter-group-main>
|
||||
@if (filter$ | async; as filter) {
|
||||
@if (!(isDesktop$ | async)) {
|
||||
<shared-filter-filter-group-main
|
||||
class="mb-8 w-full"
|
||||
[inputGroup]="filter?.filter | group: 'main'"
|
||||
></shared-filter-filter-group-main>
|
||||
}
|
||||
<div class="flex flex-row px-12 justify-center desktop-large:px-0">
|
||||
<shared-filter-input-group-main
|
||||
class="block w-full mr-3 desktop-large:mx-auto"
|
||||
@@ -17,38 +18,40 @@
|
||||
[showDescription]="false"
|
||||
[scanner]="true"
|
||||
></shared-filter-input-group-main>
|
||||
<button
|
||||
type="button"
|
||||
*ngIf="!(isDesktop$ | async)"
|
||||
(click)="showFilter.emit()"
|
||||
class="page-search-main__filter w-[6.75rem] h-14 rounded font-bold px-5 mb-4 text-lg bg-[#AEB7C1] flex flex-row flex-nowrap items-center justify-center"
|
||||
[class.active]="hasFilter$ | async"
|
||||
>
|
||||
<shared-icon class="mr-2" icon="filter-variant"></shared-icon>
|
||||
Filter
|
||||
</button>
|
||||
@if (!(isDesktop$ | async)) {
|
||||
<button
|
||||
type="button"
|
||||
(click)="showFilter.emit()"
|
||||
class="page-search-main__filter w-[6.75rem] h-14 rounded font-bold px-5 mb-4 text-lg bg-[#AEB7C1] flex flex-row flex-nowrap items-center justify-center"
|
||||
[class.active]="hasFilter$ | async"
|
||||
>
|
||||
<shared-icon class="mr-2" icon="filter-variant"></shared-icon>
|
||||
Filter
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col items-start ml-12 desktop-large:ml-8 py-6 bg-white overflow-hidden h-[calc(100%-13.5rem)]">
|
||||
<h3 class="text-p3 font-bold mb-3">Deine letzten Suchanfragen</h3>
|
||||
<ul class="flex flex-col justify-start overflow-hidden overflow-y-scroll items-start m-0 p-0 bg-white w-full">
|
||||
<li class="list-none pb-3" *ngFor="let recentQuery of history$ | async">
|
||||
<button
|
||||
class="flex flex-row items-center outline-none border-none bg-white text-black text-p2 m-0 p-0"
|
||||
(click)="setQueryHistory(filter, recentQuery.friendlyName)"
|
||||
matomoClickCategory="search"
|
||||
matomoClickAction="click"
|
||||
matomoClickName="recent-search"
|
||||
>
|
||||
<shared-icon
|
||||
class="flex w-8 h-8 justify-center items-center mr-3 rounded-full text-black bg-[#edeff0]"
|
||||
icon="magnify"
|
||||
[size]="20"
|
||||
></shared-icon>
|
||||
<p class="m-0 p-0 whitespace-nowrap overflow-hidden overflow-ellipsis max-w-[25rem]">{{ recentQuery.friendlyName }}</p>
|
||||
</button>
|
||||
</li>
|
||||
@for (recentQuery of history$ | async; track recentQuery) {
|
||||
<li class="list-none pb-3">
|
||||
<button
|
||||
class="flex flex-row items-center outline-none border-none bg-white text-black text-p2 m-0 p-0"
|
||||
(click)="setQueryHistory(filter, recentQuery.friendlyName)"
|
||||
matomoClickCategory="search"
|
||||
matomoClickAction="click"
|
||||
matomoClickName="recent-search"
|
||||
>
|
||||
<shared-icon
|
||||
class="flex w-8 h-8 justify-center items-center mr-3 rounded-full text-black bg-[#edeff0]"
|
||||
icon="magnify"
|
||||
[size]="20"
|
||||
></shared-icon>
|
||||
<p class="m-0 p-0 whitespace-nowrap overflow-hidden overflow-ellipsis max-w-[25rem]">{{ recentQuery.friendlyName }}</p>
|
||||
</button>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</ng-container>
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<p class="can-add-message" *ngIf="ref.data.canAddMessage">{{ ref.data.canAddMessage }}</p>
|
||||
@if (ref.data.canAddMessage) {
|
||||
<p class="can-add-message">{{ ref.data.canAddMessage }}</p>
|
||||
}
|
||||
|
||||
<div class="actions">
|
||||
<button (click)="continue()" class="cta cta-action-secondary">Weiter Einkaufen</button>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<ng-container *ngIf="!primaryOutletActive; else primaryOutlet">
|
||||
@if (!primaryOutletActive) {
|
||||
<div class="bg-ucla-blue rounded w-[4.375rem] h-[5.625rem] animate-[load_1s_linear_infinite]"></div>
|
||||
<div class="flex flex-col flex-grow">
|
||||
<div class="h-4 bg-ucla-blue ml-4 mb-2 w-[7.8125rem] animate-[load_1s_linear_infinite]"></div>
|
||||
@@ -16,9 +16,7 @@
|
||||
<div class="h-4 bg-ucla-blue ml-4 w-[7.8125rem] animate-[load_1s_linear_infinite]"></div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-template #primaryOutlet>
|
||||
} @else {
|
||||
<div class="bg-ucla-blue rounded w-[3rem] h-[4.125rem] animate-[load_1s_linear_infinite]"></div>
|
||||
<div class="flex flex-col ml-4 w-[36.6%]">
|
||||
<div class="h-4 bg-ucla-blue mb-2 w-[8.8125rem] animate-[load_1s_linear_infinite]"></div>
|
||||
@@ -35,4 +33,5 @@
|
||||
<div class="h-4 bg-ucla-blue mb-2 w-[8.8125rem] animate-[load_1s_linear_infinite]"></div>
|
||||
<div class="h-4 bg-ucla-blue w-[8.8125rem] animate-[load_1s_linear_infinite]"></div>
|
||||
</div>
|
||||
</ng-template>
|
||||
}
|
||||
|
||||
|
||||
@@ -2,32 +2,34 @@
|
||||
class="page-search-result-item__item-card hover p-5 desktop-small:px-4 desktop-small:py-[0.625rem] h-[13.25rem] desktop-small:h-[11.3125rem] bg-white border border-solid border-transparent rounded"
|
||||
[class.page-search-result-item__item-card-primary]="primaryOutletActive"
|
||||
[class.active]="isActive"
|
||||
>
|
||||
>
|
||||
<div class="page-search-result-item__item-thumbnail text-center mr-4 w-[3.125rem] h-[4.9375rem]">
|
||||
<img
|
||||
class="page-search-result-item__item-image w-[3.125rem] max-h-[4.9375rem]"
|
||||
loading="lazy"
|
||||
*ngIf="item?.imageId | thumbnailUrl; let thumbnailUrl"
|
||||
[src]="thumbnailUrl"
|
||||
[alt]="item?.product?.name"
|
||||
/>
|
||||
@if (item?.imageId | thumbnailUrl; as thumbnailUrl) {
|
||||
<img
|
||||
class="page-search-result-item__item-image w-[3.125rem] max-h-[4.9375rem]"
|
||||
loading="lazy"
|
||||
[src]="thumbnailUrl"
|
||||
[alt]="item?.product?.name"
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="page-search-result-item__item-grid-container"
|
||||
[class.page-search-result-item__item-grid-container-primary]="primaryOutletActive"
|
||||
>
|
||||
>
|
||||
<div
|
||||
class="page-search-result-item__item-contributors desktop-small:text-p3 font-bold text-[#0556B4] text-ellipsis overflow-hidden max-w-[24rem] whitespace-nowrap"
|
||||
>
|
||||
<a
|
||||
*ngFor="let contributor of contributors; let last = last"
|
||||
[routerLink]="resultsPath"
|
||||
[queryParams]="{ main_qs: contributor, main_author: 'author' }"
|
||||
(click)="$event?.stopPropagation()"
|
||||
>
|
||||
{{ contributor }}{{ last ? '' : ';' }}
|
||||
</a>
|
||||
@for (contributor of contributors; track contributor; let last = $last) {
|
||||
<a
|
||||
[routerLink]="resultsPath"
|
||||
[queryParams]="{ main_qs: contributor, main_author: 'author' }"
|
||||
(click)="$event?.stopPropagation()"
|
||||
>
|
||||
{{ contributor }}{{ last ? '' : ';' }}
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div
|
||||
@@ -37,21 +39,24 @@
|
||||
[class.text-md]="item?.product?.name?.length >= 50 && isTablet"
|
||||
[class.text-p3]="item?.product?.name?.length >= 60 || !isTablet"
|
||||
[class.text-xs]="item?.product?.name?.length >= 100"
|
||||
>
|
||||
>
|
||||
{{ item?.product?.name }}
|
||||
</div>
|
||||
|
||||
<div class="page-search-result-item__item-format desktop-small:text-p3">
|
||||
<div *ngIf="item?.product?.format && item?.product?.formatDetail" class="font-bold flex flex-row">
|
||||
<img
|
||||
class="mr-3"
|
||||
*ngIf="item?.product?.format !== '--'"
|
||||
loading="lazy"
|
||||
src="assets/images/Icon_{{ item?.product?.format }}.svg"
|
||||
[alt]="item?.product?.formatDetail"
|
||||
/>
|
||||
{{ item?.product?.formatDetail | substr: 30 }}
|
||||
</div>
|
||||
@if (item?.product?.format && item?.product?.formatDetail) {
|
||||
<div class="font-bold flex flex-row">
|
||||
@if (item?.product?.format !== '--') {
|
||||
<img
|
||||
class="mr-3"
|
||||
loading="lazy"
|
||||
src="assets/images/Icon_{{ item?.product?.format }}.svg"
|
||||
[alt]="item?.product?.formatDetail"
|
||||
/>
|
||||
}
|
||||
{{ item?.product?.formatDetail | substr: 30 }}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="page-search-result-item__item-manufacturer desktop-small:text-p3">
|
||||
@@ -60,31 +65,34 @@
|
||||
|
||||
<div class="page-search-result-item__item-misc desktop-small:text-p3">
|
||||
{{ item?.product?.volume }}
|
||||
<span *ngIf="item?.product?.volume && item?.product?.publicationDate">|</span>
|
||||
@if (item?.product?.volume && item?.product?.publicationDate) {
|
||||
<span>|</span>
|
||||
}
|
||||
{{ publicationDate }}
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="page-search-result-item__item-price desktop-small:text-p3 font-bold justify-self-end"
|
||||
[class.page-search-result-item__item-price-primary]="primaryOutletActive"
|
||||
>
|
||||
>
|
||||
{{ item?.catalogAvailability?.price?.value?.value | currency: 'EUR' : 'code' }}
|
||||
</div>
|
||||
|
||||
<div class="page-search-result-item__item-select-bullet justify-self-end">
|
||||
<input
|
||||
*ngIf="selectable"
|
||||
(click)="$event.stopPropagation()"
|
||||
[ngModel]="selected"
|
||||
@if (selectable) {
|
||||
<input
|
||||
(click)="$event.stopPropagation()"
|
||||
[ngModel]="selected"
|
||||
(ngModelChange)="
|
||||
setSelected();
|
||||
tracker.trackEvent({ category: 'Trefferliste', action: 'select', name: item.product.name, value: selected ? 1 : 0 })
|
||||
"
|
||||
class="isa-select-bullet"
|
||||
type="checkbox"
|
||||
matomoTracker
|
||||
#tracker="matomo"
|
||||
/>
|
||||
class="isa-select-bullet"
|
||||
type="checkbox"
|
||||
matomoTracker
|
||||
#tracker="matomo"
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
|
||||
<button
|
||||
@@ -94,20 +102,21 @@
|
||||
[overlayTriggerDisabled]="!(stockTooltipText$ | async)"
|
||||
type="button"
|
||||
(click)="$event.stopPropagation(); $event.preventDefault(); showTooltip()"
|
||||
>
|
||||
>
|
||||
<ui-icon class="mr-[0.125rem] -mt-[0.275rem]" icon="home" size="1rem"></ui-icon>
|
||||
<ng-container *ngIf="isOrderBranch$ | async">
|
||||
<span
|
||||
*ngIf="inStock$ | async; let stock"
|
||||
[class.skeleton]="stock.inStock === undefined"
|
||||
class="min-w-[0.75rem] text-right inline-block"
|
||||
>
|
||||
{{ stock?.inStock }}
|
||||
</span>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!(isOrderBranch$ | async)">
|
||||
@if (isOrderBranch$ | async) {
|
||||
@if (inStock$ | async; as stock) {
|
||||
<span
|
||||
[class.skeleton]="stock.inStock === undefined"
|
||||
class="min-w-[0.75rem] text-right inline-block"
|
||||
>
|
||||
{{ stock?.inStock }}
|
||||
</span>
|
||||
}
|
||||
}
|
||||
@if (!(isOrderBranch$ | async)) {
|
||||
<span class="min-w-[1rem] text-center inline-block">-</span>
|
||||
</ng-container>
|
||||
}
|
||||
<span>x</span>
|
||||
</button>
|
||||
<ui-tooltip #tooltip yPosition="above" xPosition="after" [yOffset]="-12" [closeable]="true">
|
||||
@@ -117,14 +126,14 @@
|
||||
<div
|
||||
class="page-search-result-item__item-ssc desktop-small:text-p3 w-full text-right overflow-hidden text-ellipsis whitespace-nowrap"
|
||||
[class.page-search-result-item__item-ssc-primary]="primaryOutletActive"
|
||||
>
|
||||
<ng-container *ngIf="ssc$ | async; let ssc">
|
||||
>
|
||||
@if (ssc$ | async; as ssc) {
|
||||
<div class="hidden" [class.page-search-result-item__item-ssc-tooltip]="primaryOutletActive">
|
||||
{{ ssc?.ssc }} - {{ ssc?.sscText }}
|
||||
</div>
|
||||
<strong>{{ ssc?.ssc }}</strong>
|
||||
- {{ ssc?.sscText }}
|
||||
</ng-container>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,50 +2,53 @@
|
||||
class="page-search-results__header bg-background-liste flex items-end justify-between"
|
||||
[class.pb-4]="!(primaryOutletActive$ | async)"
|
||||
[class.flex-col]="!(primaryOutletActive$ | async)"
|
||||
>
|
||||
>
|
||||
<div class="flex flex-row w-full desktop:w-min" [class.desktop-large:w-full]="!(primaryOutletActive$ | async)">
|
||||
<shared-filter-input-group-main
|
||||
*ngIf="filter$ | async; let filter"
|
||||
class="block mr-3 w-full desktop:w-[23.5rem]"
|
||||
[class.desktop-large:w-full]="!(primaryOutletActive$ | async)"
|
||||
[hint]="searchboxHint$ | async"
|
||||
[loading]="fetching$ | async"
|
||||
[inputGroup]="filter?.input | group: 'main'"
|
||||
(search)="search({ filter, clear: true })"
|
||||
[showDescription]="false"
|
||||
[scanner]="true"
|
||||
></shared-filter-input-group-main>
|
||||
@if (filter$ | async; as filter) {
|
||||
<shared-filter-input-group-main
|
||||
class="block mr-3 w-full desktop:w-[23.5rem]"
|
||||
[class.desktop-large:w-full]="!(primaryOutletActive$ | async)"
|
||||
[hint]="searchboxHint$ | async"
|
||||
[loading]="fetching$ | async"
|
||||
[inputGroup]="filter?.input | group: 'main'"
|
||||
(search)="search({ filter, clear: true })"
|
||||
[showDescription]="false"
|
||||
[scanner]="true"
|
||||
></shared-filter-input-group-main>
|
||||
}
|
||||
|
||||
<a
|
||||
class="page-search-results__filter w-[6.75rem] h-14 rounded font-bold px-5 mb-4 text-lg bg-[#AEB7C1] flex flex-row flex-nowrap items-center justify-center"
|
||||
[class.active]="hasFilter$ | async"
|
||||
[routerLink]="filterRoute"
|
||||
[queryParams]="filterQueryParams"
|
||||
>
|
||||
>
|
||||
<shared-icon class="mr-2" icon="filter-variant"></shared-icon>
|
||||
Filter
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div
|
||||
*ngIf="hits$ | async; let hits"
|
||||
class="page-search-results__items-count inline-flex flex-row items-center pr-5 text-p3"
|
||||
[class.mb-4]="primaryOutletActive$ | async"
|
||||
>
|
||||
{{ hits ?? 0 }}
|
||||
Titel
|
||||
</div>
|
||||
@if (hits$ | async; as hits) {
|
||||
<div
|
||||
class="page-search-results__items-count inline-flex flex-row items-center pr-5 text-p3"
|
||||
[class.mb-4]="primaryOutletActive$ | async"
|
||||
>
|
||||
{{ hits ?? 0 }}
|
||||
Titel
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="page-search-results__order-by mb-[0.125rem]" [class.page-search-results__order-by-primary]="primaryOutletActive$ | async">
|
||||
<shared-order-by-filter
|
||||
*ngIf="filter$ | async; let filter"
|
||||
[orderBy]="filter?.orderBy"
|
||||
(selectedOrderByChange)="search({ filter, clear: true, orderBy: true }); updateBreadcrumbs()"
|
||||
></shared-order-by-filter>
|
||||
@if (filter$ | async; as filter) {
|
||||
<shared-order-by-filter
|
||||
[orderBy]="filter?.orderBy"
|
||||
(selectedOrderByChange)="search({ filter, clear: true, orderBy: true }); updateBreadcrumbs()"
|
||||
></shared-order-by-filter>
|
||||
}
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="primaryOutletActive$ | async; else sideOutlet">
|
||||
@if (primaryOutletActive$ | async) {
|
||||
<cdk-virtual-scroll-viewport class="product-list" [itemSize]="103 * (scale$ | async)" (scrolledIndexChange)="scrolledIndexChange($event)">
|
||||
<a
|
||||
*cdkVirtualFor="let item of results$ | async; let i = index; trackBy: trackByItemId"
|
||||
@@ -54,7 +57,7 @@
|
||||
#rla="routerLinkActive"
|
||||
queryParamsHandling="preserve"
|
||||
(click)="scrollToItem(i)"
|
||||
>
|
||||
>
|
||||
<search-result-item
|
||||
class="page-search-results__result-item page-search-results__result-item-primary"
|
||||
(selectedChange)="addToCart($event)"
|
||||
@@ -65,24 +68,25 @@
|
||||
[isActive]="rla.isActive"
|
||||
></search-result-item>
|
||||
</a>
|
||||
<page-search-result-item-loading [primaryOutletActive]="true" *ngIf="fetching$ | async"></page-search-result-item-loading>
|
||||
@if (fetching$ | async) {
|
||||
<page-search-result-item-loading [primaryOutletActive]="true"></page-search-result-item-loading>
|
||||
}
|
||||
</cdk-virtual-scroll-viewport>
|
||||
<div class="actions z-sticky h-0">
|
||||
<button
|
||||
[disabled]="loading$ | async"
|
||||
*ngIf="(selectedItemIds$ | async)?.length > 0"
|
||||
class="cta-cart cta-action-primary"
|
||||
(click)="addToCart()"
|
||||
matomoClickCategory="Trefferliste"
|
||||
matomoClickAction="click"
|
||||
matomoClickName="In den Warenkorb legen"
|
||||
>
|
||||
<ui-spinner [show]="loading$ | async">In den Warenkorb legen</ui-spinner>
|
||||
</button>
|
||||
@if ((selectedItemIds$ | async)?.length > 0) {
|
||||
<button
|
||||
[disabled]="loading$ | async"
|
||||
class="cta-cart cta-action-primary"
|
||||
(click)="addToCart()"
|
||||
matomoClickCategory="Trefferliste"
|
||||
matomoClickAction="click"
|
||||
matomoClickName="In den Warenkorb legen"
|
||||
>
|
||||
<ui-spinner [show]="loading$ | async">In den Warenkorb legen</ui-spinner>
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-template #sideOutlet>
|
||||
} @else {
|
||||
<cdk-virtual-scroll-viewport class="product-list" [itemSize]="191 * (scale$ | async)" (scrolledIndexChange)="scrolledIndexChange($event)">
|
||||
<a
|
||||
*cdkVirtualFor="let item of results$ | async; let i = index; trackBy: trackByItemId"
|
||||
@@ -91,7 +95,7 @@
|
||||
#rla="routerLinkActive"
|
||||
queryParamsHandling="preserve"
|
||||
(click)="scrollToItem(i)"
|
||||
>
|
||||
>
|
||||
<search-result-item
|
||||
class="page-search-results__result-item"
|
||||
(selectedChange)="addToCart($event)"
|
||||
@@ -102,16 +106,20 @@
|
||||
[isActive]="rla.isActive"
|
||||
></search-result-item>
|
||||
</a>
|
||||
<page-search-result-item-loading [primaryOutletActive]="false" *ngIf="fetching$ | async"></page-search-result-item-loading>
|
||||
@if (fetching$ | async) {
|
||||
<page-search-result-item-loading [primaryOutletActive]="false"></page-search-result-item-loading>
|
||||
}
|
||||
</cdk-virtual-scroll-viewport>
|
||||
<div class="actions z-sticky h-0">
|
||||
<button
|
||||
[disabled]="loading$ | async"
|
||||
*ngIf="(selectedItemIds$ | async)?.length > 0"
|
||||
class="cta-cart cta-action-primary"
|
||||
(click)="addToCart()"
|
||||
>
|
||||
<ui-spinner [show]="loading$ | async">In den Warenkorb legen</ui-spinner>
|
||||
</button>
|
||||
@if ((selectedItemIds$ | async)?.length > 0) {
|
||||
<button
|
||||
[disabled]="loading$ | async"
|
||||
class="cta-cart cta-action-primary"
|
||||
(click)="addToCart()"
|
||||
>
|
||||
<ui-spinner [show]="loading$ | async">In den Warenkorb legen</ui-spinner>
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
</ng-template>
|
||||
}
|
||||
|
||||
|
||||
@@ -9,87 +9,93 @@
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<form *ngIf="control" [formGroup]="control" (submit)="submit()">
|
||||
<ui-form-control class="searchbox-control" label="EAN/ISBN">
|
||||
<ui-searchbox
|
||||
formControlName="ean"
|
||||
[query]="query$ | async"
|
||||
(queryChange)="setQuery($event)"
|
||||
(search)="search($event)"
|
||||
(scan)="search($event)"
|
||||
[loading]="loading$ | async"
|
||||
[hint]="message$ | async"
|
||||
tabindex="0"
|
||||
[scanner]="true"
|
||||
></ui-searchbox>
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Titel" requiredMark="*">
|
||||
<input tabindex="0" uiInput formControlName="name" />
|
||||
</ui-form-control>
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Menge" requiredMark="*">
|
||||
<input tabindex="0" uiInput formControlName="quantity" />
|
||||
@if (control) {
|
||||
<form [formGroup]="control" (submit)="submit()">
|
||||
<ui-form-control class="searchbox-control" label="EAN/ISBN">
|
||||
<ui-searchbox
|
||||
formControlName="ean"
|
||||
[query]="query$ | async"
|
||||
(queryChange)="setQuery($event)"
|
||||
(search)="search($event)"
|
||||
(scan)="search($event)"
|
||||
[loading]="loading$ | async"
|
||||
[hint]="message$ | async"
|
||||
tabindex="0"
|
||||
[scanner]="true"
|
||||
></ui-searchbox>
|
||||
</ui-form-control>
|
||||
<ui-form-control class="datepicker" label="vsl. Lieferdatum" requiredMark="*">
|
||||
<button
|
||||
tabindex="-1"
|
||||
class="date-btn"
|
||||
type="button"
|
||||
[class.content-selected]="!!(estimatedShippingDate$ | async)"
|
||||
[uiOverlayTrigger]="uiDatepicker"
|
||||
#datepicker="uiOverlayTrigger"
|
||||
>
|
||||
<strong>
|
||||
{{ estimatedShippingDate$ | async | date: 'dd.MM.yy' }}
|
||||
</strong>
|
||||
<ui-icon icon="arrow_head" class="dp-button-icon" size="20px" [rotate]="datepicker.opened ? '270deg' : '90deg'"></ui-icon>
|
||||
</button>
|
||||
<ui-datepicker
|
||||
formControlName="estimatedShippingDate"
|
||||
#uiDatepicker
|
||||
yPosition="below"
|
||||
xPosition="after"
|
||||
[min]="minDate"
|
||||
[disabledDaysOfWeek]="[0]"
|
||||
[selected]="estimatedShippingDate$ | async"
|
||||
saveLabel="Übernehmen"
|
||||
(save)="changeEstimatedShippingDate($event); uiDatepicker.close()"
|
||||
></ui-datepicker>
|
||||
<ui-form-control label="Titel" requiredMark="*">
|
||||
<input tabindex="0" uiInput formControlName="name" />
|
||||
</ui-form-control>
|
||||
</div>
|
||||
<ui-form-control label="Autor">
|
||||
<input tabindex="0" uiInput formControlName="contributors" />
|
||||
</ui-form-control>
|
||||
<ui-form-control label="Verlag">
|
||||
<input tabindex="0" uiInput formControlName="manufacturer" />
|
||||
</ui-form-control>
|
||||
<ui-form-control class="supplier-dropdown" label="Lieferant" requiredMark="*">
|
||||
<ui-select tabindex="-1" formControlName="supplier">
|
||||
<ui-select-option *ngFor="let supplier of suppliers$ | async" [label]="supplier.name" [value]="supplier.id"></ui-select-option>
|
||||
</ui-select>
|
||||
</ui-form-control>
|
||||
<div class="control-row">
|
||||
<ui-form-control class="price" label="Stückpreis" [suffix]="price.value ? '€' : ''" requiredMark="*">
|
||||
<input tabindex="0" #price uiInput formControlName="price" />
|
||||
<div class="control-row">
|
||||
<ui-form-control label="Menge" requiredMark="*">
|
||||
<input tabindex="0" uiInput formControlName="quantity" />
|
||||
</ui-form-control>
|
||||
<ui-form-control class="datepicker" label="vsl. Lieferdatum" requiredMark="*">
|
||||
<button
|
||||
tabindex="-1"
|
||||
class="date-btn"
|
||||
type="button"
|
||||
[class.content-selected]="!!(estimatedShippingDate$ | async)"
|
||||
[uiOverlayTrigger]="uiDatepicker"
|
||||
#datepicker="uiOverlayTrigger"
|
||||
>
|
||||
<strong>
|
||||
{{ estimatedShippingDate$ | async | date: 'dd.MM.yy' }}
|
||||
</strong>
|
||||
<ui-icon icon="arrow_head" class="dp-button-icon" size="20px" [rotate]="datepicker.opened ? '270deg' : '90deg'"></ui-icon>
|
||||
</button>
|
||||
<ui-datepicker
|
||||
formControlName="estimatedShippingDate"
|
||||
#uiDatepicker
|
||||
yPosition="below"
|
||||
xPosition="after"
|
||||
[min]="minDate"
|
||||
[disabledDaysOfWeek]="[0]"
|
||||
[selected]="estimatedShippingDate$ | async"
|
||||
saveLabel="Übernehmen"
|
||||
(save)="changeEstimatedShippingDate($event); uiDatepicker.close()"
|
||||
></ui-datepicker>
|
||||
</ui-form-control>
|
||||
</div>
|
||||
<ui-form-control label="Autor">
|
||||
<input tabindex="0" uiInput formControlName="contributors" />
|
||||
</ui-form-control>
|
||||
<ui-form-control class="mwst-dropdown" label="MwSt" requiredMark="*">
|
||||
<ui-select tabindex="-1" formControlName="vat">
|
||||
<ui-select-option *ngFor="let vat of vats$ | async" [label]="vat.name + '%'" [value]="vat.vatType"></ui-select-option>
|
||||
<ui-form-control label="Verlag">
|
||||
<input tabindex="0" uiInput formControlName="manufacturer" />
|
||||
</ui-form-control>
|
||||
<ui-form-control class="supplier-dropdown" label="Lieferant" requiredMark="*">
|
||||
<ui-select tabindex="-1" formControlName="supplier">
|
||||
@for (supplier of suppliers$ | async; track supplier) {
|
||||
<ui-select-option [label]="supplier.name" [value]="supplier.id"></ui-select-option>
|
||||
}
|
||||
</ui-select>
|
||||
</ui-form-control>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button
|
||||
class="cta-secondary"
|
||||
(click)="nextItem()"
|
||||
[disabled]="control.invalid || control.disabled || (loading$ | async)"
|
||||
type="button"
|
||||
>
|
||||
Weitere Artikel hinzufügen
|
||||
</button>
|
||||
<button class="cta-primary" [disabled]="control.invalid || control.disabled || (loading$ | async)" type="submit">
|
||||
Bestellung anlegen
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<div class="control-row">
|
||||
<ui-form-control class="price" label="Stückpreis" [suffix]="price.value ? '€' : ''" requiredMark="*">
|
||||
<input tabindex="0" #price uiInput formControlName="price" />
|
||||
</ui-form-control>
|
||||
<ui-form-control class="mwst-dropdown" label="MwSt" requiredMark="*">
|
||||
<ui-select tabindex="-1" formControlName="vat">
|
||||
@for (vat of vats$ | async; track vat) {
|
||||
<ui-select-option [label]="vat.name + '%'" [value]="vat.vatType"></ui-select-option>
|
||||
}
|
||||
</ui-select>
|
||||
</ui-form-control>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button
|
||||
class="cta-secondary"
|
||||
(click)="nextItem()"
|
||||
[disabled]="control.invalid || control.disabled || (loading$ | async)"
|
||||
type="button"
|
||||
>
|
||||
Weitere Artikel hinzufügen
|
||||
</button>
|
||||
<button class="cta-primary" [disabled]="control.invalid || control.disabled || (loading$ | async)" type="submit">
|
||||
Bestellung anlegen
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
<ng-container *ngIf="(groupedItems$ | async)?.length <= 0 && !(fetching$ | async); else shoppingCart">
|
||||
@if ((groupedItems$ | async)?.length <= 0 && !(fetching$ | async)) {
|
||||
<div class="card stretch">
|
||||
<div class="empty-message">
|
||||
<span class="cart-icon flex items-center justify-center">
|
||||
<shared-icon icon="shopping-cart-bold" [size]="24"></shared-icon>
|
||||
</span>
|
||||
|
||||
<h1>Ihr Warenkorb ist leer.</h1>
|
||||
<p>
|
||||
Sie haben alle Artikel aus dem
|
||||
@@ -13,84 +12,75 @@
|
||||
<br />
|
||||
keinen Artikel hinzugefügt.
|
||||
</p>
|
||||
|
||||
<div class="btn-wrapper">
|
||||
<a class="cta-primary" [routerLink]="productSearchBasePath">Artikel suchen</a>
|
||||
<button class="cta-secondary" (click)="openDummyModal({})">Neuanlage</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<div class="flex items-center justify-center card stretch" *ngIf="fetching$ | async">
|
||||
<ui-spinner [show]="true"></ui-spinner>
|
||||
</div>
|
||||
|
||||
<ng-template #shoppingCart>
|
||||
<ng-container *ngIf="shoppingCart$ | async; let shoppingCart">
|
||||
} @else {
|
||||
@if (shoppingCart$ | async; as shoppingCart) {
|
||||
<div class="card stretch">
|
||||
<div class="cta-print-wrapper">
|
||||
<button class="cta-print" (click)="openPrintModal()">Drucken</button>
|
||||
</div>
|
||||
<h1 class="header">Warenkorb</h1>
|
||||
|
||||
<ng-container *ngIf="!(isDesktop$ | async)">
|
||||
@if (!(isDesktop$ | async)) {
|
||||
<page-checkout-review-details></page-checkout-review-details>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngFor="let group of groupedItems$ | async; let lastGroup = last; trackBy: trackByGroupedItems">
|
||||
<ng-container *ngIf="group?.orderType !== undefined">
|
||||
}
|
||||
@for (group of groupedItems$ | async; track trackByGroupedItems($index, group); let lastGroup = $last) {
|
||||
@if (group?.orderType !== undefined) {
|
||||
<hr />
|
||||
<div class="row item-group-header bg-[#F5F7FA]">
|
||||
<shared-icon
|
||||
*ngIf="group.orderType !== 'Dummy'"
|
||||
class="icon-order-type"
|
||||
[size]="group.orderType === 'B2B-Versand' ? 36 : 24"
|
||||
[icon]="group.orderType"
|
||||
></shared-icon>
|
||||
|
||||
@if (group.orderType !== 'Dummy') {
|
||||
<shared-icon
|
||||
class="icon-order-type"
|
||||
[size]="group.orderType === 'B2B-Versand' ? 36 : 24"
|
||||
[icon]="group.orderType"
|
||||
></shared-icon>
|
||||
}
|
||||
<div class="label" [class.dummy]="group.orderType === 'Dummy'">
|
||||
{{ group.orderType !== 'Dummy' ? group.orderType : 'Manuelle Anlage / Dummy Bestellung' }}
|
||||
<button
|
||||
*ngIf="group.orderType === 'Dummy'"
|
||||
class="text-brand border-none font-bold text-p1 outline-none pl-4"
|
||||
(click)="openDummyModal({ changeDataFromCart: true })"
|
||||
>
|
||||
Hinzufügen
|
||||
</button>
|
||||
@if (group.orderType === 'Dummy') {
|
||||
<button
|
||||
class="text-brand border-none font-bold text-p1 outline-none pl-4"
|
||||
(click)="openDummyModal({ changeDataFromCart: true })"
|
||||
>
|
||||
Hinzufügen
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="grow"></div>
|
||||
<div class="pl-4" *ngIf="group.orderType !== 'Download' && group.orderType !== 'Dummy'">
|
||||
<button class="cta-edit" (click)="showPurchasingListModal(group.items)">Ändern</button>
|
||||
</div>
|
||||
@if (group.orderType !== 'Download' && group.orderType !== 'Dummy') {
|
||||
<div class="pl-4">
|
||||
<button class="cta-edit" (click)="showPurchasingListModal(group.items)">Ändern</button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<hr
|
||||
*ngIf="
|
||||
group.orderType === 'Download' ||
|
||||
group.orderType === 'Versand' ||
|
||||
group.orderType === 'B2B-Versand' ||
|
||||
group.orderType === 'DIG-Versand'
|
||||
"
|
||||
/>
|
||||
</ng-container>
|
||||
<ng-container *ngFor="let item of group.items; let lastItem = last; let i = index; trackBy: trackByItemId">
|
||||
<ng-container
|
||||
*ngIf="group?.orderType !== undefined && (item.features?.orderType === 'Abholung' || item.features?.orderType === 'Rücklage')"
|
||||
>
|
||||
<ng-container *ngIf="item?.destination?.data?.targetBranch?.data; let targetBranch">
|
||||
<ng-container *ngIf="i === 0 || checkIfMultipleDestinationsForOrderTypeExist(targetBranch, group, i)">
|
||||
@if (
|
||||
group.orderType === 'Download' ||
|
||||
group.orderType === 'Versand' ||
|
||||
group.orderType === 'B2B-Versand' ||
|
||||
group.orderType === 'DIG-Versand'
|
||||
) {
|
||||
<hr
|
||||
/>
|
||||
}
|
||||
}
|
||||
@for (item of group.items; track trackByItemId(i, item); let lastItem = $last; let i = $index) {
|
||||
@if (group?.orderType !== undefined && (item.features?.orderType === 'Abholung' || item.features?.orderType === 'Rücklage')) {
|
||||
@if (item?.destination?.data?.targetBranch?.data; as targetBranch) {
|
||||
@if (i === 0 || checkIfMultipleDestinationsForOrderTypeExist(targetBranch, group, i)) {
|
||||
<div
|
||||
class="flex flex-row items-center px-5 pt-0 pb-[0.875rem] -mt-2 bg-[#F5F7FA]"
|
||||
[class.multiple-destinations]="checkIfMultipleDestinationsForOrderTypeExist(targetBranch, group, i)"
|
||||
>
|
||||
>
|
||||
<span class="branch-name">{{ targetBranch?.name }} | {{ targetBranch | branchAddress }}</span>
|
||||
</div>
|
||||
<hr />
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
<page-shopping-cart-item
|
||||
(changeItem)="changeItem($event)"
|
||||
(changeDummyItem)="changeDummyItem($event)"
|
||||
@@ -101,19 +91,22 @@
|
||||
[loadingOnItemChangeById]="loadingOnItemChangeById$ | async"
|
||||
[loadingOnQuantityChangeById]="loadingOnQuantityChangeById$ | async"
|
||||
></page-shopping-cart-item>
|
||||
|
||||
<hr *ngIf="!lastItem" />
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
@if (!lastItem) {
|
||||
<hr />
|
||||
}
|
||||
}
|
||||
}
|
||||
<div class="h-[8.9375rem]"></div>
|
||||
</div>
|
||||
<div class="card footer flex flex-col justify-center items-center">
|
||||
<div class="flex flex-row items-start justify-between w-full mb-1">
|
||||
<ng-container *ngIf="totalItemCount$ | async; let totalItemCount">
|
||||
<div *ngIf="totalReadingPoints$ | async; let totalReadingPoints" class="total-item-reading-points w-full">
|
||||
{{ totalItemCount }} Artikel | {{ totalReadingPoints }} Lesepunkte
|
||||
</div>
|
||||
</ng-container>
|
||||
@if (totalItemCount$ | async; as totalItemCount) {
|
||||
@if (totalReadingPoints$ | async; as totalReadingPoints) {
|
||||
<div class="total-item-reading-points w-full">
|
||||
{{ totalItemCount }} Artikel | {{ totalReadingPoints }} Lesepunkte
|
||||
</div>
|
||||
}
|
||||
}
|
||||
<div class="flex flex-col w-full">
|
||||
<strong class="total-value">
|
||||
Zwischensumme {{ shoppingCart?.total?.value | currency: shoppingCart?.total?.currency : 'code' }}
|
||||
@@ -130,11 +123,18 @@
|
||||
notificationsControl?.invalid ||
|
||||
((primaryCtaLabel$ | async) === 'Bestellen' && ((checkingOla$ | async) || (checkoutIsInValid$ | async)))
|
||||
"
|
||||
>
|
||||
>
|
||||
<ui-spinner [show]="showOrderButtonSpinner">
|
||||
{{ primaryCtaLabel$ | async }}
|
||||
</ui-spinner>
|
||||
</button>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
}
|
||||
}
|
||||
|
||||
@if (fetching$ | async) {
|
||||
<div class="flex items-center justify-center card stretch">
|
||||
<ui-spinner [show]="true"></ui-spinner>
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
@@ -2,56 +2,61 @@
|
||||
Überprüfen Sie die Details.
|
||||
</h1>
|
||||
|
||||
<ng-container *ngIf="buyer$ | async; let buyer">
|
||||
<div *ngIf="!(showAddresses$ | async)" class="flex flex-row items-start justify-between p-5">
|
||||
<div class="flex flex-row flex-wrap pr-4">
|
||||
<ng-container *ngIf="getNameFromBuyer(buyer); let name">
|
||||
<div class="mr-3">{{ name.label }}</div>
|
||||
<div class="font-bold">{{ name.value }}</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<button (click)="changeAddress()" class="text-p1 font-bold text-[#F70400]">Ändern</button>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="showNotificationChannels$ | async">
|
||||
<form class="pb-4" *ngIf="control" [formGroup]="control">
|
||||
<shared-notification-channel-control
|
||||
[communicationDetails]="communicationDetails$ | async"
|
||||
(channelActionEvent)="updateNotifications($event)"
|
||||
[channelActionName]="'Speichern'"
|
||||
[channelActionLoading]="notificationChannelLoading$ | async"
|
||||
formGroupName="notificationChannel"
|
||||
></shared-notification-channel-control>
|
||||
</form>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="payer$ | async; let payer">
|
||||
<div *ngIf="showAddresses$ | async" class="flex flex-row items-start justify-between p-5 pt-0">
|
||||
<div class="flex flex-row flex-wrap pr-4" data-address-type="Rechnungsadresse" data-which="Rechnungsadresse">
|
||||
<div class="mr-3" data-what="title">Rechnungsadresse</div>
|
||||
<div class="font-bold" data-what="address">
|
||||
{{ payer | payerAddress }}
|
||||
@if (buyer$ | async; as buyer) {
|
||||
@if (!(showAddresses$ | async)) {
|
||||
<div class="flex flex-row items-start justify-between p-5">
|
||||
<div class="flex flex-row flex-wrap pr-4">
|
||||
@if (getNameFromBuyer(buyer); as name) {
|
||||
<div class="mr-3">{{ name.label }}</div>
|
||||
<div class="font-bold">{{ name.value }}</div>
|
||||
}
|
||||
</div>
|
||||
<button (click)="changeAddress()" class="text-p1 font-bold text-[#F70400]">Ändern</button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
<button (click)="changeAddress()" class="text-p1 font-bold text-[#F70400]">Ändern</button>
|
||||
</div>
|
||||
</ng-container>
|
||||
@if (showNotificationChannels$ | async) {
|
||||
@if (control) {
|
||||
<form class="pb-4" [formGroup]="control">
|
||||
<shared-notification-channel-control
|
||||
[communicationDetails]="communicationDetails$ | async"
|
||||
(channelActionEvent)="updateNotifications($event)"
|
||||
[channelActionName]="'Speichern'"
|
||||
[channelActionLoading]="notificationChannelLoading$ | async"
|
||||
formGroupName="notificationChannel"
|
||||
></shared-notification-channel-control>
|
||||
</form>
|
||||
}
|
||||
}
|
||||
|
||||
<ng-container *ngIf="payer$ | async; let payer">
|
||||
<div *ngIf="showAddresses$ | async" class="flex flex-row items-start justify-between px-5">
|
||||
<div class="flex flex-row flex-wrap pr-4" data-address-type="Lieferadresse" data-which="Lieferadresse">
|
||||
<div class="mr-3" data-what="title">Lieferadresse</div>
|
||||
<div class="font-bold" data-what="address">
|
||||
{{ shippingAddress$ | async | shippingAddress }}
|
||||
@if (payer$ | async; as payer) {
|
||||
@if (showAddresses$ | async) {
|
||||
<div class="flex flex-row items-start justify-between p-5 pt-0">
|
||||
<div class="flex flex-row flex-wrap pr-4" data-address-type="Rechnungsadresse" data-which="Rechnungsadresse">
|
||||
<div class="mr-3" data-what="title">Rechnungsadresse</div>
|
||||
<div class="font-bold" data-what="address">
|
||||
{{ payer | payerAddress }}
|
||||
</div>
|
||||
</div>
|
||||
<button (click)="changeAddress()" class="text-p1 font-bold text-[#F70400]">Ändern</button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
<button (click)="changeAddress()" class="text-p1 font-bold text-[#F70400]">Ändern</button>
|
||||
</div>
|
||||
</ng-container>
|
||||
@if (payer$ | async; as payer) {
|
||||
@if (showAddresses$ | async) {
|
||||
<div class="flex flex-row items-start justify-between px-5">
|
||||
<div class="flex flex-row flex-wrap pr-4" data-address-type="Lieferadresse" data-which="Lieferadresse">
|
||||
<div class="mr-3" data-what="title">Lieferadresse</div>
|
||||
<div class="font-bold" data-what="address">
|
||||
{{ shippingAddress$ | async | shippingAddress }}
|
||||
</div>
|
||||
</div>
|
||||
<button (click)="changeAddress()" class="text-p1 font-bold text-[#F70400]">Ändern</button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
<page-special-comment
|
||||
class="mb-6 mt-4"
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
<div class="item-thumbnail">
|
||||
<a [routerLink]="productSearchDetailsPath" [queryParams]="{ main_qs: item?.product?.ean }">
|
||||
<img loading="lazy" *ngIf="item?.product?.ean | productImage; let thumbnailUrl" [src]="thumbnailUrl" [alt]="item?.product?.name" />
|
||||
@if (item?.product?.ean | productImage; as thumbnailUrl) {
|
||||
<img loading="lazy" [src]="thumbnailUrl" [alt]="item?.product?.name" />
|
||||
}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="item-contributors">
|
||||
<a
|
||||
*ngFor="let contributor of contributors$ | async; let last = last"
|
||||
[routerLink]="productSearchResultsPath"
|
||||
[queryParams]="{ main_qs: contributor, main_author: 'author' }"
|
||||
(click)="$event?.stopPropagation()"
|
||||
>
|
||||
{{ contributor }}{{ last ? '' : ';' }}
|
||||
</a>
|
||||
@for (contributor of contributors$ | async; track contributor; let last = $last) {
|
||||
<a
|
||||
[routerLink]="productSearchResultsPath"
|
||||
[queryParams]="{ main_qs: contributor, main_author: 'author' }"
|
||||
(click)="$event?.stopPropagation()"
|
||||
>
|
||||
{{ contributor }}{{ last ? '' : ';' }}
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div
|
||||
@@ -21,86 +24,106 @@
|
||||
[class.text-p1]="item?.product?.name?.length >= 50 || !isTablet"
|
||||
[class.text-p2]="item?.product?.name?.length >= 60 && isTablet"
|
||||
[class.text-p3]="item?.product?.name?.length >= 100"
|
||||
>
|
||||
>
|
||||
<a [routerLink]="productSearchDetailsPath" [queryParams]="{ main_qs: item?.product?.ean }">{{ item?.product?.name }}</a>
|
||||
</div>
|
||||
|
||||
<div class="item-format" *ngIf="item?.product?.format && item?.product?.formatDetail">
|
||||
<img
|
||||
*ngIf="item?.product?.format !== '--'"
|
||||
src="assets/images/Icon_{{ item?.product?.format }}.svg"
|
||||
[alt]="item?.product?.formatDetail"
|
||||
/>
|
||||
{{ item?.product?.formatDetail }}
|
||||
</div>
|
||||
@if (item?.product?.format && item?.product?.formatDetail) {
|
||||
<div class="item-format">
|
||||
@if (item?.product?.format !== '--') {
|
||||
<img
|
||||
src="assets/images/Icon_{{ item?.product?.format }}.svg"
|
||||
[alt]="item?.product?.formatDetail"
|
||||
/>
|
||||
}
|
||||
{{ item?.product?.formatDetail }}
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="item-info text-p2">
|
||||
<div class="mb-1">{{ item?.product?.manufacturer | substr: 25 }} | {{ item?.product?.ean }}</div>
|
||||
<div class="mb-1">
|
||||
{{ item?.product?.volume }}
|
||||
<span *ngIf="item?.product?.volume && item?.product?.publicationDate">|</span>
|
||||
@if (item?.product?.volume && item?.product?.publicationDate) {
|
||||
<span>|</span>
|
||||
}
|
||||
{{ item?.product?.publicationDate | date }}
|
||||
</div>
|
||||
<div *ngIf="notAvailable$ | async">
|
||||
<span class="text-brand item-date">Nicht verfügbar</span>
|
||||
</div>
|
||||
|
||||
<shared-skeleton-loader class="w-40" *ngIf="refreshingAvailabilit$ | async; else avaTmplt"></shared-skeleton-loader>
|
||||
|
||||
<ng-template #avaTmplt>
|
||||
<div class="item-date" [class.availability-changed]="estimatedShippingDateChanged$ | async" *ngIf="orderType === 'Abholung'">
|
||||
Abholung ab {{ item?.availability?.estimatedShippingDate | date }}
|
||||
@if (notAvailable$ | async) {
|
||||
<div>
|
||||
<span class="text-brand item-date">Nicht verfügbar</span>
|
||||
</div>
|
||||
<div
|
||||
class="item-date"
|
||||
[class.availability-changed]="estimatedShippingDateChanged$ | async"
|
||||
*ngIf="orderType === 'Versand' || orderType === 'B2B-Versand' || orderType === 'DIG-Versand'"
|
||||
>
|
||||
<ng-container *ngIf="item?.availability?.estimatedDelivery; else estimatedShippingDate">
|
||||
Zustellung zwischen {{ (item?.availability?.estimatedDelivery?.start | date: 'EEE, dd.MM.')?.replace('.', '') }}
|
||||
und
|
||||
{{ (item?.availability?.estimatedDelivery?.stop | date: 'EEE, dd.MM.')?.replace('.', '') }}
|
||||
</ng-container>
|
||||
<ng-template #estimatedShippingDate>Versand {{ item?.availability?.estimatedShippingDate | date }}</ng-template>
|
||||
</div>
|
||||
</ng-template>
|
||||
}
|
||||
|
||||
<div class="item-availability-message" *ngIf="olaError$ | async">Artikel nicht verfügbar</div>
|
||||
@if (refreshingAvailabilit$ | async) {
|
||||
<shared-skeleton-loader class="w-40"></shared-skeleton-loader>
|
||||
} @else {
|
||||
@if (orderType === 'Abholung') {
|
||||
<div class="item-date" [class.availability-changed]="estimatedShippingDateChanged$ | async">
|
||||
Abholung ab {{ item?.availability?.estimatedShippingDate | date }}
|
||||
</div>
|
||||
}
|
||||
@if (orderType === 'Versand' || orderType === 'B2B-Versand' || orderType === 'DIG-Versand') {
|
||||
<div
|
||||
class="item-date"
|
||||
[class.availability-changed]="estimatedShippingDateChanged$ | async"
|
||||
>
|
||||
@if (item?.availability?.estimatedDelivery) {
|
||||
Zustellung zwischen {{ (item?.availability?.estimatedDelivery?.start | date: 'EEE, dd.MM.')?.replace('.', '') }}
|
||||
und
|
||||
{{ (item?.availability?.estimatedDelivery?.stop | date: 'EEE, dd.MM.')?.replace('.', '') }}
|
||||
} @else {
|
||||
Versand {{ item?.availability?.estimatedShippingDate | date }}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@if (olaError$ | async) {
|
||||
<div class="item-availability-message">Artikel nicht verfügbar</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="item-price-stock flex flex-col">
|
||||
<div class="text-p2 font-bold">{{ item?.availability?.price?.value?.value | currency: 'EUR' : 'code' }}</div>
|
||||
<div class="text-p2 font-normal">
|
||||
<ui-quantity-dropdown
|
||||
*ngIf="!(isDummy$ | async); else quantityDummy"
|
||||
[ngModel]="item?.quantity"
|
||||
(ngModelChange)="onChangeQuantity($event)"
|
||||
[showSpinner]="(loadingOnQuantityChangeById$ | async) === item?.id"
|
||||
[disabled]="(loadingOnItemChangeById$ | async) === item?.id"
|
||||
[range]="quantityRange$ | async"
|
||||
></ui-quantity-dropdown>
|
||||
<ng-template #quantityDummy>
|
||||
@if (!(isDummy$ | async)) {
|
||||
<ui-quantity-dropdown
|
||||
[ngModel]="item?.quantity"
|
||||
(ngModelChange)="onChangeQuantity($event)"
|
||||
[showSpinner]="(loadingOnQuantityChangeById$ | async) === item?.id"
|
||||
[disabled]="(loadingOnItemChangeById$ | async) === item?.id"
|
||||
[range]="quantityRange$ | async"
|
||||
></ui-quantity-dropdown>
|
||||
} @else {
|
||||
<div class="mt-2">{{ item?.quantity }}x</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
<div class="quantity-error" *ngIf="quantityError">
|
||||
{{ quantityError }}
|
||||
}
|
||||
</div>
|
||||
@if (quantityError) {
|
||||
<div class="quantity-error">
|
||||
{{ quantityError }}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="actions" *ngIf="orderType !== 'Download'">
|
||||
<button
|
||||
[disabled]="(loadingOnQuantityChangeById$ | async) === item?.id || (loadingOnItemChangeById$ | async) === item?.id"
|
||||
(click)="onChangeItem()"
|
||||
*ngIf="!(hasOrderType$ | async)"
|
||||
>
|
||||
<ui-spinner [show]="(loadingOnItemChangeById$ | async) === item?.id">Lieferweg auswählen</ui-spinner>
|
||||
</button>
|
||||
<button
|
||||
[disabled]="(loadingOnQuantityChangeById$ | async) === item?.id || (loadingOnItemChangeById$ | async) === item?.id"
|
||||
(click)="onChangeItem()"
|
||||
*ngIf="canEdit$ | async"
|
||||
>
|
||||
<ui-spinner [show]="(loadingOnItemChangeById$ | async) === item?.id">Lieferweg ändern</ui-spinner>
|
||||
</button>
|
||||
</div>
|
||||
@if (orderType !== 'Download') {
|
||||
<div class="actions">
|
||||
@if (!(hasOrderType$ | async)) {
|
||||
<button
|
||||
[disabled]="(loadingOnQuantityChangeById$ | async) === item?.id || (loadingOnItemChangeById$ | async) === item?.id"
|
||||
(click)="onChangeItem()"
|
||||
>
|
||||
<ui-spinner [show]="(loadingOnItemChangeById$ | async) === item?.id">Lieferweg auswählen</ui-spinner>
|
||||
</button>
|
||||
}
|
||||
@if (canEdit$ | async) {
|
||||
<button
|
||||
[disabled]="(loadingOnQuantityChangeById$ | async) === item?.id || (loadingOnItemChangeById$ | async) === item?.id"
|
||||
(click)="onChangeItem()"
|
||||
>
|
||||
<ui-spinner [show]="(loadingOnItemChangeById$ | async) === item?.id">Lieferweg ändern</ui-spinner>
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -24,11 +24,15 @@
|
||||
></textarea>
|
||||
|
||||
<div class="comment-actions py-4">
|
||||
<button type="reset" class="clear pl-4" *ngIf="!disabled && !!value" (click)="clear(); triggerResize()">
|
||||
<shared-icon icon="close" [size]="24"></shared-icon>
|
||||
</button>
|
||||
@if (!disabled && !!value) {
|
||||
<button type="reset" class="clear pl-4" (click)="clear(); triggerResize()">
|
||||
<shared-icon icon="close" [size]="24"></shared-icon>
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="!(hasPayer || hasBuyer)" class="text-p3">Zur Info: Sie haben dem Warenkorb noch keinen Kunden hinzugefügt.</div>
|
||||
@if (!(hasPayer || hasBuyer)) {
|
||||
<div class="text-p3">Zur Info: Sie haben dem Warenkorb noch keinen Kunden hinzugefügt.</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -7,66 +7,63 @@
|
||||
<h1 class="text-center text-h2 my-1 font-bold">Bestellbestätigung</h1>
|
||||
<p class="text-center text-p1 mb-10">Nachfolgend erhalten Sie die Übersicht Ihrer Bestellung.</p>
|
||||
|
||||
<ng-container *ngFor="let displayOrder of displayOrders$ | async; let i = index; let orderLast = last">
|
||||
<ng-container *ngIf="i === 0">
|
||||
@for (displayOrder of displayOrders$ | async; track displayOrder; let i = $index; let orderLast = $last) {
|
||||
@if (i === 0) {
|
||||
<div class="flex flex-row items-center bg-white shadow-card min-h-[3.3125rem]">
|
||||
<div class="text-h3 font-bold px-5 py-[0.875rem]">
|
||||
{{ displayOrder?.buyer | buyerName }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
</ng-container>
|
||||
|
||||
}
|
||||
<div class="flex flex-row items-center bg-[#F5F7FA] min-h-[3.3125rem]">
|
||||
<div class="flex flex-row items-center justify-center px-5 py-[0.875rem]">
|
||||
<shared-icon
|
||||
*ngIf="(displayOrder?.items)[0]?.features?.orderType !== 'Dummy'"
|
||||
class="mr-2"
|
||||
[size]="(displayOrder?.items)[0]?.features?.orderType === 'B2B-Versand' ? 36 : 24"
|
||||
[icon]="(displayOrder?.items)[0]?.features?.orderType"
|
||||
></shared-icon>
|
||||
@if ((displayOrder?.items)[0]?.features?.orderType !== 'Dummy') {
|
||||
<shared-icon
|
||||
class="mr-2"
|
||||
[size]="(displayOrder?.items)[0]?.features?.orderType === 'B2B-Versand' ? 36 : 24"
|
||||
[icon]="(displayOrder?.items)[0]?.features?.orderType"
|
||||
></shared-icon>
|
||||
}
|
||||
<p class="text-p1 font-bold mr-3">{{ (displayOrder?.items)[0]?.features?.orderType }}</p>
|
||||
<div
|
||||
*ngIf="
|
||||
(displayOrder?.items)[0]?.features?.orderType === 'Abholung' || (displayOrder?.items)[0]?.features?.orderType === 'Rücklage';
|
||||
else shippingAddress
|
||||
"
|
||||
>
|
||||
{{ displayOrder.targetBranch?.name }}, {{ displayOrder.targetBranch | branchAddress }}
|
||||
</div>
|
||||
<ng-template #shippingAddress>
|
||||
@if (
|
||||
(displayOrder?.items)[0]?.features?.orderType === 'Abholung' || (displayOrder?.items)[0]?.features?.orderType === 'Rücklage') {
|
||||
<div
|
||||
>
|
||||
{{ displayOrder.targetBranch?.name }}, {{ displayOrder.targetBranch | branchAddress }}
|
||||
</div>
|
||||
} @else {
|
||||
{{ displayOrder.shippingAddress | branchAddress }}
|
||||
</ng-template>
|
||||
<div *ngIf="(displayOrder?.items)[0]?.features?.orderType === 'Download'">
|
||||
| {{ displayOrder.buyer?.communicationDetails?.email }}
|
||||
</div>
|
||||
}
|
||||
@if ((displayOrder?.items)[0]?.features?.orderType === 'Download') {
|
||||
<div>
|
||||
| {{ displayOrder.buyer?.communicationDetails?.email }}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="flex flex-row justify-between items-center">
|
||||
<div class="flex flex-col px-5 py-4 bg-white" [attr.data-order-type]="(displayOrder?.items)[0]?.features?.orderType">
|
||||
<div class="flex flex-row justify-between items-center mb-[0.375rem]">
|
||||
<div class="flex flex-row">
|
||||
<span class="w-32">Vorgangs-ID</span>
|
||||
<ng-container *ngIf="customer$ | async; let customer">
|
||||
<a
|
||||
data-which="Vorgangs-ID"
|
||||
data-what="link"
|
||||
*ngIf="customer$ | async; let customer"
|
||||
class="font-bold text-[#0556B4] no-underline"
|
||||
[routerLink]="['/kunde', processId, 'customer', 'search', customer?.id, 'orders', displayOrder.id]"
|
||||
[queryParams]="{ main_qs: customer?.customerNumber, filter_customertype: '' }"
|
||||
>
|
||||
{{ displayOrder.orderNumber }}
|
||||
</a>
|
||||
</ng-container>
|
||||
@if (customer$ | async; as customer) {
|
||||
@if (customer$ | async; as customer) {
|
||||
<a
|
||||
data-which="Vorgangs-ID"
|
||||
data-what="link"
|
||||
class="font-bold text-[#0556B4] no-underline"
|
||||
[routerLink]="['/kunde', processId, 'customer', 'search', customer?.id, 'orders', displayOrder.id]"
|
||||
[queryParams]="{ main_qs: customer?.customerNumber, filter_customertype: '' }"
|
||||
>
|
||||
{{ displayOrder.orderNumber }}
|
||||
</a>
|
||||
}
|
||||
}
|
||||
<ui-spinner class="text-[#0556B4] h-4 w-4" [show]="!(customer$ | async)"></ui-spinner>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row justify-between items-center">
|
||||
<div class="flex flex-row">
|
||||
<span class="w-32">Bestelldatum</span>
|
||||
@@ -74,14 +71,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mr-4">
|
||||
<button
|
||||
(click)="expanded[i] = !expanded[i]"
|
||||
type="button"
|
||||
class="text-[#0556B4] font-bold flex flex-row items-center justify-center"
|
||||
[class.flex-row-reverse]="!expanded[i]"
|
||||
>
|
||||
>
|
||||
<shared-icon
|
||||
class="mr-1"
|
||||
icon="arrow-back"
|
||||
@@ -93,135 +89,139 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-container *ngFor="let order of displayOrder.items; let last = last">
|
||||
<ng-container *ngIf="expanded[i]">
|
||||
@for (order of displayOrder.items; track order; let last = $last) {
|
||||
@if (expanded[i]) {
|
||||
<div
|
||||
class="page-checkout-summary__items-tablet px-5 pb-[1.875rem] bg-white"
|
||||
[class.page-checkout-summary__items]="isDesktop$ | async"
|
||||
[class.last]="last"
|
||||
>
|
||||
>
|
||||
<div class="page-checkout-summary__items-thumbnail flex flex-row">
|
||||
<a [routerLink]="getProductSearchDetailsPath(order?.product?.ean)" [queryParams]="getProductSearchDetailsQueryParams(order)">
|
||||
<img class="w-[3.125rem] max-h-20 mr-2" [src]="order.product?.ean | productImage: 195 : 315 : true" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="page-checkout-summary__items-title whitespace-nowrap overflow-ellipsis overflow-hidden">
|
||||
<a
|
||||
class="font-bold no-underline text-[#0556B4]"
|
||||
[routerLink]="getProductSearchDetailsPath(order?.product?.ean)"
|
||||
[queryParams]="getProductSearchDetailsQueryParams(order)"
|
||||
>
|
||||
>
|
||||
{{ order?.product?.name }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="page-checkout-summary__items-ssc" *ngIf="(order?.subsetItems)[0]; let subsetItem">
|
||||
<span class="mr-2">{{ subsetItem.supplierName }}</span>
|
||||
<span *ngIf="subsetItem?.ssc && subsetItem?.sscText" class="font-bold border-l border-black pl-2">
|
||||
{{ subsetItem.ssc }} - {{ subsetItem.sscText }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@if ((order?.subsetItems)[0]; as subsetItem) {
|
||||
<div class="page-checkout-summary__items-ssc">
|
||||
<span class="mr-2">{{ subsetItem.supplierName }}</span>
|
||||
@if (subsetItem?.ssc && subsetItem?.sscText) {
|
||||
<span class="font-bold border-l border-black pl-2">
|
||||
{{ subsetItem.ssc }} - {{ subsetItem.sscText }}
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
<div class="page-checkout-summary__items-quantity font-bold justify-self-end">
|
||||
<span>{{ order.quantity }}x</span>
|
||||
</div>
|
||||
|
||||
<div class="page-checkout-summary__items-price font-bold justify-self-end">
|
||||
<span>{{ order.price?.value?.value | currency: ' ' }} {{ order.price?.value?.currency }}</span>
|
||||
</div>
|
||||
|
||||
<div class="page-checkout-summary__items-delivery product-details">
|
||||
<div class="delivery-row" [ngSwitch]="order?.features?.orderType">
|
||||
<ng-container *ngSwitchCase="'Abholung'">
|
||||
<span class="order-type">
|
||||
Abholung ab {{ (order?.subsetItems)[0]?.estimatedShippingDate | date }}
|
||||
<ng-container [ngTemplateOutlet]="abholfrist" [ngTemplateOutletContext]="{ order: order }"></ng-container>
|
||||
</span>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="'Rücklage'">
|
||||
<span class="order-type">
|
||||
{{ order?.features?.orderType }}
|
||||
<ng-container [ngTemplateOutlet]="abholfrist" [ngTemplateOutletContext]="{ order: order }"></ng-container>
|
||||
</span>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="['Versand', 'B2B-Versand', 'DIG-Versand'].indexOf(order?.features?.orderType) > -1">
|
||||
<ng-container *ngIf="(order?.subsetItems)[0]?.estimatedDelivery; else estimatedShippingDate">
|
||||
<div class="delivery-row">
|
||||
@switch (order?.features?.orderType) {
|
||||
@case ('Abholung') {
|
||||
<span class="order-type">
|
||||
Zustellung zwischen
|
||||
{{ ((order?.subsetItems)[0]?.estimatedDelivery?.start | date: 'EEE, dd.MM.')?.replace('.', '') }} und
|
||||
{{ ((order?.subsetItems)[0]?.estimatedDelivery?.stop | date: 'EEE, dd.MM.')?.replace('.', '') }}
|
||||
Abholung ab {{ (order?.subsetItems)[0]?.estimatedShippingDate | date }}
|
||||
<ng-container [ngTemplateOutlet]="abholfrist" [ngTemplateOutletContext]="{ order: order }"></ng-container>
|
||||
</span>
|
||||
</ng-container>
|
||||
<ng-template #estimatedShippingDate>
|
||||
<span class="order-type">Versanddatum {{ (order?.subsetItems)[0]?.estimatedShippingDate | date }}</span>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchDefault>
|
||||
<span class="order-type">{{ order?.features?.orderType }}</span>
|
||||
</ng-container>
|
||||
}
|
||||
@case ('Rücklage') {
|
||||
<span class="order-type">
|
||||
{{ order?.features?.orderType }}
|
||||
<ng-container [ngTemplateOutlet]="abholfrist" [ngTemplateOutletContext]="{ order: order }"></ng-container>
|
||||
</span>
|
||||
}
|
||||
@case (['Versand', 'B2B-Versand', 'DIG-Versand'].indexOf(order?.features?.orderType) > -1) {
|
||||
@if ((order?.subsetItems)[0]?.estimatedDelivery) {
|
||||
<span class="order-type">
|
||||
Zustellung zwischen
|
||||
{{ ((order?.subsetItems)[0]?.estimatedDelivery?.start | date: 'EEE, dd.MM.')?.replace('.', '') }} und
|
||||
{{ ((order?.subsetItems)[0]?.estimatedDelivery?.stop | date: 'EEE, dd.MM.')?.replace('.', '') }}
|
||||
</span>
|
||||
} @else {
|
||||
<span class="order-type">Versanddatum {{ (order?.subsetItems)[0]?.estimatedShippingDate | date }}</span>
|
||||
}
|
||||
}
|
||||
@default {
|
||||
<span class="order-type">{{ order?.features?.orderType }}</span>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<hr *ngIf="last" />
|
||||
</ng-container>
|
||||
<ng-container *ngIf="orderLast">
|
||||
}
|
||||
@if (last) {
|
||||
<hr />
|
||||
}
|
||||
}
|
||||
@if (orderLast) {
|
||||
<div class="flex flex-row justify-between items-center min-h-[3.3125rem] bg-white px-5 py-4 rounded-b">
|
||||
<span *ngIf="totalReadingPoints$ | async; let totalReadingPoints" class="text-p2 font-bold">
|
||||
{{ totalItemCount$ | async }} Artikel | {{ totalReadingPoints }} Lesepunkte
|
||||
</span>
|
||||
|
||||
@if (totalReadingPoints$ | async; as totalReadingPoints) {
|
||||
<span class="text-p2 font-bold">
|
||||
{{ totalItemCount$ | async }} Artikel | {{ totalReadingPoints }} Lesepunkte
|
||||
</span>
|
||||
}
|
||||
<div class="flex flex-row items-center justify-center">
|
||||
<div class="text-p1 font-bold flex flex-row items-center">
|
||||
<div class="mr-1">Gesamtsumme {{ totalPrice$ | async | currency: ' ' }} {{ totalPriceCurrency$ | async }}</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="bg-brand text-white font-bold text-p1 outline-none border-none rounded-full px-6 py-3 ml-2"
|
||||
*ngIf="(takeNowOrders$ | async)?.length === 1 && (isB2BCustomer$ | async)"
|
||||
>
|
||||
<button class="cta-goods-out" (click)="navigateToShelfOut()">Zur Warenausgabe</button>
|
||||
</div>
|
||||
@if ((takeNowOrders$ | async)?.length === 1 && (isB2BCustomer$ | async)) {
|
||||
<div
|
||||
class="bg-brand text-white font-bold text-p1 outline-none border-none rounded-full px-6 py-3 ml-2"
|
||||
>
|
||||
<button class="cta-goods-out" (click)="navigateToShelfOut()">Zur Warenausgabe</button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-template #abholfrist let-order="order">
|
||||
<div *ngIf="!(updatingPreferredPickUpDate$ | async)[(order?.subsetItems)[0].id]" class="inline-flex">
|
||||
<button [uiOverlayTrigger]="deadlineDatepicker" #deadlineDatepickerTrigger="uiOverlayTrigger" class="flex flex-row items-center">
|
||||
<span class="mx-[0.625rem] font-normal">bis</span>
|
||||
<strong class="border-r border-[#AEB7C1] pr-4">
|
||||
{{ ((order?.subsetItems)[0]?.preferredPickUpDate | date: 'dd.MM.yy') || 'TT.MM.JJJJ' }}
|
||||
</strong>
|
||||
|
||||
<shared-icon class="text-[#596470] ml-4" [size]="24" icon="isa-calendar"></shared-icon>
|
||||
</button>
|
||||
<ui-datepicker
|
||||
#deadlineDatepicker
|
||||
yPosition="below"
|
||||
xPosition="after"
|
||||
[xOffset]="8"
|
||||
[min]="minDateDatepicker"
|
||||
[disabledDaysOfWeek]="[0]"
|
||||
[(selected)]="selectedDate"
|
||||
>
|
||||
<div #content class="grid grid-flow-row gap-2">
|
||||
<button
|
||||
class="rounded-full font-bold text-white bg-brand py-px-15 px-px-25"
|
||||
(click)="updatePreferredPickUpDate(undefined, selectedDate); deadlineDatepickerTrigger.close()"
|
||||
@if (!(updatingPreferredPickUpDate$ | async)[(order?.subsetItems)[0].id]) {
|
||||
<div class="inline-flex">
|
||||
<button [uiOverlayTrigger]="deadlineDatepicker" #deadlineDatepickerTrigger="uiOverlayTrigger" class="flex flex-row items-center">
|
||||
<span class="mx-[0.625rem] font-normal">bis</span>
|
||||
<strong class="border-r border-[#AEB7C1] pr-4">
|
||||
{{ ((order?.subsetItems)[0]?.preferredPickUpDate | date: 'dd.MM.yy') || 'TT.MM.JJJJ' }}
|
||||
</strong>
|
||||
<shared-icon class="text-[#596470] ml-4" [size]="24" icon="isa-calendar"></shared-icon>
|
||||
</button>
|
||||
<ui-datepicker
|
||||
#deadlineDatepicker
|
||||
yPosition="below"
|
||||
xPosition="after"
|
||||
[xOffset]="8"
|
||||
[min]="minDateDatepicker"
|
||||
[disabledDaysOfWeek]="[0]"
|
||||
[(selected)]="selectedDate"
|
||||
>
|
||||
Für den Warenkorb festlegen
|
||||
</button>
|
||||
</div>
|
||||
</ui-datepicker>
|
||||
</div>
|
||||
<div class="fetching" *ngIf="!!(updatingPreferredPickUpDate$ | async)[(order?.subsetItems)[0].id]"></div>
|
||||
<div #content class="grid grid-flow-row gap-2">
|
||||
<button
|
||||
class="rounded-full font-bold text-white bg-brand py-px-15 px-px-25"
|
||||
(click)="updatePreferredPickUpDate(undefined, selectedDate); deadlineDatepickerTrigger.close()"
|
||||
>
|
||||
Für den Warenkorb festlegen
|
||||
</button>
|
||||
</div>
|
||||
</ui-datepicker>
|
||||
</div>
|
||||
}
|
||||
@if (!!(updatingPreferredPickUpDate$ | async)[(order?.subsetItems)[0].id]) {
|
||||
<div class="fetching"></div>
|
||||
}
|
||||
</ng-template>
|
||||
|
||||
<div class="relative">
|
||||
@@ -232,17 +232,18 @@
|
||||
type="button"
|
||||
class="px-6 py-2 rounded-full border-2 border-solid border-brand text-brand bg-white font-bold text-lg whitespace-nowrap h-14 flex flex-row items-center justify-center print-button"
|
||||
(click)="printOrderConfirmation()"
|
||||
>
|
||||
>
|
||||
<ui-spinner class="min-h-4 min-w-4" [show]="isPrinting$ | async">Bestellbestätigung drucken</ui-spinner>
|
||||
</button>
|
||||
|
||||
<button
|
||||
*ngIf="hasAbholung$ | async"
|
||||
type="button"
|
||||
class="px-6 py-2 rounded-full border-2 border-solid border-brand text-brand bg-white font-bold text-lg whitespace-nowrap h-14"
|
||||
(click)="sendOrderConfirmation()"
|
||||
>
|
||||
Bestellbestätigung senden
|
||||
</button>
|
||||
@if (hasAbholung$ | async) {
|
||||
<button
|
||||
type="button"
|
||||
class="px-6 py-2 rounded-full border-2 border-solid border-brand text-brand bg-white font-bold text-lg whitespace-nowrap h-14"
|
||||
(click)="sendOrderConfirmation()"
|
||||
>
|
||||
Bestellbestätigung senden
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -15,7 +15,11 @@ import { CrmCustomerService } from '@domain/crm';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { DomainOmsService } from '@domain/oms';
|
||||
import { DomainCatalogService } from '@domain/catalog';
|
||||
import { DisplayOrderDTO, DisplayOrderItemDTO, DisplayOrderItemSubsetDTO } from '@generated/swagger/oms-api';
|
||||
import {
|
||||
DisplayOrderDTO,
|
||||
DisplayOrderItemDTO,
|
||||
DisplayOrderItemSubsetDTO,
|
||||
} from '@generated/swagger/oms-api';
|
||||
import { BreadcrumbService } from '@core/breadcrumb';
|
||||
import { ApplicationService } from '@core/application';
|
||||
import { DomainPrinterService } from '@domain/printer';
|
||||
@@ -49,45 +53,73 @@ export class CheckoutSummaryComponent implements OnInit, OnDestroy {
|
||||
private _onDestroy$ = new Subject<void>();
|
||||
processId = Date.now();
|
||||
selectedDate = this.dateAdapter.today();
|
||||
minDateDatepicker = this.dateAdapter.addCalendarDays(this.dateAdapter.today(), -1);
|
||||
minDateDatepicker = this.dateAdapter.addCalendarDays(
|
||||
this.dateAdapter.today(),
|
||||
-1,
|
||||
);
|
||||
|
||||
updatingPreferredPickUpDate$ = new BehaviorSubject<Record<string, string>>({});
|
||||
updatingPreferredPickUpDate$ = new BehaviorSubject<Record<string, string>>(
|
||||
{},
|
||||
);
|
||||
|
||||
displayOrders$ = combineLatest([this.domainCheckoutService.getOrders(), this._route.params]).pipe(
|
||||
displayOrders$ = combineLatest([
|
||||
this.domainCheckoutService.getOrders(),
|
||||
this._route.params,
|
||||
]).pipe(
|
||||
map(([orders, params]) => {
|
||||
let filteredOrders: DisplayOrderDTO[] = [];
|
||||
if (params?.orderIds) {
|
||||
const orderIds: string[] = params.orderIds.split(',');
|
||||
filteredOrders = orders.filter((order) => orderIds.find((id) => Number(id) === order.id));
|
||||
filteredOrders = orders.filter((order) =>
|
||||
orderIds.find((id) => Number(id) === order.id),
|
||||
);
|
||||
} else {
|
||||
return filteredOrders;
|
||||
}
|
||||
|
||||
// Ticket #4228 Für die korrekte Gruppierung der Items bei gleichem Bestellziel (Aufsplitten von Abholung und Rücklage)
|
||||
const ordersWithMultipleFeatures = filteredOrders.filter((order) =>
|
||||
order.items.find((item) => item.features.orderType !== order.features.orderType),
|
||||
order.items.find(
|
||||
(item) => item.features.orderType !== order.features.orderType,
|
||||
),
|
||||
);
|
||||
|
||||
if (ordersWithMultipleFeatures) {
|
||||
for (let orderWithMultipleFeatures of ordersWithMultipleFeatures) {
|
||||
if (orderWithMultipleFeatures?.items?.length > 1) {
|
||||
const itemsWithOrderFeature = orderWithMultipleFeatures.items.filter(
|
||||
(item) => item.features.orderType === orderWithMultipleFeatures.features.orderType,
|
||||
);
|
||||
const itemsWithDifferentOrderFeature = orderWithMultipleFeatures.items.filter(
|
||||
(item) => item.features.orderType !== orderWithMultipleFeatures.features.orderType,
|
||||
);
|
||||
const itemsWithOrderFeature =
|
||||
orderWithMultipleFeatures.items.filter(
|
||||
(item) =>
|
||||
item.features.orderType ===
|
||||
orderWithMultipleFeatures.features.orderType,
|
||||
);
|
||||
const itemsWithDifferentOrderFeature =
|
||||
orderWithMultipleFeatures.items.filter(
|
||||
(item) =>
|
||||
item.features.orderType !==
|
||||
orderWithMultipleFeatures.features.orderType,
|
||||
);
|
||||
|
||||
filteredOrders = [...filteredOrders.filter((order) => order.id !== orderWithMultipleFeatures.id)];
|
||||
filteredOrders = [
|
||||
...filteredOrders.filter(
|
||||
(order) => order.id !== orderWithMultipleFeatures.id,
|
||||
),
|
||||
];
|
||||
|
||||
if (itemsWithOrderFeature?.length > 0) {
|
||||
filteredOrders = [...filteredOrders, { ...orderWithMultipleFeatures, items: itemsWithOrderFeature }];
|
||||
filteredOrders = [
|
||||
...filteredOrders,
|
||||
{ ...orderWithMultipleFeatures, items: itemsWithOrderFeature },
|
||||
];
|
||||
}
|
||||
|
||||
if (itemsWithDifferentOrderFeature?.length > 0) {
|
||||
filteredOrders = [
|
||||
...filteredOrders,
|
||||
{ ...orderWithMultipleFeatures, items: itemsWithDifferentOrderFeature },
|
||||
{
|
||||
...orderWithMultipleFeatures,
|
||||
items: itemsWithDifferentOrderFeature,
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -97,7 +129,9 @@ export class CheckoutSummaryComponent implements OnInit, OnDestroy {
|
||||
return filteredOrders?.map((order) => {
|
||||
return {
|
||||
...order,
|
||||
items: [...order.items]?.sort((a, b) => a.product?.name.localeCompare(b.product?.name)),
|
||||
items: [...order.items]?.sort((a, b) =>
|
||||
a.product?.name.localeCompare(b.product?.name),
|
||||
),
|
||||
};
|
||||
});
|
||||
}),
|
||||
@@ -105,14 +139,23 @@ export class CheckoutSummaryComponent implements OnInit, OnDestroy {
|
||||
);
|
||||
|
||||
hasAbholung$ = this.displayOrders$.pipe(
|
||||
map((displayOrders) => displayOrders.filter((order) => order.features?.orderType === 'Abholung')?.length > 0),
|
||||
map(
|
||||
(displayOrders) =>
|
||||
displayOrders.filter(
|
||||
(order) => order.features?.orderType === 'Abholung',
|
||||
)?.length > 0,
|
||||
),
|
||||
);
|
||||
|
||||
totalItemCount$ = this.displayOrders$.pipe(
|
||||
map((displayOrders) =>
|
||||
displayOrders.reduce(
|
||||
(total, displayOrder) =>
|
||||
total + displayOrder?.items?.reduce((subTotal, order) => subTotal + order?.quantity, 0),
|
||||
total +
|
||||
displayOrder?.items?.reduce(
|
||||
(subTotal, order) => subTotal + order?.quantity,
|
||||
0,
|
||||
),
|
||||
0,
|
||||
),
|
||||
),
|
||||
@@ -121,7 +164,10 @@ export class CheckoutSummaryComponent implements OnInit, OnDestroy {
|
||||
totalReadingPoints$ = this.displayOrders$.pipe(
|
||||
switchMap((displayOrders) => {
|
||||
const items = displayOrders
|
||||
.reduce<DisplayOrderItemDTO[]>((items, order) => [...items, ...order.items], [])
|
||||
.reduce<DisplayOrderItemDTO[]>(
|
||||
(items, order) => [...items, ...order.items],
|
||||
[],
|
||||
)
|
||||
.map((i) => {
|
||||
if (i?.product?.catalogProductNumber) {
|
||||
return {
|
||||
@@ -135,7 +181,14 @@ export class CheckoutSummaryComponent implements OnInit, OnDestroy {
|
||||
if (items.length !== 0) {
|
||||
return this.domainCatalogService
|
||||
.getPromotionPoints({ items })
|
||||
.pipe(map((response) => Object.values(response.result).reduce((sum, points) => sum + points, 0)));
|
||||
.pipe(
|
||||
map((response) =>
|
||||
Object.values(response.result).reduce(
|
||||
(sum, points) => sum + points,
|
||||
0,
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return NEVER;
|
||||
}
|
||||
@@ -147,7 +200,11 @@ export class CheckoutSummaryComponent implements OnInit, OnDestroy {
|
||||
displayOrders.reduce(
|
||||
(total, displayOrder) =>
|
||||
total +
|
||||
displayOrder?.items?.reduce((subTotal, order) => subTotal + order?.price?.value?.value * order.quantity, 0),
|
||||
displayOrder?.items?.reduce(
|
||||
(subTotal, order) =>
|
||||
subTotal + order?.price?.value?.value * order.quantity,
|
||||
0,
|
||||
),
|
||||
0,
|
||||
),
|
||||
),
|
||||
@@ -162,22 +219,33 @@ export class CheckoutSummaryComponent implements OnInit, OnDestroy {
|
||||
containsDeliveryOrder$ = this.displayOrders$.pipe(
|
||||
map(
|
||||
(displayOrders) =>
|
||||
displayOrders.filter((o) => ['Versand', 'B2B-Versand', 'DIG-Versand'].indexOf(o.features?.orderType) > -1)
|
||||
?.length > 0,
|
||||
displayOrders.filter(
|
||||
(o) =>
|
||||
['Versand', 'B2B-Versand', 'DIG-Versand'].indexOf(
|
||||
o.features?.orderType,
|
||||
) > -1,
|
||||
)?.length > 0,
|
||||
),
|
||||
);
|
||||
|
||||
customer$ = this.displayOrders$.pipe(
|
||||
switchMap((o) => this.customerService.getCustomers(o[0].buyerNumber, { take: 5 })),
|
||||
switchMap((o) =>
|
||||
this.customerService.getCustomers(o[0].buyerNumber, { take: 5 }),
|
||||
),
|
||||
map((customers) => customers.result[0]),
|
||||
shareReplay(),
|
||||
);
|
||||
|
||||
isB2BCustomer$ = this.customer$.pipe(map((customer) => customer?.features?.find((f) => f.key === 'b2b') != null));
|
||||
isB2BCustomer$ = this.customer$.pipe(
|
||||
map((customer) => customer?.features?.find((f) => f.key === 'b2b') != null),
|
||||
);
|
||||
|
||||
takeNowOrders$ = this.displayOrders$.pipe(
|
||||
map((displayOrders) =>
|
||||
displayOrders.filter((o) => o.items.find((oi) => oi.features?.orderType === 'Rücklage') != null),
|
||||
displayOrders.filter(
|
||||
(o) =>
|
||||
o.items.find((oi) => oi.features?.orderType === 'Rücklage') != null,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -210,7 +278,9 @@ export class CheckoutSummaryComponent implements OnInit, OnDestroy {
|
||||
private _cdr: ChangeDetectorRef,
|
||||
) {
|
||||
this.breadcrumb
|
||||
.getBreadcrumbsByKeyAndTags$(this.applicationService.activatedProcessId, ['checkout'])
|
||||
.getBreadcrumbsByKeyAndTags$(this.applicationService.activatedProcessId, [
|
||||
'checkout',
|
||||
])
|
||||
.pipe(first())
|
||||
.subscribe(async (crumbs) => {
|
||||
for await (const crumb of crumbs) {
|
||||
@@ -264,7 +334,8 @@ export class CheckoutSummaryComponent implements OnInit, OnDestroy {
|
||||
getProductSearchDetailsQueryParams(item: DisplayOrderItemDTO) {
|
||||
return {
|
||||
main_qs: item?.product?.ean,
|
||||
filter_format: item?.features?.orderType === 'Download' ? 'eb;dl' : undefined,
|
||||
filter_format:
|
||||
item?.features?.orderType === 'Download' ? 'eb;dl' : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -274,9 +345,14 @@ export class CheckoutSummaryComponent implements OnInit, OnDestroy {
|
||||
try {
|
||||
const items = item ? [item] : await this.getAllOrderItems();
|
||||
const subsetItems = items
|
||||
.filter((item) => ['Rücklage', 'Abholung'].includes(item.features.orderType))
|
||||
.filter((item) =>
|
||||
['Rücklage', 'Abholung'].includes(item.features.orderType),
|
||||
)
|
||||
// .flatMap((item) => item.subsetItems);
|
||||
.reduce<DisplayOrderItemSubsetDTO[]>((acc, item) => [...acc, ...item.subsetItems], []);
|
||||
.reduce<DisplayOrderItemSubsetDTO[]>(
|
||||
(acc, item) => [...acc, ...item.subsetItems],
|
||||
[],
|
||||
);
|
||||
subsetItems.forEach((item) => (data[`${item.id}`] = date?.toISOString()));
|
||||
|
||||
try {
|
||||
@@ -310,7 +386,10 @@ export class CheckoutSummaryComponent implements OnInit, OnDestroy {
|
||||
|
||||
async getAllOrderItems() {
|
||||
const orders = await this.displayOrders$.pipe(first()).toPromise();
|
||||
return orders.reduce<DisplayOrderItemDTO[]>((agg, order) => [...agg, ...order.items], []);
|
||||
return orders.reduce<DisplayOrderItemDTO[]>(
|
||||
(agg, order) => [...agg, ...order.items],
|
||||
[],
|
||||
);
|
||||
}
|
||||
|
||||
async updateDisplayOrderItem(item: DisplayOrderItemDTO) {
|
||||
@@ -322,9 +401,16 @@ export class CheckoutSummaryComponent implements OnInit, OnDestroy {
|
||||
if (takeNowOrders.length != 1) return;
|
||||
|
||||
try {
|
||||
await this.router.navigate(this._shelfOutNavigationService.listRoute({ processId: Date.now() }).path, {
|
||||
queryParams: { main_qs: takeNowOrders[0].orderNumber, filter_supplier_id: '16' },
|
||||
});
|
||||
await this.router.navigate(
|
||||
this._shelfOutNavigationService.listRoute({ processId: Date.now() })
|
||||
.path,
|
||||
{
|
||||
queryParams: {
|
||||
main_qs: takeNowOrders[0].orderNumber,
|
||||
filter_supplier_id: '16',
|
||||
},
|
||||
},
|
||||
);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
@@ -344,7 +430,8 @@ export class CheckoutSummaryComponent implements OnInit, OnDestroy {
|
||||
.pipe(
|
||||
first(),
|
||||
map((printers) => {
|
||||
if (Array.isArray(printers)) return printers.find((printer) => printer.selected === true);
|
||||
if (Array.isArray(printers))
|
||||
return printers.find((printer) => printer.selected === true);
|
||||
}),
|
||||
)
|
||||
.toPromise();
|
||||
@@ -362,10 +449,16 @@ export class CheckoutSummaryComponent implements OnInit, OnDestroy {
|
||||
const result = await this.domainPrinterService
|
||||
.printOrder({ orderIds: orders.map((o) => o.id), printer })
|
||||
.toPromise();
|
||||
this._toaster.open({ type: 'success', message: 'Bestellbestätigung wurde gedruckt' });
|
||||
this._toaster.open({
|
||||
type: 'success',
|
||||
message: 'Bestellbestätigung wurde gedruckt',
|
||||
});
|
||||
return result;
|
||||
} catch (error) {
|
||||
this._toaster.open({ type: 'danger', message: 'Fehler beim Drucken der Bestellbestätigung' });
|
||||
this._toaster.open({
|
||||
type: 'danger',
|
||||
message: 'Fehler beim Drucken der Bestellbestätigung',
|
||||
});
|
||||
} finally {
|
||||
this.isPrinting$.next(false);
|
||||
}
|
||||
@@ -381,12 +474,21 @@ export class CheckoutSummaryComponent implements OnInit, OnDestroy {
|
||||
} else {
|
||||
try {
|
||||
const result = await this.domainPrinterService
|
||||
.printOrder({ orderIds: orders.map((o) => o.id), printer: selectedPrinter.key })
|
||||
.printOrder({
|
||||
orderIds: orders.map((o) => o.id),
|
||||
printer: selectedPrinter.key,
|
||||
})
|
||||
.toPromise();
|
||||
this._toaster.open({ type: 'success', message: 'Bestellbestätigung wurde gedruckt' });
|
||||
this._toaster.open({
|
||||
type: 'success',
|
||||
message: 'Bestellbestätigung wurde gedruckt',
|
||||
});
|
||||
return result;
|
||||
} catch (error) {
|
||||
this._toaster.open({ type: 'danger', message: 'Fehler beim Drucken der Bestellbestätigung' });
|
||||
this._toaster.open({
|
||||
type: 'danger',
|
||||
message: 'Fehler beim Drucken der Bestellbestätigung',
|
||||
});
|
||||
} finally {
|
||||
this.isPrinting$.next(false);
|
||||
}
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
<ng-container *ngIf="orderItem$ | async; let orderItem">
|
||||
@if (orderItem$ | async; as orderItem) {
|
||||
<div #features class="page-customer-order-details-item__features">
|
||||
<ng-container *ngIf="orderItem?.features?.prebooked">
|
||||
@if (orderItem?.features?.prebooked) {
|
||||
<img [uiOverlayTrigger]="prebookedTooltip" src="/assets/images/tag_icon_preorder.svg" [alt]="orderItem?.features?.prebooked" />
|
||||
<ui-tooltip yPosition="above" xPosition="after" [yOffset]="-11" [xOffset]="-8" #prebookedTooltip [closeable]="true">
|
||||
Artikel wird für Sie vorgemerkt.
|
||||
</ui-tooltip>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="notificationsSent$ | async; let notificationsSent">
|
||||
<ng-container *ngIf="notificationsSent?.NOTIFICATION_EMAIL">
|
||||
}
|
||||
@if (notificationsSent$ | async; as notificationsSent) {
|
||||
@if (notificationsSent?.NOTIFICATION_EMAIL) {
|
||||
<img [uiOverlayTrigger]="emailTooltip" src="/assets/images/email_bookmark.svg" />
|
||||
<ui-tooltip yPosition="above" xPosition="after" [yOffset]="-11" [xOffset]="-8" #emailTooltip [closeable]="true">
|
||||
Per E-Mail benachrichtigt
|
||||
<br />
|
||||
<ng-container *ngFor="let notification of notificationsSent?.NOTIFICATION_EMAIL">
|
||||
@for (notification of notificationsSent?.NOTIFICATION_EMAIL; track notification) {
|
||||
{{ notification | date: 'dd.MM.yyyy | HH:mm' }} Uhr
|
||||
<br />
|
||||
</ng-container>
|
||||
}
|
||||
</ui-tooltip>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="notificationsSent?.NOTIFICATION_SMS">
|
||||
}
|
||||
@if (notificationsSent?.NOTIFICATION_SMS) {
|
||||
<img [uiOverlayTrigger]="smsTooltip" src="/assets/images/sms_bookmark.svg" />
|
||||
<ui-tooltip yPosition="above" xPosition="after" [yOffset]="-11" [xOffset]="-8" #smsTooltip [closeable]="true">
|
||||
Per SMS benachrichtigt
|
||||
<br />
|
||||
<ng-container *ngFor="let notification of notificationsSent?.NOTIFICATION_SMS">
|
||||
@for (notification of notificationsSent?.NOTIFICATION_SMS; track notification) {
|
||||
{{ notification | date: 'dd.MM.yyyy | HH:mm' }} Uhr
|
||||
<br />
|
||||
</ng-container>
|
||||
}
|
||||
</ui-tooltip>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
<div class="page-customer-order-details-item__item-container">
|
||||
<div class="page-customer-order-details-item__thumbnail">
|
||||
@@ -42,169 +42,190 @@
|
||||
#elementDistance="uiElementDistance"
|
||||
[style.max-width.px]="elementDistance.distanceChange | async"
|
||||
class="flex flex-col"
|
||||
>
|
||||
>
|
||||
<div class="font-normal mb-[0.375rem]">{{ orderItem.product?.contributors }}</div>
|
||||
<div>{{ orderItem.product?.name }}</div>
|
||||
</h3>
|
||||
<div class="history-wrapper flex flex-col items-end justify-center">
|
||||
<button class="cta-history text-p1" (click)="historyClick.emit(orderItem)">Historie</button>
|
||||
|
||||
<input
|
||||
*ngIf="selectable$ | async"
|
||||
[ngModel]="selected$ | async"
|
||||
(ngModelChange)="setSelected($event)"
|
||||
class="isa-select-bullet mt-4"
|
||||
type="checkbox"
|
||||
/>
|
||||
@if (selectable$ | async) {
|
||||
<input
|
||||
[ngModel]="selected$ | async"
|
||||
(ngModelChange)="setSelected($event)"
|
||||
class="isa-select-bullet mt-4"
|
||||
type="checkbox"
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail">
|
||||
<div class="label">Menge</div>
|
||||
<div class="value">
|
||||
<ng-container *ngIf="!(canChangeQuantity$ | async)">{{ orderItem?.quantity }}x</ng-container>
|
||||
<ui-quantity-dropdown
|
||||
*ngIf="canChangeQuantity$ | async"
|
||||
[showTrash]="false"
|
||||
[range]="orderItem?.quantity"
|
||||
[(ngModel)]="quantity"
|
||||
[showSpinner]="false"
|
||||
></ui-quantity-dropdown>
|
||||
@if (!(canChangeQuantity$ | async)) {
|
||||
{{ orderItem?.quantity }}x
|
||||
}
|
||||
@if (canChangeQuantity$ | async) {
|
||||
<ui-quantity-dropdown
|
||||
[showTrash]="false"
|
||||
[range]="orderItem?.quantity"
|
||||
[(ngModel)]="quantity"
|
||||
[showSpinner]="false"
|
||||
></ui-quantity-dropdown>
|
||||
}
|
||||
<span class="overall-quantity">(von {{ orderItem?.overallQuantity }})</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail" *ngIf="!!orderItem.product?.formatDetail">
|
||||
<div class="label">Format</div>
|
||||
<div class="value">
|
||||
<img
|
||||
*ngIf="orderItem?.product?.format && orderItem?.product?.format !== 'UNKNOWN'"
|
||||
class="format-icon"
|
||||
[src]="'/assets/images/Icon_' + orderItem.product?.format + '.svg'"
|
||||
alt="format icon"
|
||||
/>
|
||||
<span>{{ orderItem.product?.formatDetail }}</span>
|
||||
@if (!!orderItem.product?.formatDetail) {
|
||||
<div class="detail">
|
||||
<div class="label">Format</div>
|
||||
<div class="value">
|
||||
@if (orderItem?.product?.format && orderItem?.product?.format !== 'UNKNOWN') {
|
||||
<img
|
||||
class="format-icon"
|
||||
[src]="'/assets/images/Icon_' + orderItem.product?.format + '.svg'"
|
||||
alt="format icon"
|
||||
/>
|
||||
}
|
||||
<span>{{ orderItem.product?.formatDetail }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="detail" *ngIf="!!orderItem.product?.ean">
|
||||
<div class="label">ISBN/EAN</div>
|
||||
<div class="value">{{ orderItem.product?.ean }}</div>
|
||||
</div>
|
||||
<div class="detail" *ngIf="orderItem.price !== undefined">
|
||||
<div class="label">Preis</div>
|
||||
<div class="value">{{ orderItem.price | currency: 'EUR' }}</div>
|
||||
</div>
|
||||
<div class="detail" *ngIf="!!orderItem.retailPrice?.vat?.inPercent">
|
||||
<div class="label">MwSt</div>
|
||||
<div class="value">{{ orderItem.retailPrice?.vat?.inPercent }}%</div>
|
||||
</div>
|
||||
|
||||
}
|
||||
@if (!!orderItem.product?.ean) {
|
||||
<div class="detail">
|
||||
<div class="label">ISBN/EAN</div>
|
||||
<div class="value">{{ orderItem.product?.ean }}</div>
|
||||
</div>
|
||||
}
|
||||
@if (orderItem.price !== undefined) {
|
||||
<div class="detail">
|
||||
<div class="label">Preis</div>
|
||||
<div class="value">{{ orderItem.price | currency: 'EUR' }}</div>
|
||||
</div>
|
||||
}
|
||||
@if (!!orderItem.retailPrice?.vat?.inPercent) {
|
||||
<div class="detail">
|
||||
<div class="label">MwSt</div>
|
||||
<div class="value">{{ orderItem.retailPrice?.vat?.inPercent }}%</div>
|
||||
</div>
|
||||
}
|
||||
<hr class="border-[#EDEFF0] border-t-2 my-4" />
|
||||
|
||||
<div class="detail" *ngIf="orderItem.supplier">
|
||||
<div class="label">Lieferant</div>
|
||||
<div class="value">{{ orderItem.supplier }}</div>
|
||||
</div>
|
||||
<div class="detail" *ngIf="!!orderItem.ssc || !!orderItem.sscText">
|
||||
<div class="label">Meldenummer</div>
|
||||
<div class="value">{{ orderItem.ssc }} - {{ orderItem.sscText }}</div>
|
||||
</div>
|
||||
<div class="detail" *ngIf="!!orderItem.targetBranch">
|
||||
<div class="label">Zielfiliale</div>
|
||||
<div class="value">{{ orderItem.targetBranch }}</div>
|
||||
</div>
|
||||
@if (orderItem.supplier) {
|
||||
<div class="detail">
|
||||
<div class="label">Lieferant</div>
|
||||
<div class="value">{{ orderItem.supplier }}</div>
|
||||
</div>
|
||||
}
|
||||
@if (!!orderItem.ssc || !!orderItem.sscText) {
|
||||
<div class="detail">
|
||||
<div class="label">Meldenummer</div>
|
||||
<div class="value">{{ orderItem.ssc }} - {{ orderItem.sscText }}</div>
|
||||
</div>
|
||||
}
|
||||
@if (!!orderItem.targetBranch) {
|
||||
<div class="detail">
|
||||
<div class="label">Zielfiliale</div>
|
||||
<div class="value">{{ orderItem.targetBranch }}</div>
|
||||
</div>
|
||||
}
|
||||
<div class="detail">
|
||||
<div class="label">
|
||||
<ng-container
|
||||
*ngIf="
|
||||
orderItemFeature(orderItem) === 'Versand' ||
|
||||
orderItemFeature(orderItem) === 'B2B-Versand' ||
|
||||
orderItemFeature(orderItem) === 'DIG-Versand'
|
||||
"
|
||||
>
|
||||
@if (
|
||||
orderItemFeature(orderItem) === 'Versand' ||
|
||||
orderItemFeature(orderItem) === 'B2B-Versand' ||
|
||||
orderItemFeature(orderItem) === 'DIG-Versand'
|
||||
) {
|
||||
{{ orderItem?.estimatedDelivery ? 'Lieferung zwischen' : 'Lieferung ab' }}
|
||||
</ng-container>
|
||||
<ng-container *ngIf="orderItemFeature(orderItem) === 'Abholung' || orderItemFeature(orderItem) === 'Rücklage'">
|
||||
}
|
||||
@if (orderItemFeature(orderItem) === 'Abholung' || orderItemFeature(orderItem) === 'Rücklage') {
|
||||
Abholung ab
|
||||
</ng-container>
|
||||
}
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="!!orderItem?.estimatedDelivery || !!orderItem?.estimatedShippingDate">
|
||||
@if (!!orderItem?.estimatedDelivery || !!orderItem?.estimatedShippingDate) {
|
||||
<div class="value bg-[#D8DFE5] rounded w-max px-2">
|
||||
<ng-container *ngIf="!!orderItem?.estimatedDelivery; else estimatedShippingDate">
|
||||
@if (!!orderItem?.estimatedDelivery) {
|
||||
{{ orderItem?.estimatedDelivery?.start | date: 'dd.MM.yy' }} und
|
||||
{{ orderItem?.estimatedDelivery?.stop | date: 'dd.MM.yy' }}
|
||||
</ng-container>
|
||||
} @else {
|
||||
@if (!!orderItem?.estimatedShippingDate) {
|
||||
{{ orderItem?.estimatedShippingDate | date: 'dd.MM.yy' }}
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-template #estimatedShippingDate>
|
||||
<ng-container *ngIf="!!orderItem?.estimatedShippingDate">
|
||||
{{ orderItem?.estimatedShippingDate | date: 'dd.MM.yy' }}
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
}
|
||||
</div>
|
||||
<div class="page-customer-order-details-item__tracking-details" *ngIf="getOrderItemTrackingData(orderItem); let trackingData">
|
||||
<div class="label">{{ trackingData.length > 1 ? 'Sendungsnummern' : 'Sendungsnummer' }}</div>
|
||||
<ng-container *ngFor="let tracking of trackingData">
|
||||
<ng-container *ngIf="tracking.trackingProvider === 'DHL' && !isNative; else noTrackingLink">
|
||||
<a class="value text-[#0556B4]" [href]="getTrackingNumberLink(tracking.trackingNumber)" target="_blank">
|
||||
{{ tracking.trackingProvider }}: {{ tracking.trackingNumber }}
|
||||
</a>
|
||||
</ng-container>
|
||||
<ng-template #noTrackingLink>
|
||||
<p class="value">{{ tracking.trackingProvider }}: {{ tracking.trackingNumber }}</p>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
@if (getOrderItemTrackingData(orderItem); as trackingData) {
|
||||
<div class="page-customer-order-details-item__tracking-details">
|
||||
<div class="label">{{ trackingData.length > 1 ? 'Sendungsnummern' : 'Sendungsnummer' }}</div>
|
||||
@for (tracking of trackingData; track tracking) {
|
||||
@if (tracking.trackingProvider === 'DHL' && !isNative) {
|
||||
<a class="value text-[#0556B4]" [href]="getTrackingNumberLink(tracking.trackingNumber)" target="_blank">
|
||||
{{ tracking.trackingProvider }}: {{ tracking.trackingNumber }}
|
||||
</a>
|
||||
} @else {
|
||||
<p class="value">{{ tracking.trackingProvider }}: {{ tracking.trackingNumber }}</p>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
<hr class="border-[#EDEFF0] border-t-2 my-4" />
|
||||
|
||||
<div class="detail" *ngIf="!!orderItem?.compartmentCode">
|
||||
<div class="label">Abholfachnr.</div>
|
||||
<div class="value">{{ orderItem?.compartmentCode }}</div>
|
||||
</div>
|
||||
@if (!!orderItem?.compartmentCode) {
|
||||
<div class="detail">
|
||||
<div class="label">Abholfachnr.</div>
|
||||
<div class="value">{{ orderItem?.compartmentCode }}</div>
|
||||
</div>
|
||||
}
|
||||
<div class="detail">
|
||||
<div class="label">Vormerker</div>
|
||||
<div class="value">{{ orderItem.isPrebooked ? 'Ja' : 'Nein' }}</div>
|
||||
</div>
|
||||
|
||||
<hr class="border-[#EDEFF0] border-t-2 my-4" />
|
||||
|
||||
<div class="detail" *ngIf="!!orderItem.paymentProcessing">
|
||||
<div class="label">Zahlungsweg</div>
|
||||
<div class="value">{{ orderItem.paymentProcessing || '-' }}</div>
|
||||
</div>
|
||||
<div class="detail" *ngIf="!!orderItem.paymentType">
|
||||
<div class="label">Zahlungsart</div>
|
||||
<div class="value">{{ orderItem.paymentType | paymentType }}</div>
|
||||
</div>
|
||||
|
||||
<h4 class="receipt-header" *ngIf="receiptCount$ | async; let count">
|
||||
{{ count > 1 ? 'Belege' : 'Beleg' }}
|
||||
</h4>
|
||||
<ng-container *ngFor="let receipt of receipts$ | async">
|
||||
<div class="detail" *ngIf="!!receipt?.receiptNumber">
|
||||
<div class="label">Belegnummer</div>
|
||||
<div class="value">{{ receipt?.receiptNumber }}</div>
|
||||
@if (!!orderItem.paymentProcessing) {
|
||||
<div class="detail">
|
||||
<div class="label">Zahlungsweg</div>
|
||||
<div class="value">{{ orderItem.paymentProcessing || '-' }}</div>
|
||||
</div>
|
||||
<div class="detail" *ngIf="!!receipt?.printedDate">
|
||||
<div class="label">Erstellt am</div>
|
||||
<div class="value">{{ receipt?.printedDate | date: 'dd.MM.yy | HH:mm' }} Uhr</div>
|
||||
}
|
||||
@if (!!orderItem.paymentType) {
|
||||
<div class="detail">
|
||||
<div class="label">Zahlungsart</div>
|
||||
<div class="value">{{ orderItem.paymentType | paymentType }}</div>
|
||||
</div>
|
||||
<div class="detail" *ngIf="!!receipt?.receiptText">
|
||||
<div class="label">Rechnungstext</div>
|
||||
<div class="value">{{ receipt?.receiptText || '-' }}</div>
|
||||
</div>
|
||||
<div class="detail" *ngIf="!!receipt?.receiptType">
|
||||
<div class="label">Belegart</div>
|
||||
<div class="value">
|
||||
{{ receipt?.receiptType === 1 ? 'Lieferschein' : receipt?.receiptType === 64 ? 'Zahlungsbeleg' : '-' }}
|
||||
}
|
||||
@if (receiptCount$ | async; as count) {
|
||||
<h4 class="receipt-header">
|
||||
{{ count > 1 ? 'Belege' : 'Beleg' }}
|
||||
</h4>
|
||||
}
|
||||
@for (receipt of receipts$ | async; track receipt) {
|
||||
@if (!!receipt?.receiptNumber) {
|
||||
<div class="detail">
|
||||
<div class="label">Belegnummer</div>
|
||||
<div class="value">{{ receipt?.receiptNumber }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
}
|
||||
@if (!!receipt?.printedDate) {
|
||||
<div class="detail">
|
||||
<div class="label">Erstellt am</div>
|
||||
<div class="value">{{ receipt?.printedDate | date: 'dd.MM.yy | HH:mm' }} Uhr</div>
|
||||
</div>
|
||||
}
|
||||
@if (!!receipt?.receiptText) {
|
||||
<div class="detail">
|
||||
<div class="label">Rechnungstext</div>
|
||||
<div class="value">{{ receipt?.receiptText || '-' }}</div>
|
||||
</div>
|
||||
}
|
||||
@if (!!receipt?.receiptType) {
|
||||
<div class="detail">
|
||||
<div class="label">Belegart</div>
|
||||
<div class="value">
|
||||
{{ receipt?.receiptType === 1 ? 'Lieferschein' : receipt?.receiptType === 64 ? 'Zahlungsbeleg' : '-' }}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
<div class="page-customer-order-details-item__comment flex flex-col items-start mt-[1.625rem]">
|
||||
<div class="label mb-[0.375rem]">Anmerkung</div>
|
||||
|
||||
<div class="flex flex-row w-full">
|
||||
<textarea
|
||||
matInput
|
||||
@@ -222,27 +243,28 @@
|
||||
[formControl]="specialCommentControl"
|
||||
[class.inactive]="!specialCommentControl.dirty"
|
||||
></textarea>
|
||||
|
||||
<div class="comment-actions">
|
||||
<button
|
||||
type="reset"
|
||||
class="clear"
|
||||
*ngIf="!!specialCommentControl.value?.length"
|
||||
(click)="specialCommentControl.setValue(''); saveSpecialComment(); triggerResize()"
|
||||
>
|
||||
<shared-icon icon="close" [size]="24"></shared-icon>
|
||||
</button>
|
||||
<button
|
||||
class="cta-save"
|
||||
type="submit"
|
||||
*ngIf="specialCommentControl?.enabled && specialCommentControl.dirty"
|
||||
(click)="saveSpecialComment()"
|
||||
>
|
||||
Speichern
|
||||
</button>
|
||||
@if (!!specialCommentControl.value?.length) {
|
||||
<button
|
||||
type="reset"
|
||||
class="clear"
|
||||
(click)="specialCommentControl.setValue(''); saveSpecialComment(); triggerResize()"
|
||||
>
|
||||
<shared-icon icon="close" [size]="24"></shared-icon>
|
||||
</button>
|
||||
}
|
||||
@if (specialCommentControl?.enabled && specialCommentControl.dirty) {
|
||||
<button
|
||||
class="cta-save"
|
||||
type="submit"
|
||||
(click)="saveSpecialComment()"
|
||||
>
|
||||
Speichern
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
}
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
<div class="page-customer-order-details-tags__wrapper">
|
||||
<button
|
||||
class="page-customer-order-details-tags__tag"
|
||||
type="button"
|
||||
[class.selected]="tag === (selected$ | async) && !inputFocus.focused"
|
||||
*ngFor="let tag of defaultTags"
|
||||
(click)="setCompartmentInfo(tag)"
|
||||
>
|
||||
{{ tag }}
|
||||
</button>
|
||||
@for (tag of defaultTags; track tag) {
|
||||
<button
|
||||
class="page-customer-order-details-tags__tag"
|
||||
type="button"
|
||||
[class.selected]="tag === (selected$ | async) && !inputFocus.focused"
|
||||
(click)="setCompartmentInfo(tag)"
|
||||
>
|
||||
{{ tag }}
|
||||
</button>
|
||||
}
|
||||
<button
|
||||
(click)="inputFocus.focus()"
|
||||
type="button"
|
||||
class="page-customer-order-details-tags__tag"
|
||||
[class.selected]="(inputValue$ | async) === (selected$ | async) && (inputValue$ | async)"
|
||||
>
|
||||
>
|
||||
<input
|
||||
#inputFocus="uiFocus"
|
||||
uiFocus
|
||||
@@ -23,6 +24,6 @@
|
||||
placeholder="..."
|
||||
[size]="controlSize$ | async"
|
||||
maxlength="15"
|
||||
/>
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { NgFor, AsyncPipe } from '@angular/common';
|
||||
import { AsyncPipe } from '@angular/common';
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, forwardRef } from '@angular/core';
|
||||
import { ControlValueAccessor, FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { UiCommonModule } from '@ui/common';
|
||||
@@ -14,7 +14,7 @@ import { PickUpShelfDetailsTagsComponent } from '../../../pickup-shelf/shared/pi
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
host: { class: 'page-pickup-shelf-details-tags' },
|
||||
standalone: true,
|
||||
imports: [NgFor, UiCommonModule, FormsModule, AsyncPipe, MatomoModule],
|
||||
imports: [UiCommonModule, FormsModule, AsyncPipe, MatomoModule],
|
||||
providers: [
|
||||
{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => PickUpShelfDetailsTagsComponent), multi: true },
|
||||
],
|
||||
|
||||
@@ -4,39 +4,44 @@
|
||||
(handleAction)="handleAction($event)"
|
||||
[order]="order$ | async"
|
||||
></page-customer-order-details-header>
|
||||
<page-customer-order-details-item
|
||||
class="mb-px-2"
|
||||
*ngFor="let item of items$ | async"
|
||||
[orderItem]="item"
|
||||
[order]="order$ | async"
|
||||
[selected]="true"
|
||||
(historyClick)="navigateToHistoryPage($event)"
|
||||
(specialCommentChanged)="onSpecialCommentChange()"
|
||||
></page-customer-order-details-item>
|
||||
<page-customer-order-details-tags *ngIf="showTagsComponent$ | async"></page-customer-order-details-tags>
|
||||
@for (item of items$ | async; track item) {
|
||||
<page-customer-order-details-item
|
||||
class="mb-px-2"
|
||||
[orderItem]="item"
|
||||
[order]="order$ | async"
|
||||
[selected]="true"
|
||||
(historyClick)="navigateToHistoryPage($event)"
|
||||
(specialCommentChanged)="onSpecialCommentChange()"
|
||||
></page-customer-order-details-item>
|
||||
}
|
||||
@if (showTagsComponent$ | async) {
|
||||
<page-customer-order-details-tags></page-customer-order-details-tags>
|
||||
}
|
||||
</div>
|
||||
<div class="page-customer-order-details__action-wrapper">
|
||||
<button
|
||||
[disabled]="addToPreviousCompartmentActionDisabled$ | async"
|
||||
*ngIf="addToPreviousCompartmentAction$ | async; let action"
|
||||
class="cta-action shadow-action"
|
||||
[class.cta-action-primary]="action.selected"
|
||||
[class.cta-action-secondary]="!action.selected"
|
||||
(click)="handleAction(action, { compartmentCode: latestCompartmentCode, compartmentInfo: latestCompartmentInfo })"
|
||||
>
|
||||
<ui-spinner [show]="(changeActionLoader$ | async) === action.command">
|
||||
{{ latestCompartmentCode$ | async | addToPreviousCompartmentCodeLabelPipe }} zubuchen
|
||||
</ui-spinner>
|
||||
</button>
|
||||
@if (addToPreviousCompartmentAction$ | async; as action) {
|
||||
<button
|
||||
[disabled]="addToPreviousCompartmentActionDisabled$ | async"
|
||||
class="cta-action shadow-action"
|
||||
[class.cta-action-primary]="action.selected"
|
||||
[class.cta-action-secondary]="!action.selected"
|
||||
(click)="handleAction(action, { compartmentCode: latestCompartmentCode, compartmentInfo: latestCompartmentInfo })"
|
||||
>
|
||||
<ui-spinner [show]="(changeActionLoader$ | async) === action.command">
|
||||
{{ latestCompartmentCode$ | async | addToPreviousCompartmentCodeLabelPipe }} zubuchen
|
||||
</ui-spinner>
|
||||
</button>
|
||||
}
|
||||
|
||||
<button
|
||||
[disabled]="actionsDisabled$ | async"
|
||||
class="cta-action shadow-action"
|
||||
[class.cta-action-primary]="action.selected"
|
||||
[class.cta-action-secondary]="!action.selected"
|
||||
*ngFor="let action of mainActions$ | async"
|
||||
(click)="handleAction(action)"
|
||||
>
|
||||
<ui-spinner [show]="(changeActionLoader$ | async) === action.command">{{ action.label }}</ui-spinner>
|
||||
</button>
|
||||
@for (action of mainActions$ | async; track action) {
|
||||
<button
|
||||
[disabled]="actionsDisabled$ | async"
|
||||
class="cta-action shadow-action"
|
||||
[class.cta-action-primary]="action.selected"
|
||||
[class.cta-action-secondary]="!action.selected"
|
||||
(click)="handleAction(action)"
|
||||
>
|
||||
<ui-spinner [show]="(changeActionLoader$ | async) === action.command">{{ action.label }}</ui-spinner>
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
<div *ngIf="items$ | async; let items">
|
||||
<shared-goods-in-out-order-edit (navigation)="navigateToDetailsPage($event)" [items]="items"></shared-goods-in-out-order-edit>
|
||||
</div>
|
||||
@if (items$ | async; as items) {
|
||||
<div>
|
||||
<shared-goods-in-out-order-edit (navigation)="navigateToDetailsPage($event)" [items]="items"></shared-goods-in-out-order-edit>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
<div class="hidden desktop-large:block" [class.show-filter]="showFilter">
|
||||
<ng-container *ngIf="filter$ | async; let filter">
|
||||
@if (filter$ | async; as filter) {
|
||||
<div class="customer-orders-search-filter-content">
|
||||
<div class="w-full flex flex-row justify-end items-center">
|
||||
<button (click)="clearFilter(filter)" class="text-[#0556B4] p-4">Alle Filter entfernen</button>
|
||||
<a
|
||||
*ngIf="showFilterClose$ | async"
|
||||
class="text-black p-4 outline-none border-none bg-transparent"
|
||||
[routerLink]="closeFilterRoute"
|
||||
(click)="showFilter = false"
|
||||
queryParamsHandling="preserve"
|
||||
>
|
||||
<shared-icon icon="close" [size]="25"></shared-icon>
|
||||
</a>
|
||||
@if (showFilterClose$ | async) {
|
||||
<a
|
||||
class="text-black p-4 outline-none border-none bg-transparent"
|
||||
[routerLink]="closeFilterRoute"
|
||||
(click)="showFilter = false"
|
||||
queryParamsHandling="preserve"
|
||||
>
|
||||
<shared-icon icon="close" [size]="25"></shared-icon>
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="customer-orders-search-filter-content-main -mt-14 desktop-small:-mt-8 desktop-large:-mt-12">
|
||||
<h1 class="text-h3 text-[1.625rem] font-bold text-center pt-6 pb-10">Filter</h1>
|
||||
<shared-filter
|
||||
@@ -22,20 +22,18 @@
|
||||
(search)="applyFilter(filter)"
|
||||
[hint]="message$ | async"
|
||||
[scanner]="true"
|
||||
>
|
||||
>
|
||||
<page-order-branch-id-input *sharedFilterCustomInput="'order_branch_id'; let input" [input]="input"></page-order-branch-id-input>
|
||||
</shared-filter>
|
||||
</div>
|
||||
|
||||
<div class="cta-wrapper">
|
||||
<button class="cta-reset-filter" (click)="resetFilter()" [disabled]="loading$ | async">Filter zurücksetzen</button>
|
||||
|
||||
<button class="cta-apply-filter" (click)="applyFilter(filter)" [disabled]="(loading$ | async) || !hasSelectedOptions(filter)">
|
||||
<ui-spinner [show]="loading$ | async">Filter anwenden</ui-spinner>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
}
|
||||
</div>
|
||||
<div class="desktop-large:hidden" [class.hidden]="showFilter">
|
||||
<page-customer-order-search-main (showFilter)="showFilter = true"></page-customer-order-search-main>
|
||||
|
||||
@@ -11,53 +11,57 @@
|
||||
<br />
|
||||
oder scannen Sie die Kundenkarte.
|
||||
</p>
|
||||
<ng-container *ngIf="filter$ | async; let filter">
|
||||
<shared-filter-filter-group-main
|
||||
class="mb-8 w-full"
|
||||
*ngIf="!(isDesktop$ | async)"
|
||||
[inputGroup]="filter?.filter | group: 'main'"
|
||||
></shared-filter-filter-group-main>
|
||||
@if (filter$ | async; as filter) {
|
||||
@if (!(isDesktop$ | async)) {
|
||||
<shared-filter-filter-group-main
|
||||
class="mb-8 w-full"
|
||||
[inputGroup]="filter?.filter | group: 'main'"
|
||||
></shared-filter-filter-group-main>
|
||||
}
|
||||
<div class="flex flex-row px-12 justify-center desktop-large:px-0">
|
||||
<shared-filter-input-group-main
|
||||
class="block w-full mr-3 desktop-large:mx-auto"
|
||||
*ngIf="filter?.input | group: 'main'; let inputGroup"
|
||||
[inputGroup]="inputGroup"
|
||||
[loading]="loading$ | async"
|
||||
(search)="search(filter)"
|
||||
[hint]="message$ | async"
|
||||
[scanner]="true"
|
||||
></shared-filter-input-group-main>
|
||||
<button
|
||||
*ngIf="!(isDesktop$ | async)"
|
||||
(click)="showFilter.emit()"
|
||||
class="page-search-main__filter w-[6.75rem] h-14 rounded font-bold px-5 mb-4 text-lg bg-[#AEB7C1] flex flex-row flex-nowrap items-center justify-center"
|
||||
[class.active]="hasFilter$ | async"
|
||||
type="button"
|
||||
>
|
||||
<shared-icon class="mr-2" icon="filter-variant"></shared-icon>
|
||||
Filter
|
||||
</button>
|
||||
@if (filter?.input | group: 'main'; as inputGroup) {
|
||||
<shared-filter-input-group-main
|
||||
class="block w-full mr-3 desktop-large:mx-auto"
|
||||
[inputGroup]="inputGroup"
|
||||
[loading]="loading$ | async"
|
||||
(search)="search(filter)"
|
||||
[hint]="message$ | async"
|
||||
[scanner]="true"
|
||||
></shared-filter-input-group-main>
|
||||
}
|
||||
@if (!(isDesktop$ | async)) {
|
||||
<button
|
||||
(click)="showFilter.emit()"
|
||||
class="page-search-main__filter w-[6.75rem] h-14 rounded font-bold px-5 mb-4 text-lg bg-[#AEB7C1] flex flex-row flex-nowrap items-center justify-center"
|
||||
[class.active]="hasFilter$ | async"
|
||||
type="button"
|
||||
>
|
||||
<shared-icon class="mr-2" icon="filter-variant"></shared-icon>
|
||||
Filter
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex flex-col items-start ml-12 desktop-large:ml-8 py-6 bg-white overflow-hidden h-[calc(100%-21rem)] desktop-large:h-[calc(100%-15rem)]"
|
||||
>
|
||||
>
|
||||
<h3 class="text-p3 font-bold mb-3">Deine letzten Suchanfragen</h3>
|
||||
<ul class="flex flex-col justify-start overflow-hidden overflow-y-scroll items-start m-0 p-0 bg-white w-full">
|
||||
<li class="list-none pb-3" *ngFor="let query of history$ | async">
|
||||
<button
|
||||
class="flex flex-row items-center outline-none border-none bg-white text-black text-p2 m-0 p-0"
|
||||
(click)="setQueryHistory(filter, query)"
|
||||
>
|
||||
<shared-icon
|
||||
class="flex w-8 h-8 justify-center items-center mr-3 rounded-full text-black bg-[#edeff0]"
|
||||
icon="magnify"
|
||||
[size]="20"
|
||||
></shared-icon>
|
||||
<p class="m-0 p-0 whitespace-nowrap overflow-hidden overflow-ellipsis max-w-[25rem]">{{ query }}</p>
|
||||
</button>
|
||||
</li>
|
||||
@for (query of history$ | async; track query) {
|
||||
<li class="list-none pb-3">
|
||||
<button
|
||||
class="flex flex-row items-center outline-none border-none bg-white text-black text-p2 m-0 p-0"
|
||||
(click)="setQueryHistory(filter, query)"
|
||||
>
|
||||
<shared-icon
|
||||
class="flex w-8 h-8 justify-center items-center mr-3 rounded-full text-black bg-[#edeff0]"
|
||||
icon="magnify"
|
||||
[size]="20"
|
||||
></shared-icon>
|
||||
<p class="m-0 p-0 whitespace-nowrap overflow-hidden overflow-ellipsis max-w-[25rem]">{{ query }}</p>
|
||||
</button>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</ng-container>
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -5,19 +5,20 @@
|
||||
[routerLinkActive]="!isTablet && !primaryOutletActive ? 'active' : ''"
|
||||
[queryParams]="queryParams"
|
||||
(click)="isDesktopLarge ? scrollIntoView() : ''"
|
||||
>
|
||||
>
|
||||
<div
|
||||
class="page-customer-order-item__item-grid-container"
|
||||
[class.page-customer-order-item__item-grid-container-main]="primaryOutletActive"
|
||||
>
|
||||
>
|
||||
<div class="page-customer-order-item__item-thumbnail text-center mr-4 w-[3.125rem] h-[4.9375rem]">
|
||||
<img
|
||||
class="page-customer-order-item__item-image w-[3.125rem] max-h-[4.9375rem]"
|
||||
loading="lazy"
|
||||
*ngIf="item?.product?.ean | productImage; let productImage"
|
||||
[src]="productImage"
|
||||
[alt]="item?.product?.name"
|
||||
/>
|
||||
@if (item?.product?.ean | productImage; as productImage) {
|
||||
<img
|
||||
class="page-customer-order-item__item-image w-[3.125rem] max-h-[4.9375rem]"
|
||||
loading="lazy"
|
||||
[src]="productImage"
|
||||
[alt]="item?.product?.name"
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div
|
||||
@@ -27,7 +28,7 @@
|
||||
[class.text-p2]="item?.product?.name?.length >= 50 && isTablet"
|
||||
[class.text-p3]="item?.product?.name?.length >= 60 || !isTablet"
|
||||
[class.text-p4]="item?.product?.name?.length >= 100"
|
||||
>
|
||||
>
|
||||
{{ item?.product?.name }}
|
||||
</div>
|
||||
|
||||
@@ -35,88 +36,111 @@
|
||||
{{ item?.specialComment }}
|
||||
</div>
|
||||
|
||||
<div *ngIf="primaryOutletActive" class="page-customer-order-item__item-format desktop-small:text-p2">
|
||||
<div *ngIf="item?.product?.format && item?.product?.formatDetail" class="font-bold flex flex-row">
|
||||
<img
|
||||
class="mr-3"
|
||||
*ngIf="item?.product?.format !== '--'"
|
||||
loading="lazy"
|
||||
src="assets/images/Icon_{{ item?.product?.format }}.svg"
|
||||
[alt]="item?.product?.formatDetail"
|
||||
/>
|
||||
{{ item?.product?.formatDetail | substr: 30 }}
|
||||
@if (primaryOutletActive) {
|
||||
<div class="page-customer-order-item__item-format desktop-small:text-p2">
|
||||
@if (item?.product?.format && item?.product?.formatDetail) {
|
||||
<div class="font-bold flex flex-row">
|
||||
@if (item?.product?.format !== '--') {
|
||||
<img
|
||||
class="mr-3"
|
||||
loading="lazy"
|
||||
src="assets/images/Icon_{{ item?.product?.format }}.svg"
|
||||
[alt]="item?.product?.formatDetail"
|
||||
/>
|
||||
}
|
||||
{{ item?.product?.formatDetail | substr: 30 }}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div *ngIf="primaryOutletActive" class="page-customer-order-item__item-ean desktop-small:text-p2">
|
||||
{{ item?.product?.ean }}
|
||||
</div>
|
||||
@if (primaryOutletActive) {
|
||||
<div class="page-customer-order-item__item-ean desktop-small:text-p2">
|
||||
{{ item?.product?.ean }}
|
||||
</div>
|
||||
}
|
||||
|
||||
<div *ngIf="primaryOutletActive" class="page-customer-order-item__item-price desktop-small:text-p2 font-bold">
|
||||
{{ item.price | currency: 'EUR' : 'code' }}
|
||||
</div>
|
||||
@if (primaryOutletActive) {
|
||||
<div class="page-customer-order-item__item-price desktop-small:text-p2 font-bold">
|
||||
{{ item.price | currency: 'EUR' : 'code' }}
|
||||
</div>
|
||||
}
|
||||
|
||||
<div *ngIf="primaryOutletActive" class="page-customer-order-item__item-changed desktop-small:text-p2">
|
||||
<ng-container [ngSwitch]="showChangeDate$ | async">
|
||||
<div class="flex flex-row" *ngSwitchCase="true">
|
||||
<div class="min-w-[7.5rem]">Geändert</div>
|
||||
<div class="font-bold">{{ item?.processingStatusDate | date: 'dd.MM.yy | HH:mm' }} Uhr</div>
|
||||
</div>
|
||||
<div class="flex flex-row" *ngSwitchCase="false">
|
||||
<div class="min-w-[7.5rem]">Bestelldatum</div>
|
||||
<div class="font-bold">{{ item?.orderDate | date: 'dd.MM.yy | HH:mm' }} Uhr</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
@if (primaryOutletActive) {
|
||||
<div class="page-customer-order-item__item-changed desktop-small:text-p2">
|
||||
@switch (showChangeDate$ | async) {
|
||||
@case (true) {
|
||||
<div class="flex flex-row">
|
||||
<div class="min-w-[7.5rem]">Geändert</div>
|
||||
<div class="font-bold">{{ item?.processingStatusDate | date: 'dd.MM.yy | HH:mm' }} Uhr</div>
|
||||
</div>
|
||||
}
|
||||
@case (false) {
|
||||
<div class="flex flex-row">
|
||||
<div class="min-w-[7.5rem]">Bestelldatum</div>
|
||||
<div class="font-bold">{{ item?.orderDate | date: 'dd.MM.yy | HH:mm' }} Uhr</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
<div *ngIf="primaryOutletActive" class="page-customer-order-item__item-quantity flex flex-row desktop-small:text-p2">
|
||||
<div class="min-w-[7.5rem]">Menge</div>
|
||||
<div class="font-bold">{{ item.quantity }} x</div>
|
||||
</div>
|
||||
@if (primaryOutletActive) {
|
||||
<div class="page-customer-order-item__item-quantity flex flex-row desktop-small:text-p2">
|
||||
<div class="min-w-[7.5rem]">Menge</div>
|
||||
<div class="font-bold">{{ item.quantity }} x</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div *ngIf="primaryOutletActive" class="page-customer-order-item__item-target-branch flex flex-row desktop-small:text-p2">
|
||||
<ng-container *ngIf="item.orderType === 1; else showDelivery">
|
||||
<div class="min-w-[7.5rem]">Zielfiliale</div>
|
||||
<div class="font-bold">{{ item.targetBranch }}</div>
|
||||
</ng-container>
|
||||
<ng-template #showDelivery>
|
||||
<div class="min-w-[7.5rem]">Versanddatum</div>
|
||||
<div class="font-bold">{{ item?.estimatedShippingDate | date: 'dd.MM.yy | HH:mm' }} Uhr</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
@if (primaryOutletActive) {
|
||||
<div class="page-customer-order-item__item-target-branch flex flex-row desktop-small:text-p2">
|
||||
@if (item.orderType === 1) {
|
||||
<div class="min-w-[7.5rem]">Zielfiliale</div>
|
||||
<div class="font-bold">{{ item.targetBranch }}</div>
|
||||
} @else {
|
||||
<div class="min-w-[7.5rem]">Versanddatum</div>
|
||||
<div class="font-bold">{{ item?.estimatedShippingDate | date: 'dd.MM.yy | HH:mm' }} Uhr</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
<hr
|
||||
*ngIf="!primaryOutletActive"
|
||||
class="page-customer-order-item__separator border-[#EDEFF0] border-solid border-[1px] -mx-[0.875rem]"
|
||||
/>
|
||||
@if (!primaryOutletActive) {
|
||||
<hr
|
||||
class="page-customer-order-item__separator border-[#EDEFF0] border-solid border-[1px] -mx-[0.875rem]"
|
||||
/>
|
||||
}
|
||||
|
||||
<div
|
||||
class="page-customer-order-item__item-order-number desktop-small:text-xl justify-self-end font-bold"
|
||||
[class.page-customer-order-item__item-order-number-main]="!primaryOutletActive"
|
||||
>
|
||||
<ng-container *ngIf="item?.compartmentCode; else orderNumber">
|
||||
>
|
||||
@if (item?.compartmentCode) {
|
||||
{{ item?.compartmentCode }}{{ item?.compartmentInfo && '_' + item?.compartmentInfo }}
|
||||
</ng-container>
|
||||
<ng-template #orderNumber>{{ item?.orderNumber }}</ng-template>
|
||||
} @else {
|
||||
{{ item?.orderNumber }}
|
||||
}
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="page-customer-order-item__item-processing-paid-status flex flex-col font-bold desktop-small:text-p2 justify-self-end self-center"
|
||||
>
|
||||
>
|
||||
<div class="page-customer-order-item__item-processing-status flex flex-row mb-[0.375rem]">
|
||||
<shared-icon
|
||||
class="flex items-center justify-center mr-1"
|
||||
[size]="16"
|
||||
*ngIf="item.processingStatus | processingStatus: 'icon'; let icon"
|
||||
[icon]="icon"
|
||||
></shared-icon>
|
||||
@if (item.processingStatus | processingStatus: 'icon'; as icon) {
|
||||
<shared-icon
|
||||
class="flex items-center justify-center mr-1"
|
||||
[size]="16"
|
||||
[icon]="icon"
|
||||
></shared-icon>
|
||||
}
|
||||
{{ item.processingStatus | processingStatus }}
|
||||
</div>
|
||||
|
||||
<div class="page-customer-order-item__item-paid flex flex-row self-end">
|
||||
<div class="font-bold w-fit desktop-small:text-p2 px-3 py-[0.125rem] rounded text-white bg-[#26830C]" *ngIf="item.features?.paid">
|
||||
{{ item.features?.paid }}
|
||||
</div>
|
||||
@if (item.features?.paid) {
|
||||
<div class="font-bold w-fit desktop-small:text-p2 px-3 py-[0.125rem] rounded text-white bg-[#26830C]">
|
||||
{{ item.features?.paid }}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,102 +2,111 @@
|
||||
class="page-customer-order-search-results__header bg-background-liste flex items-end justify-between"
|
||||
[class.pb-4]="!(primaryOutletActive$ | async)"
|
||||
[class.flex-col]="!(primaryOutletActive$ | async)"
|
||||
>
|
||||
>
|
||||
<div class="flex flex-row w-full desktop-small:w-min" [class.desktop-large:w-full]="!(primaryOutletActive$ | async)">
|
||||
<shared-filter-input-group-main
|
||||
*ngIf="filter$ | async; let filter"
|
||||
class="block mr-3 w-full desktop-small:w-[23.5rem]"
|
||||
[class.desktop-large:w-full]="!(primaryOutletActive$ | async)"
|
||||
[hint]="message$ | async"
|
||||
[loading]="loading$ | async"
|
||||
[inputGroup]="filter?.input | group: 'main'"
|
||||
(search)="search({ filter, clear: true })"
|
||||
[showDescription]="false"
|
||||
[scanner]="true"
|
||||
></shared-filter-input-group-main>
|
||||
@if (filter$ | async; as filter) {
|
||||
<shared-filter-input-group-main
|
||||
class="block mr-3 w-full desktop-small:w-[23.5rem]"
|
||||
[class.desktop-large:w-full]="!(primaryOutletActive$ | async)"
|
||||
[hint]="message$ | async"
|
||||
[loading]="loading$ | async"
|
||||
[inputGroup]="filter?.input | group: 'main'"
|
||||
(search)="search({ filter, clear: true })"
|
||||
[showDescription]="false"
|
||||
[scanner]="true"
|
||||
></shared-filter-input-group-main>
|
||||
}
|
||||
|
||||
<a
|
||||
class="page-customer-orders-results__filter w-[6.75rem] h-14 rounded font-bold px-5 mb-4 text-lg bg-[#AEB7C1] flex flex-row flex-nowrap items-center justify-center"
|
||||
[class.active]="hasFilter$ | async"
|
||||
[routerLink]="filterRoute"
|
||||
queryParamsHandling="preserve"
|
||||
>
|
||||
>
|
||||
<shared-icon class="mr-2" icon="filter-variant"></shared-icon>
|
||||
Filter
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div
|
||||
*ngIf="hits$ | async; let hits"
|
||||
class="page-customer-order-search-results__items-count inline-flex flex-row items-center pr-5 text-p3"
|
||||
[class.mb-4]="primaryOutletActive$ | async"
|
||||
>
|
||||
{{ hits ?? 0 }}
|
||||
Titel
|
||||
</div>
|
||||
@if (hits$ | async; as hits) {
|
||||
<div
|
||||
class="page-customer-order-search-results__items-count inline-flex flex-row items-center pr-5 text-p3"
|
||||
[class.mb-4]="primaryOutletActive$ | async"
|
||||
>
|
||||
{{ hits ?? 0 }}
|
||||
Titel
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<ui-scroll-container
|
||||
*ngIf="!(listEmpty$ | async); else emptyMessage"
|
||||
class="page-customer-order-results__scroll-container m-0 p-0"
|
||||
[showScrollbar]="false"
|
||||
[showScrollArrow]="false"
|
||||
(reachEnd)="loadMore()"
|
||||
[deltaEnd]="150"
|
||||
[itemLength]="itemLength$ | async"
|
||||
[containerHeight]="25"
|
||||
[showSpacer]="(primaryOutletActive$ | async) || (isTablet$ | async)"
|
||||
>
|
||||
<ng-container *ngIf="processId$ | async; let processId">
|
||||
<div class="page-customer-order-results__items-list w-full" *ngFor="let bueryNumberGroup of items$ | async | groupBy: byBuyerNumberFn">
|
||||
<ng-container *ngIf="bueryNumberGroup.items[0]; let firstItem">
|
||||
<div
|
||||
class="page-customer-order-search__item-header-group w-full grid grid-flow-col gap-x-4 items-center justify-between bg-white text-xl rounded-t px-4 py-[0.875rem] font-bold mb-px-2"
|
||||
>
|
||||
<h3 class="m-0 break-words" [class.w-72]="!(primaryOutletActive$ | async)">
|
||||
{{ firstItem?.organisation }}
|
||||
<ng-container *ngIf="!!firstItem?.organisation && (!!firstItem?.firstName || !!firstItem?.lastName)">-</ng-container>
|
||||
{{ firstItem?.lastName }}
|
||||
{{ firstItem?.firstName }}
|
||||
</h3>
|
||||
<h3 class="m-0 break-words text-right" [class.w-40]="!(primaryOutletActive$ | async)">{{ firstItem?.buyerNumber }}</h3>
|
||||
@if (!(listEmpty$ | async)) {
|
||||
<ui-scroll-container
|
||||
class="page-customer-order-results__scroll-container m-0 p-0"
|
||||
[showScrollbar]="false"
|
||||
[showScrollArrow]="false"
|
||||
(reachEnd)="loadMore()"
|
||||
[deltaEnd]="150"
|
||||
[itemLength]="itemLength$ | async"
|
||||
[containerHeight]="25"
|
||||
[showSpacer]="(primaryOutletActive$ | async) || (isTablet$ | async)"
|
||||
>
|
||||
@if (processId$ | async; as processId) {
|
||||
@for (bueryNumberGroup of items$ | async | groupBy: byBuyerNumberFn; track bueryNumberGroup) {
|
||||
<div class="page-customer-order-results__items-list w-full">
|
||||
@if (bueryNumberGroup.items[0]; as firstItem) {
|
||||
<div
|
||||
class="page-customer-order-search__item-header-group w-full grid grid-flow-col gap-x-4 items-center justify-between bg-white text-xl rounded-t px-4 py-[0.875rem] font-bold mb-px-2"
|
||||
>
|
||||
<h3 class="m-0 break-words" [class.w-72]="!(primaryOutletActive$ | async)">
|
||||
{{ firstItem?.organisation }}
|
||||
@if (!!firstItem?.organisation && (!!firstItem?.firstName || !!firstItem?.lastName)) {
|
||||
-
|
||||
}
|
||||
{{ firstItem?.lastName }}
|
||||
{{ firstItem?.firstName }}
|
||||
</h3>
|
||||
<h3 class="m-0 break-words text-right" [class.w-40]="!(primaryOutletActive$ | async)">{{ firstItem?.buyerNumber }}</h3>
|
||||
</div>
|
||||
}
|
||||
@for (orderNumberGroup of bueryNumberGroup.items | groupBy: byOrderNumberFn; track orderNumberGroup) {
|
||||
@for (processingStatusGroup of orderNumberGroup.items | groupBy: byProcessingStatusFn; track processingStatusGroup) {
|
||||
@for (compartmentCodeGroup of processingStatusGroup.items | groupBy: byCompartmentCodeFn; track compartmentCodeGroup) {
|
||||
@for (item of compartmentCodeGroup.items; track trackByFn($index, item); let firstItem = $first) {
|
||||
<page-customer-order-item
|
||||
class="page-customer-orders-results__result-item mb-[0.625rem]"
|
||||
[class.page-customer-orders-results__result-item-main]="primaryOutletActive$ | async"
|
||||
[item]="item"
|
||||
[primaryOutletActive]="primaryOutletActive$ | async"
|
||||
></page-customer-order-item>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngFor="let orderNumberGroup of bueryNumberGroup.items | groupBy: byOrderNumberFn">
|
||||
<ng-container *ngFor="let processingStatusGroup of orderNumberGroup.items | groupBy: byProcessingStatusFn">
|
||||
<ng-container *ngFor="let compartmentCodeGroup of processingStatusGroup.items | groupBy: byCompartmentCodeFn">
|
||||
<page-customer-order-item
|
||||
*ngFor="let item of compartmentCodeGroup.items; let firstItem = first; trackBy: trackByFn"
|
||||
class="page-customer-orders-results__result-item mb-[0.625rem]"
|
||||
[class.page-customer-orders-results__result-item-main]="primaryOutletActive$ | async"
|
||||
[item]="item"
|
||||
[primaryOutletActive]="primaryOutletActive$ | async"
|
||||
></page-customer-order-item>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ui-scroll-container>
|
||||
|
||||
<ng-template #emptyMessage>
|
||||
}
|
||||
}
|
||||
</ui-scroll-container>
|
||||
} @else {
|
||||
<div class="empty-message">
|
||||
Es sind im Moment keine Bestellposten vorhanden,
|
||||
<br />
|
||||
die bearbeitet werden können.
|
||||
</div>
|
||||
</ng-template>
|
||||
}
|
||||
|
||||
<div class="actions z-fixed" *ngIf="actions$ | async; let actions">
|
||||
<button
|
||||
[disabled]="(loadingFetchedActionButton$ | async) || (loading$ | async)"
|
||||
class="cta-action"
|
||||
*ngFor="let action of actions"
|
||||
[class.cta-action-primary]="action.selected"
|
||||
[class.cta-action-secondary]="!action.selected"
|
||||
(click)="handleAction(action)"
|
||||
>
|
||||
<ui-spinner [show]="(loadingFetchedActionButton$ | async) || (loading$ | async)">{{ action.label }}</ui-spinner>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@if (actions$ | async; as actions) {
|
||||
<div class="actions z-fixed">
|
||||
@for (action of actions; track action) {
|
||||
<button
|
||||
[disabled]="(loadingFetchedActionButton$ | async) || (loading$ | async)"
|
||||
class="cta-action"
|
||||
[class.cta-action-primary]="action.selected"
|
||||
[class.cta-action-secondary]="!action.selected"
|
||||
(click)="handleAction(action)"
|
||||
>
|
||||
<ui-spinner [show]="(loadingFetchedActionButton$ | async) || (loading$ | async)">{{ action.label }}</ui-spinner>
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
[cdkMenuTriggerFor]="navMenu"
|
||||
#menuTrigger="cdkMenuTriggerFor"
|
||||
[class.open]="menuTrigger.isOpen()"
|
||||
>
|
||||
>
|
||||
<shared-icon icon="apps" [size]="24"></shared-icon>
|
||||
<shared-icon [icon]="menuTrigger.isOpen() ? 'arrow-drop-up' : 'arrow-drop-down'" [size]="24"></shared-icon>
|
||||
</button>
|
||||
@@ -12,42 +12,46 @@
|
||||
<ng-template #navMenu>
|
||||
<div class="pt-1">
|
||||
<shared-menu>
|
||||
<a
|
||||
sharedMenuItem
|
||||
*ngIf="customerDetailsRoute$ | async; let customerDetailsRoute"
|
||||
[routerLink]="customerDetailsRoute.path"
|
||||
[queryParams]="customerDetailsRoute.queryParams"
|
||||
[queryParamsHandling]="'merge'"
|
||||
>
|
||||
Kundendetails
|
||||
</a>
|
||||
<a
|
||||
sharedMenuItem
|
||||
*ngIf="ordersRoute$ | async; let ordersRoute"
|
||||
[routerLink]="ordersRoute.path"
|
||||
[queryParams]="ordersRoute.queryParams"
|
||||
[queryParamsHandling]="'merge'"
|
||||
>
|
||||
Bestellungen
|
||||
</a>
|
||||
<a
|
||||
sharedMenuItem
|
||||
*ngIf="kundenkarteRoute$ | async; let kundenkarteRoute"
|
||||
[routerLink]="kundenkarteRoute.path"
|
||||
[queryParams]="kundenkarteRoute.queryParams"
|
||||
[queryParamsHandling]="'merge'"
|
||||
>
|
||||
Kundenkarte
|
||||
</a>
|
||||
<a
|
||||
sharedMenuItem
|
||||
*ngIf="historyRoute$ | async; let historyRoute"
|
||||
[routerLink]="historyRoute.path"
|
||||
[queryParams]="historyRoute.queryParams"
|
||||
[queryParamsHandling]="'merge'"
|
||||
>
|
||||
Historie
|
||||
</a>
|
||||
@if (customerDetailsRoute$ | async; as customerDetailsRoute) {
|
||||
<a
|
||||
sharedMenuItem
|
||||
[routerLink]="customerDetailsRoute.path"
|
||||
[queryParams]="customerDetailsRoute.queryParams"
|
||||
[queryParamsHandling]="'merge'"
|
||||
>
|
||||
Kundendetails
|
||||
</a>
|
||||
}
|
||||
@if (ordersRoute$ | async; as ordersRoute) {
|
||||
<a
|
||||
sharedMenuItem
|
||||
[routerLink]="ordersRoute.path"
|
||||
[queryParams]="ordersRoute.queryParams"
|
||||
[queryParamsHandling]="'merge'"
|
||||
>
|
||||
Bestellungen
|
||||
</a>
|
||||
}
|
||||
@if (kundenkarteRoute$ | async; as kundenkarteRoute) {
|
||||
<a
|
||||
sharedMenuItem
|
||||
[routerLink]="kundenkarteRoute.path"
|
||||
[queryParams]="kundenkarteRoute.queryParams"
|
||||
[queryParamsHandling]="'merge'"
|
||||
>
|
||||
Kundenkarte
|
||||
</a>
|
||||
}
|
||||
@if (historyRoute$ | async; as historyRoute) {
|
||||
<a
|
||||
sharedMenuItem
|
||||
[routerLink]="historyRoute.path"
|
||||
[queryParams]="historyRoute.queryParams"
|
||||
[queryParamsHandling]="'merge'"
|
||||
>
|
||||
Historie
|
||||
</a>
|
||||
}
|
||||
</shared-menu>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
@@ -8,7 +8,7 @@ import { map } from 'rxjs/operators';
|
||||
import { CustomerSearchNavigation } from '@shared/services/navigation';
|
||||
import { ComponentStore } from '@ngrx/component-store';
|
||||
import { RouterLink } from '@angular/router';
|
||||
import { AsyncPipe, NgIf } from '@angular/common';
|
||||
import { AsyncPipe } from '@angular/common';
|
||||
|
||||
export interface CustomerMenuComponentState {
|
||||
customerId?: number;
|
||||
@@ -26,7 +26,7 @@ export interface CustomerMenuComponentState {
|
||||
styleUrls: ['customer-menu.component.css'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
host: { class: 'page-customer-menu' },
|
||||
imports: [CdkMenuModule, SharedMenuModule, IconComponent, RouterLink, NgIf, AsyncPipe],
|
||||
imports: [CdkMenuModule, SharedMenuModule, IconComponent, RouterLink, AsyncPipe],
|
||||
})
|
||||
export class CustomerMenuComponent extends ComponentStore<CustomerMenuComponentState> {
|
||||
private _navigation = inject(CustomerSearchNavigation);
|
||||
|
||||
@@ -1,59 +1,66 @@
|
||||
<cdk-virtual-scroll-viewport
|
||||
itemSize="100"
|
||||
class="h-[calc(100vh-20.125rem)] desktop-small:h-[calc(100vh-18.625rem)]"
|
||||
*ngIf="!compact"
|
||||
(scrolledIndexChange)="scrolledIndexChange($event)"
|
||||
>
|
||||
<a
|
||||
*cdkVirtualFor="let customer of customers; trackBy: trackByFn; let index = index"
|
||||
[routerLink]="customerSearchNavigation.detailsRoute({ processId: processId, customerId: customer.id })?.path"
|
||||
[queryParams]="customerSearchNavigation.detailsRoute({ processId: processId, customerId: customer.id })?.queryParams"
|
||||
[queryParamsHandling]="'merge'"
|
||||
(click)="scrolledIndexChange(index)"
|
||||
routerLinkActive
|
||||
#rla="routerLinkActive"
|
||||
>
|
||||
<page-customer-result-list-item-full [class.active]="rla.isActive" [customer]="customer"></page-customer-result-list-item-full>
|
||||
</a>
|
||||
<div class="h-[6.125rem] bg-white rounded px-4 py-3" *ngIf="hits === customers?.length && !fetching">
|
||||
<ng-container *ngTemplateOutlet="customerNotFound"></ng-container>
|
||||
</div>
|
||||
</cdk-virtual-scroll-viewport>
|
||||
@if (!compact) {
|
||||
<cdk-virtual-scroll-viewport
|
||||
itemSize="100"
|
||||
class="h-[calc(100vh-20.125rem)] desktop-small:h-[calc(100vh-18.625rem)]"
|
||||
(scrolledIndexChange)="scrolledIndexChange($event)"
|
||||
>
|
||||
<a
|
||||
*cdkVirtualFor="let customer of customers; trackBy: trackByFn; let index = index"
|
||||
[routerLink]="customerSearchNavigation.detailsRoute({ processId: processId, customerId: customer.id })?.path"
|
||||
[queryParams]="customerSearchNavigation.detailsRoute({ processId: processId, customerId: customer.id })?.queryParams"
|
||||
[queryParamsHandling]="'merge'"
|
||||
(click)="scrolledIndexChange(index)"
|
||||
routerLinkActive
|
||||
#rla="routerLinkActive"
|
||||
>
|
||||
<page-customer-result-list-item-full [class.active]="rla.isActive" [customer]="customer"></page-customer-result-list-item-full>
|
||||
</a>
|
||||
@if (hits === customers?.length && !fetching) {
|
||||
<div class="h-[6.125rem] bg-white rounded px-4 py-3">
|
||||
<ng-container *ngTemplateOutlet="customerNotFound"></ng-container>
|
||||
</div>
|
||||
}
|
||||
</cdk-virtual-scroll-viewport>
|
||||
}
|
||||
|
||||
<cdk-virtual-scroll-viewport
|
||||
itemSize="191"
|
||||
class="h-[calc(100vh-20.75rem)]"
|
||||
*ngIf="compact"
|
||||
(scrolledIndexChange)="scrolledIndexChange($event)"
|
||||
>
|
||||
<a
|
||||
*cdkVirtualFor="let customer of customers; trackBy: trackByFn; let index = index"
|
||||
[routerLink]="customerSearchNavigation.detailsRoute({ processId: processId, customerId: customer.id })?.path"
|
||||
[queryParams]="customerSearchNavigation.detailsRoute({ processId: processId, customerId: customer.id })?.queryParams"
|
||||
[queryParamsHandling]="'merge'"
|
||||
(click)="scrolledIndexChange(index)"
|
||||
routerLinkActive
|
||||
#rla="routerLinkActive"
|
||||
>
|
||||
<page-customer-result-list-item [class.active]="rla.isActive" [customer]="customer"></page-customer-result-list-item>
|
||||
</a>
|
||||
<div class="h-[11.3125rem] bg-white rounded px-4 py-3" *ngIf="hits === customers?.length && !fetching">
|
||||
<ng-container *ngTemplateOutlet="customerNotFound"></ng-container>
|
||||
</div>
|
||||
</cdk-virtual-scroll-viewport>
|
||||
@if (compact) {
|
||||
<cdk-virtual-scroll-viewport
|
||||
itemSize="191"
|
||||
class="h-[calc(100vh-20.75rem)]"
|
||||
(scrolledIndexChange)="scrolledIndexChange($event)"
|
||||
>
|
||||
<a
|
||||
*cdkVirtualFor="let customer of customers; trackBy: trackByFn; let index = index"
|
||||
[routerLink]="customerSearchNavigation.detailsRoute({ processId: processId, customerId: customer.id })?.path"
|
||||
[queryParams]="customerSearchNavigation.detailsRoute({ processId: processId, customerId: customer.id })?.queryParams"
|
||||
[queryParamsHandling]="'merge'"
|
||||
(click)="scrolledIndexChange(index)"
|
||||
routerLinkActive
|
||||
#rla="routerLinkActive"
|
||||
>
|
||||
<page-customer-result-list-item [class.active]="rla.isActive" [customer]="customer"></page-customer-result-list-item>
|
||||
</a>
|
||||
@if (hits === customers?.length && !fetching) {
|
||||
<div class="h-[11.3125rem] bg-white rounded px-4 py-3">
|
||||
<ng-container *ngTemplateOutlet="customerNotFound"></ng-container>
|
||||
</div>
|
||||
}
|
||||
</cdk-virtual-scroll-viewport>
|
||||
}
|
||||
<ng-template #customerNotFound>
|
||||
<div class="text-sm">
|
||||
Hinweis: Aus Datenschutzgründen werden nur Teilinformationen dargestellt. Tab auf einen Kunden um mehr zu erfahren.
|
||||
</div>
|
||||
<div class="font-bold text-lg mt-3">
|
||||
<span>Kunden nicht gefunden?</span>
|
||||
<a
|
||||
*ngIf="customerCreateNavigation.defaultRoute({ processId: processId }); let route"
|
||||
[routerLink]="route.path"
|
||||
[queryParams]="route.queryParams"
|
||||
class="text-brand"
|
||||
>
|
||||
Neue Kundendaten erfassen
|
||||
</a>
|
||||
@if (customerCreateNavigation.defaultRoute({ processId: processId }); as route) {
|
||||
<a
|
||||
[routerLink]="route.path"
|
||||
[queryParams]="route.queryParams"
|
||||
class="text-brand"
|
||||
>
|
||||
Neue Kundendaten erfassen
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
@@ -1,21 +1,25 @@
|
||||
<ng-container *ifRole="'Store'">
|
||||
<shared-checkbox
|
||||
*ngIf="customerType !== 'b2b'"
|
||||
[ngModel]="p4mUser"
|
||||
(ngModelChange)="setValue({ p4mUser: !p4mUser })"
|
||||
[disabled]="p4mReadonly || readonly"
|
||||
>
|
||||
Kundenkarte
|
||||
</shared-checkbox>
|
||||
</ng-container>
|
||||
<ng-container *ngFor="let option of filteredOptions$ | async">
|
||||
<shared-checkbox
|
||||
*ngIf="option?.enabled !== false"
|
||||
[ngModel]="option.value === customerType"
|
||||
(ngModelChange)="setValue({ customerType: $event ? option.value : undefined })"
|
||||
[disabled]="isOptionDisabled(option)"
|
||||
[name]="option.value"
|
||||
>
|
||||
{{ option.label }}
|
||||
</shared-checkbox>
|
||||
</ng-container>
|
||||
<!-- <ng-container *ifRole="'Store'">
|
||||
@if (customerType !== 'b2b') {
|
||||
<shared-checkbox
|
||||
[ngModel]="p4mUser"
|
||||
(ngModelChange)="setValue({ p4mUser: !p4mUser })"
|
||||
[disabled]="p4mReadonly || readonly"
|
||||
>
|
||||
Kundenkarte
|
||||
</shared-checkbox>
|
||||
}
|
||||
</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 })
|
||||
"
|
||||
[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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,57 +6,59 @@
|
||||
type="text"
|
||||
formControlName="street"
|
||||
[tabindex]="tabIndexStart"
|
||||
|
||||
[readonly]="readonly"
|
||||
/>
|
||||
</shared-form-control>
|
||||
<shared-form-control label="Hausnummer">
|
||||
<input
|
||||
placeholder="Hausnummer"
|
||||
class="input-control"
|
||||
type="text"
|
||||
formControlName="streetNumber"
|
||||
[tabindex]="tabIndexStart + 1"
|
||||
[readonly]="readonly"
|
||||
/>
|
||||
</shared-form-control>
|
||||
<shared-form-control label="PLZ">
|
||||
<input
|
||||
placeholder="PLZ"
|
||||
class="input-control"
|
||||
type="text"
|
||||
formControlName="zipCode"
|
||||
[tabindex]="tabIndexStart + 2"
|
||||
[readonly]="readonly"
|
||||
/>
|
||||
</shared-form-control>
|
||||
<shared-form-control label="Ort">
|
||||
<input
|
||||
placeholder="Ort"
|
||||
class="input-control"
|
||||
type="text"
|
||||
formControlName="city"
|
||||
[tabindex]="tabIndexStart + 3"
|
||||
[readonly]="readonly"
|
||||
/>
|
||||
</shared-form-control>
|
||||
|
||||
<shared-form-control class="col-span-2" label="Adresszusatz">
|
||||
<input
|
||||
placeholder="Adresszusatz"
|
||||
class="input-control"
|
||||
type="text"
|
||||
formControlName="info"
|
||||
[tabindex]="tabIndexStart + 4"
|
||||
[readonly]="readonly"
|
||||
/>
|
||||
</shared-form-control>
|
||||
/>
|
||||
</shared-form-control>
|
||||
<shared-form-control label="Hausnummer">
|
||||
<input
|
||||
placeholder="Hausnummer"
|
||||
class="input-control"
|
||||
type="text"
|
||||
formControlName="streetNumber"
|
||||
[tabindex]="tabIndexStart + 1"
|
||||
[readonly]="readonly"
|
||||
/>
|
||||
</shared-form-control>
|
||||
<shared-form-control label="PLZ">
|
||||
<input
|
||||
placeholder="PLZ"
|
||||
class="input-control"
|
||||
type="text"
|
||||
formControlName="zipCode"
|
||||
[tabindex]="tabIndexStart + 2"
|
||||
[readonly]="readonly"
|
||||
/>
|
||||
</shared-form-control>
|
||||
<shared-form-control label="Ort">
|
||||
<input
|
||||
placeholder="Ort"
|
||||
class="input-control"
|
||||
type="text"
|
||||
formControlName="city"
|
||||
[tabindex]="tabIndexStart + 3"
|
||||
[readonly]="readonly"
|
||||
/>
|
||||
</shared-form-control>
|
||||
|
||||
<shared-form-control class="col-span-2" label="Land">
|
||||
<shared-select placeholder="Land" formControlName="country" [tabindex]="tabIndexStart + 5" [readonly]="readonly">
|
||||
<shared-select-option *ngFor="let country of countries || (countries$ | async)" [value]="country.isO3166_A_3">
|
||||
{{ country.name }}
|
||||
</shared-select-option>
|
||||
</shared-select>
|
||||
</shared-form-control>
|
||||
</ng-container>
|
||||
<shared-form-control class="col-span-2" label="Adresszusatz">
|
||||
<input
|
||||
placeholder="Adresszusatz"
|
||||
class="input-control"
|
||||
type="text"
|
||||
formControlName="info"
|
||||
[tabindex]="tabIndexStart + 4"
|
||||
[readonly]="readonly"
|
||||
/>
|
||||
</shared-form-control>
|
||||
|
||||
<shared-form-control class="col-span-2" label="Land">
|
||||
<shared-select placeholder="Land" formControlName="country" [tabindex]="tabIndexStart + 5" [readonly]="readonly">
|
||||
@for (country of countries || (countries$ | async); track country) {
|
||||
<shared-select-option [value]="country.isO3166_A_3">
|
||||
{{ country.name }}
|
||||
</shared-select-option>
|
||||
}
|
||||
</shared-select>
|
||||
</shared-form-control>
|
||||
</ng-container>
|
||||
|
||||
@@ -4,59 +4,64 @@
|
||||
[tabindex]="tabIndexStart"
|
||||
[autofocus]="focusAfterInit"
|
||||
[readonly]="readonly"
|
||||
>
|
||||
>
|
||||
<ng-content></ng-content>
|
||||
</shared-checkbox>
|
||||
<div class="address-block" *ngIf="control.value.deviatingAddress">
|
||||
<div class="wrapper">
|
||||
<app-organisation-form-block
|
||||
*ngIf="organisation"
|
||||
[tabIndexStart]="tabIndexStart + 1"
|
||||
#orgaBlock
|
||||
(onInit)="addOrganisationGroup($event)"
|
||||
(onDestroy)="removeOrganisationGroup()"
|
||||
[data]="data?.organisation"
|
||||
#nameFormBlock
|
||||
[tabIndexStart]="tabIndexStart + 1"
|
||||
[requiredMarks]="organisationRequiredMarks"
|
||||
[validatorFns]="organisationValidatorFns"
|
||||
[readonly]="readonly"
|
||||
></app-organisation-form-block>
|
||||
<app-name-form-block
|
||||
(onInit)="addNameGroup($event)"
|
||||
(onDestroy)="removeNameGroup()"
|
||||
[data]="data?.name"
|
||||
#nameFormBlock
|
||||
[requiredMarks]="nameRequiredMarks"
|
||||
[validatorFns]="nameValidatorFns"
|
||||
[readonly]="readonly"
|
||||
></app-name-form-block>
|
||||
<app-address-form-block
|
||||
#addressFormBlock
|
||||
(onInit)="addAddressGroup($event)"
|
||||
(onDestroy)="removeAddressGroup()"
|
||||
[data]="data?.address"
|
||||
[requiredMarks]="addressRequiredMarks"
|
||||
[validatorFns]="addressValidatorFns"
|
||||
[readonly]="readonly"
|
||||
></app-address-form-block>
|
||||
<app-email-form-block
|
||||
*ngIf="email"
|
||||
#emailFormBlock
|
||||
(onInit)="addEmailGroup($event)"
|
||||
(onDestroy)="removeEmailGroup()"
|
||||
[data]="data?.email"
|
||||
[requiredMark]="emailRequiredMark"
|
||||
[validatorFns]="emailValidationFns"
|
||||
[readonly]="readonly"
|
||||
></app-email-form-block>
|
||||
<app-phone-numbers-form-block
|
||||
*ngIf="phoneNumbers"
|
||||
(onInit)="addPhoneNumbersGroup($event)"
|
||||
(onDestroy)="removePhoneNumbersGroup()"
|
||||
[readonly]="readonly"
|
||||
>
|
||||
[tabIndexStart]="emailFormBlock?.tabIndexEnd+1" [requiredMarks]="phoneNumbersRequiredMarks" [validatorFns]="phoneNumbersValidatorFns">
|
||||
</app-phone-numbers-form-block>
|
||||
@if (control.value.deviatingAddress) {
|
||||
<div class="address-block">
|
||||
<div class="wrapper">
|
||||
@if (organisation) {
|
||||
<app-organisation-form-block
|
||||
[tabIndexStart]="tabIndexStart + 1"
|
||||
#orgaBlock
|
||||
(onInit)="addOrganisationGroup($event)"
|
||||
(onDestroy)="removeOrganisationGroup()"
|
||||
[data]="data?.organisation"
|
||||
#nameFormBlock
|
||||
[tabIndexStart]="tabIndexStart + 1"
|
||||
[requiredMarks]="organisationRequiredMarks"
|
||||
[validatorFns]="organisationValidatorFns"
|
||||
[readonly]="readonly"
|
||||
></app-organisation-form-block>
|
||||
}
|
||||
<app-name-form-block
|
||||
(onInit)="addNameGroup($event)"
|
||||
(onDestroy)="removeNameGroup()"
|
||||
[data]="data?.name"
|
||||
#nameFormBlock
|
||||
[requiredMarks]="nameRequiredMarks"
|
||||
[validatorFns]="nameValidatorFns"
|
||||
[readonly]="readonly"
|
||||
></app-name-form-block>
|
||||
<app-address-form-block
|
||||
#addressFormBlock
|
||||
(onInit)="addAddressGroup($event)"
|
||||
(onDestroy)="removeAddressGroup()"
|
||||
[data]="data?.address"
|
||||
[requiredMarks]="addressRequiredMarks"
|
||||
[validatorFns]="addressValidatorFns"
|
||||
[readonly]="readonly"
|
||||
></app-address-form-block>
|
||||
@if (email) {
|
||||
<app-email-form-block
|
||||
#emailFormBlock
|
||||
(onInit)="addEmailGroup($event)"
|
||||
(onDestroy)="removeEmailGroup()"
|
||||
[data]="data?.email"
|
||||
[requiredMark]="emailRequiredMark"
|
||||
[validatorFns]="emailValidationFns"
|
||||
[readonly]="readonly"
|
||||
></app-email-form-block>
|
||||
}
|
||||
@if (phoneNumbers) {
|
||||
<app-phone-numbers-form-block
|
||||
(onInit)="addPhoneNumbersGroup($event)"
|
||||
(onDestroy)="removePhoneNumbersGroup()"
|
||||
[readonly]="readonly"
|
||||
>
|
||||
[tabIndexStart]="emailFormBlock?.tabIndexEnd+1" [requiredMarks]="phoneNumbersRequiredMarks" [validatorFns]="phoneNumbersValidatorFns">
|
||||
</app-phone-numbers-form-block>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -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,12 +1,13 @@
|
||||
<div class="interests-description">Geben Sie Interessen an, um Ihre persönlichen Kontoangaben zu verfeinern.</div>
|
||||
<div class="interests-wrapper" [formGroup]="control">
|
||||
<shared-checkbox
|
||||
*ngFor="let pair of interests | keyvalue; let idx = index"
|
||||
[formControlName]="pair.key"
|
||||
[tabindex]="tabIndexStart + idx"
|
||||
[autofocus]="focusAfterInit"
|
||||
[readonly]="readonly"
|
||||
>
|
||||
{{ pair.value }}
|
||||
</shared-checkbox>
|
||||
@for (pair of interests | keyvalue; track pair; let idx = $index) {
|
||||
<shared-checkbox
|
||||
[formControlName]="pair.key"
|
||||
[tabindex]="tabIndexStart + idx"
|
||||
[autofocus]="focusAfterInit"
|
||||
[readonly]="readonly"
|
||||
>
|
||||
{{ pair.value }}
|
||||
</shared-checkbox>
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -6,8 +6,10 @@
|
||||
[readonly]="readonly"
|
||||
[tabindex]="tabIndexStart"
|
||||
[autofocus]="focusAfterInit"
|
||||
>
|
||||
<shared-select-option *ngFor="let gender of genderSettings.genders" [value]="gender.value">{{ gender.label }}</shared-select-option>
|
||||
>
|
||||
@for (gender of genderSettings.genders; track gender) {
|
||||
<shared-select-option [value]="gender.value">{{ gender.label }}</shared-select-option>
|
||||
}
|
||||
</shared-select>
|
||||
</shared-form-control>
|
||||
|
||||
@@ -30,7 +32,7 @@
|
||||
formControlName="lastName"
|
||||
[tabindex]="tabIndexStart + 2"
|
||||
[readonly]="readonly"
|
||||
/>
|
||||
/>
|
||||
</shared-form-control>
|
||||
|
||||
<shared-form-control label="Vorname">
|
||||
@@ -41,6 +43,6 @@
|
||||
formControlName="firstName"
|
||||
[tabindex]="tabIndexStart + 3"
|
||||
[readonly]="readonly"
|
||||
/>
|
||||
/>
|
||||
</shared-form-control>
|
||||
</ng-container>
|
||||
|
||||
@@ -6,30 +6,30 @@
|
||||
type="text"
|
||||
formControlName="name"
|
||||
[tabindex]="tabIndexStart"
|
||||
|
||||
|
||||
[readonly]="readonly"
|
||||
/>
|
||||
</shared-form-control>
|
||||
<ng-container *ngIf="appearence === 'default'">
|
||||
<shared-form-control label="Abteilung">
|
||||
<input
|
||||
placeholder="Abteilung"
|
||||
class="input-control"
|
||||
type="text"
|
||||
formControlName="department"
|
||||
[tabindex]="tabIndexStart + 1"
|
||||
[readonly]="readonly"
|
||||
/>
|
||||
</shared-form-control>
|
||||
<shared-form-control label="USt-ID">
|
||||
<input
|
||||
placeholder="USt-ID"
|
||||
class="input-control"
|
||||
type="text"
|
||||
formControlName="vatId"
|
||||
[tabindex]="tabIndexStart + 2"
|
||||
[readonly]="readonly"
|
||||
/>
|
||||
</shared-form-control>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
@if (appearence === 'default') {
|
||||
<shared-form-control label="Abteilung">
|
||||
<input
|
||||
placeholder="Abteilung"
|
||||
class="input-control"
|
||||
type="text"
|
||||
formControlName="department"
|
||||
[tabindex]="tabIndexStart + 1"
|
||||
[readonly]="readonly"
|
||||
/>
|
||||
</shared-form-control>
|
||||
<shared-form-control label="USt-ID">
|
||||
<input
|
||||
placeholder="USt-ID"
|
||||
class="input-control"
|
||||
type="text"
|
||||
formControlName="vatId"
|
||||
[tabindex]="tabIndexStart + 2"
|
||||
[readonly]="readonly"
|
||||
/>
|
||||
</shared-form-control>
|
||||
}
|
||||
</ng-container>
|
||||
|
||||
@@ -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"
|
||||
@@ -6,9 +6,11 @@
|
||||
[formControl]="control"
|
||||
[tabindex]="tabIndexStart"
|
||||
[readonly]="readonly"
|
||||
|
||||
/>
|
||||
</shared-form-control>
|
||||
<button type="button" *ngIf="!readonly && canScan()" (click)="scan()">
|
||||
<shared-icon icon="barcode-scan" [size]="32"></shared-icon>
|
||||
</button>
|
||||
|
||||
/>
|
||||
</shared-form-control>
|
||||
@if (!readonly && canScan()) {
|
||||
<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,38 +1,62 @@
|
||||
<div class="wrapper text-center" [@cardFlip]="state" (@cardFlip.done)="flipAnimationDone($event)">
|
||||
<div *ngIf="cardDetails" class="card-main">
|
||||
<div class="icons text-brand">
|
||||
<button *ngIf="isCustomerCard && frontside" class="icon-barcode" (click)="flipCard()">
|
||||
<shared-icon [size]="35" icon="barcode-scanner"></shared-icon>
|
||||
</button>
|
||||
<button *ngIf="isCustomerCard && !frontside" class="icon-back" (click)="flipCard()">
|
||||
<shared-icon [size]="35" icon="refresh"></shared-icon>
|
||||
</button>
|
||||
<!-- <div *ngIf="!isCustomerCard" class="icon-delete"><ui-icon (click)="onDeletePartnerCard()" size="25px" icon="trash"></ui-icon></div> -->
|
||||
</div>
|
||||
<div class="headline">
|
||||
<p *ngIf="isCustomerCard && frontside">Ihre Lesepunkte</p>
|
||||
<p *ngIf="isCustomerCard && !frontside">Kartennummer</p>
|
||||
<p *ngIf="!isCustomerCard">Partnerkartennummer</p>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<div *ngIf="!isCustomerCard || (isCustomerCard && !frontside)" class="card-number">{{ cardDetails.code }}</div>
|
||||
<div *ngIf="isCustomerCard && frontside" class="points">{{ cardDetails.totalPoints | number }}</div>
|
||||
</div>
|
||||
<div class="barcode-button">
|
||||
<div *ngIf="!isCustomerCard || (isCustomerCard && !frontside)" class="barcode-field">
|
||||
<img class="barcode" src="/assets/images/barcode.png" alt="Barcode" />
|
||||
@if (cardDetails) {
|
||||
<div class="card-main">
|
||||
<div class="icons text-brand">
|
||||
@if (isCustomerCard && frontside) {
|
||||
<button class="icon-barcode" (click)="flipCard()">
|
||||
<shared-icon [size]="35" icon="barcode-scanner"></shared-icon>
|
||||
</button>
|
||||
}
|
||||
@if (isCustomerCard && !frontside) {
|
||||
<button class="icon-back" (click)="flipCard()">
|
||||
<shared-icon [size]="35" icon="refresh"></shared-icon>
|
||||
</button>
|
||||
}
|
||||
<!-- <div *ngIf="!isCustomerCard" class="icon-delete"><ui-icon (click)="onDeletePartnerCard()" size="25px" icon="trash"></ui-icon></div> -->
|
||||
</div>
|
||||
<div *ngIf="isCustomerCard && frontside">
|
||||
<button class="button" (click)="onRewardShop()">Zum Prämienshop</button>
|
||||
<div class="headline">
|
||||
@if (isCustomerCard && frontside) {
|
||||
<p>Ihre Lesepunkte</p>
|
||||
}
|
||||
@if (isCustomerCard && !frontside) {
|
||||
<p>Kartennummer</p>
|
||||
}
|
||||
@if (!isCustomerCard) {
|
||||
<p>Partnerkartennummer</p>
|
||||
}
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
@if (!isCustomerCard || (isCustomerCard && !frontside)) {
|
||||
<div class="card-number">{{ cardDetails.code }}</div>
|
||||
}
|
||||
@if (isCustomerCard && frontside) {
|
||||
<div class="points">{{ cardDetails.totalPoints | number }}</div>
|
||||
}
|
||||
</div>
|
||||
<div class="barcode-button">
|
||||
@if (!isCustomerCard || (isCustomerCard && !frontside)) {
|
||||
<div class="barcode-field">
|
||||
<img class="barcode" src="/assets/images/barcode.png" alt="Barcode" />
|
||||
</div>
|
||||
}
|
||||
@if (isCustomerCard && frontside) {
|
||||
<div>
|
||||
<button class="button" (click)="onRewardShop()">Zum Prämienshop</button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="card-bottom">
|
||||
<div *ngIf="!isCustomerCard || (isCustomerCard && !frontside)" class="customer-name">
|
||||
<p>{{ cardDetails.firstName }} {{ cardDetails.lastName }}</p>
|
||||
</div>
|
||||
<div *ngIf="isCustomerCard && frontside" class="logo ml-2">
|
||||
<img class="logo-picture" src="/assets/images/Hugendubel_Logo.png" alt="Hugendubel Logo" />
|
||||
</div>
|
||||
@if (!isCustomerCard || (isCustomerCard && !frontside)) {
|
||||
<div class="customer-name">
|
||||
<p>{{ cardDetails.firstName }} {{ cardDetails.lastName }}</p>
|
||||
</div>
|
||||
}
|
||||
@if (isCustomerCard && frontside) {
|
||||
<div class="logo ml-2">
|
||||
<img class="logo-picture" src="/assets/images/Hugendubel_Logo.png" alt="Hugendubel Logo" />
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { animate, state, style, transition, trigger } from '@angular/animations';
|
||||
import { DecimalPipe, NgIf } from '@angular/common';
|
||||
import { DecimalPipe } from '@angular/common';
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { IconComponent } from '@shared/components/icon';
|
||||
import { BonusCardInfoDTO } from '@generated/swagger/crm-api';
|
||||
@@ -8,7 +8,7 @@ import { BonusCardInfoDTO } from '@generated/swagger/crm-api';
|
||||
selector: 'page-customer-kundenkarte',
|
||||
templateUrl: 'kundenkarte.component.html',
|
||||
styleUrls: ['kundenkarte.component.scss'],
|
||||
imports: [IconComponent, NgIf, DecimalPipe],
|
||||
imports: [IconComponent, DecimalPipe],
|
||||
animations: [
|
||||
trigger('cardFlip', [
|
||||
state(
|
||||
|
||||
@@ -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,89 +1,83 @@
|
||||
<form *ngIf="formData$ | async; let data" (keydown.enter)="$event.preventDefault()">
|
||||
<h1 class="title">Kundendaten erfassen</h1>
|
||||
<p class="description">
|
||||
Um Ihnen den ausgewählten Service zu
|
||||
<br />
|
||||
ermöglichen, legen wir Ihnen gerne
|
||||
<br />
|
||||
ein Kundenkonto an.
|
||||
</p>
|
||||
|
||||
<app-customer-type-selector
|
||||
[processId]="processId$ | async"
|
||||
[p4mUser]="false"
|
||||
customerType="b2b"
|
||||
(valueChanges)="customerTypeChanged($event)"
|
||||
></app-customer-type-selector>
|
||||
|
||||
<app-organisation-form-block
|
||||
#orga
|
||||
[tabIndexStart]="1"
|
||||
[data]="data.organisation"
|
||||
(dataChanges)="patchFormData('organisation', $event)"
|
||||
(onInit)="addFormBlock('organisation', $event)"
|
||||
[requiredMarks]="organisationFormBlockRequiredMarks"
|
||||
[validatorFns]="organisationFormBlockValidators"
|
||||
></app-organisation-form-block>
|
||||
|
||||
<app-name-form-block
|
||||
#name
|
||||
[tabIndexStart]="orga.tabIndexEnd + 1"
|
||||
[data]="data.name"
|
||||
(dataChanges)="patchFormData('name', $event)"
|
||||
(onInit)="addFormBlock('name', $event)"
|
||||
></app-name-form-block>
|
||||
|
||||
<app-address-form-block
|
||||
#address
|
||||
[tabIndexStart]="name.tabIndexEnd + 1"
|
||||
[data]="data.address"
|
||||
(dataChanges)="patchFormData('address', $event)"
|
||||
(onInit)="addFormBlock('address', $event)"
|
||||
[requiredMarks]="addressRequiredMarks"
|
||||
[validatorFns]="addressValidators"
|
||||
[defaults]="{ country: 'DEU' }"
|
||||
></app-address-form-block>
|
||||
|
||||
<app-email-form-block
|
||||
#email
|
||||
[tabIndexStart]="address.tabIndexEnd + 1"
|
||||
[data]="data.email"
|
||||
(dataChanges)="patchFormData('email', $event)"
|
||||
[validatorFns]="emailFormBlockValidators"
|
||||
(onInit)="addFormBlock('email', $event)"
|
||||
></app-email-form-block>
|
||||
|
||||
<app-phone-numbers-form-block
|
||||
#phoneNumbers
|
||||
[tabIndexStart]="email.tabIndexEnd + 1"
|
||||
[data]="data.phoneNumbers"
|
||||
(dataChanges)="patchFormData('phoneNumbers', $event)"
|
||||
(onInit)="addFormBlock('phoneNumbers', $event)"
|
||||
></app-phone-numbers-form-block>
|
||||
|
||||
<app-deviating-address-form-block
|
||||
[tabIndexStart]="phoneNumbers.tabIndexEnd + 1"
|
||||
[data]="data.deviatingDeliveryAddress"
|
||||
(dataChanges)="patchFormData('deviatingDeliveryAddress', $event)"
|
||||
(onInit)="addFormBlock('deviatingDeliveryAddress', $event)"
|
||||
[nameRequiredMarks]="deviatingNameRequiredMarks"
|
||||
[nameValidatorFns]="deviatingNameValidationFns"
|
||||
[addressRequiredMarks]="addressRequiredMarks"
|
||||
[addressValidatorFns]="addressValidators"
|
||||
[organisationRequiredMarks]="organisationFormBlockRequiredMarks"
|
||||
[organisationValidatorFns]="organisationFormBlockValidators"
|
||||
[defaults]="{ address: { country: 'DEU' } }"
|
||||
[organisation]="true"
|
||||
[email]="true"
|
||||
[phoneNumbers]="true"
|
||||
>
|
||||
Die Lieferadresse weicht von der Rechnungsadresse ab
|
||||
</app-deviating-address-form-block>
|
||||
|
||||
<div class="spacer"></div>
|
||||
<div class="sticky w-full flex items-center justify-center">
|
||||
<button class="cta-submit" type="button" (click)="save()" [disabled]="form.invalid || form.pending">
|
||||
<ui-spinner [show]="busy$ | async">Speichern</ui-spinner>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
@if (formData$ | async; as data) {
|
||||
<form (keydown.enter)="$event.preventDefault()">
|
||||
<h1 class="title">Kundendaten erfassen</h1>
|
||||
<p class="description">
|
||||
Um Ihnen den ausgewählten Service zu
|
||||
<br />
|
||||
ermöglichen, legen wir Ihnen gerne
|
||||
<br />
|
||||
ein Kundenkonto an.
|
||||
</p>
|
||||
<app-customer-type-selector
|
||||
[processId]="processId$ | async"
|
||||
[p4mUser]="false"
|
||||
customerType="b2b"
|
||||
(valueChanges)="customerTypeChanged($event)"
|
||||
></app-customer-type-selector>
|
||||
<app-organisation-form-block
|
||||
#orga
|
||||
[tabIndexStart]="1"
|
||||
[data]="data.organisation"
|
||||
(dataChanges)="patchFormData('organisation', $event)"
|
||||
(onInit)="addFormBlock('organisation', $event)"
|
||||
[requiredMarks]="organisationFormBlockRequiredMarks"
|
||||
[validatorFns]="organisationFormBlockValidators"
|
||||
></app-organisation-form-block>
|
||||
<app-name-form-block
|
||||
#name
|
||||
[tabIndexStart]="orga.tabIndexEnd + 1"
|
||||
[data]="data.name"
|
||||
(dataChanges)="patchFormData('name', $event)"
|
||||
(onInit)="addFormBlock('name', $event)"
|
||||
></app-name-form-block>
|
||||
<app-address-form-block
|
||||
#address
|
||||
[tabIndexStart]="name.tabIndexEnd + 1"
|
||||
[data]="data.address"
|
||||
(dataChanges)="patchFormData('address', $event)"
|
||||
(onInit)="addFormBlock('address', $event)"
|
||||
[requiredMarks]="addressRequiredMarks"
|
||||
[validatorFns]="addressValidators"
|
||||
[defaults]="{ country: 'DEU' }"
|
||||
></app-address-form-block>
|
||||
<app-email-form-block
|
||||
#email
|
||||
[tabIndexStart]="address.tabIndexEnd + 1"
|
||||
[data]="data.email"
|
||||
(dataChanges)="patchFormData('email', $event)"
|
||||
[validatorFns]="emailFormBlockValidators"
|
||||
(onInit)="addFormBlock('email', $event)"
|
||||
></app-email-form-block>
|
||||
<app-phone-numbers-form-block
|
||||
#phoneNumbers
|
||||
[tabIndexStart]="email.tabIndexEnd + 1"
|
||||
[data]="data.phoneNumbers"
|
||||
(dataChanges)="patchFormData('phoneNumbers', $event)"
|
||||
(onInit)="addFormBlock('phoneNumbers', $event)"
|
||||
></app-phone-numbers-form-block>
|
||||
<app-deviating-address-form-block
|
||||
[tabIndexStart]="phoneNumbers.tabIndexEnd + 1"
|
||||
[data]="data.deviatingDeliveryAddress"
|
||||
(dataChanges)="patchFormData('deviatingDeliveryAddress', $event)"
|
||||
(onInit)="addFormBlock('deviatingDeliveryAddress', $event)"
|
||||
[nameRequiredMarks]="deviatingNameRequiredMarks"
|
||||
[nameValidatorFns]="deviatingNameValidationFns"
|
||||
[addressRequiredMarks]="addressRequiredMarks"
|
||||
[addressValidatorFns]="addressValidators"
|
||||
[organisationRequiredMarks]="organisationFormBlockRequiredMarks"
|
||||
[organisationValidatorFns]="organisationFormBlockValidators"
|
||||
[defaults]="{ address: { country: 'DEU' } }"
|
||||
[organisation]="true"
|
||||
[email]="true"
|
||||
[phoneNumbers]="true"
|
||||
>
|
||||
Die Lieferadresse weicht von der Rechnungsadresse ab
|
||||
</app-deviating-address-form-block>
|
||||
<div class="spacer"></div>
|
||||
<div class="sticky w-full flex items-center justify-center">
|
||||
<button class="cta-submit" type="button" (click)="save()" [disabled]="form.invalid || form.pending">
|
||||
<ui-spinner [show]="busy$ | async">Speichern</ui-spinner>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { Component, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { ActivatedRoute, RouterModule } from '@angular/router';
|
||||
import { CustomerCreateSideViewModule } from './customer-create-side-view';
|
||||
@@ -10,7 +10,7 @@ import { SharedSplitscreenComponent } from '@shared/components/splitscreen';
|
||||
templateUrl: 'create-customer.component.html',
|
||||
styleUrls: ['create-customer.component.css'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [CommonModule, RouterModule, SharedSplitscreenComponent, CustomerCreateSideViewModule],
|
||||
imports: [RouterModule, SharedSplitscreenComponent, CustomerCreateSideViewModule],
|
||||
})
|
||||
export class CreateCustomerComponent {
|
||||
processId$ = this._activatedRoute.parent.data.pipe(map((data) => data.processId));
|
||||
|
||||
@@ -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,102 +1,94 @@
|
||||
<form *ngIf="formData$ | async; let data" (keydown.enter)="$event.preventDefault()">
|
||||
<h1 class="title">Kundendaten erfassen</h1>
|
||||
<p class="description">
|
||||
Um Ihnen den ausgewählten Service
|
||||
<br />
|
||||
zu ermöglichen, legen wir Ihnen
|
||||
<br />
|
||||
gerne ein Kundenkonto an.
|
||||
<br />
|
||||
</p>
|
||||
|
||||
<app-customer-type-selector
|
||||
[processId]="processId$ | async"
|
||||
[p4mUser]="false"
|
||||
customerType="guest"
|
||||
(valueChanges)="customerTypeChanged($event)"
|
||||
></app-customer-type-selector>
|
||||
|
||||
<app-name-form-block
|
||||
#name
|
||||
[tabIndexStart]="1"
|
||||
[data]="data.name"
|
||||
(dataChanges)="patchFormData('name', $event)"
|
||||
(onInit)="addFormBlock('name', $event)"
|
||||
[requiredMarks]="nameRequiredMarks"
|
||||
[validatorFns]="nameValidationFns"
|
||||
></app-name-form-block>
|
||||
|
||||
<p class="info">
|
||||
Wir werden Ihnen Werbung zu ähnlichen Produkten oder Dienstleistungen aus unserem Sortiment per E-Mail zusenden. Sie können der
|
||||
Verwendung Ihrer Daten jederzeit z.B. mittels der in den E-Mails enthaltenen Abmeldelinks widersprechen, ohne dass hierfür andere als
|
||||
die Übermittlungskosten nach den Basistarifen entstehen.
|
||||
</p>
|
||||
|
||||
<app-email-form-block
|
||||
#email
|
||||
[tabIndexStart]="name.tabIndexEnd + 1"
|
||||
[data]="data.email"
|
||||
[requiredMark]="true"
|
||||
(dataChanges)="patchFormData('email', $event)"
|
||||
[validatorFns]="emailFormBlockValidators"
|
||||
(onInit)="addFormBlock('email', $event)"
|
||||
></app-email-form-block>
|
||||
|
||||
<app-organisation-form-block
|
||||
#orga
|
||||
[tabIndexStart]="email.tabIndexStart + 1"
|
||||
[data]="data.organisation"
|
||||
(dataChanges)="patchFormData('organisation', $event)"
|
||||
(onInit)="addFormBlock('organisation', $event)"
|
||||
appearence="compact"
|
||||
></app-organisation-form-block>
|
||||
|
||||
<app-address-form-block
|
||||
#address
|
||||
[tabIndexStart]="orga.tabIndexEnd + 1"
|
||||
[data]="data.address"
|
||||
(dataChanges)="patchFormData('address', $event)"
|
||||
(onInit)="addFormBlock('address', $event)"
|
||||
[requiredMarks]="addressRequiredMarks"
|
||||
[validatorFns]="addressValidators"
|
||||
[defaults]="{ country: 'DEU' }"
|
||||
></app-address-form-block>
|
||||
|
||||
<app-phone-numbers-form-block
|
||||
#phoneNumbers
|
||||
[tabIndexStart]="address.tabIndexEnd + 1"
|
||||
[data]="data.phoneNumbers"
|
||||
(dataChanges)="patchFormData('phoneNumbers', $event)"
|
||||
(onInit)="addFormBlock('phoneNumbers', $event)"
|
||||
></app-phone-numbers-form-block>
|
||||
|
||||
<app-birth-date-form-block
|
||||
#birthDate
|
||||
[tabIndexStart]="phoneNumbers.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('birthDate', $event)"
|
||||
[data]="data.birthDate"
|
||||
(dataChanges)="patchFormData('birthDate', $event)"
|
||||
></app-birth-date-form-block>
|
||||
|
||||
<app-deviating-address-form-block
|
||||
[tabIndexStart]="birthDate.tabIndexEnd + 1"
|
||||
[data]="data.deviatingDeliveryAddress"
|
||||
(dataChanges)="patchFormData('deviatingDeliveryAddress', $event)"
|
||||
(onInit)="addFormBlock('deviatingDeliveryAddress', $event)"
|
||||
[nameRequiredMarks]="deviatingNameRequiredMarks"
|
||||
[nameValidatorFns]="deviatingNameValidationFns"
|
||||
[addressRequiredMarks]="addressRequiredMarks"
|
||||
[addressValidatorFns]="addressValidators"
|
||||
[defaults]="{ address: { country: 'DEU' } }"
|
||||
[organisation]="true"
|
||||
>
|
||||
Die Lieferadresse weicht von der Rechnungsadresse ab
|
||||
</app-deviating-address-form-block>
|
||||
|
||||
<div class="spacer"></div>
|
||||
<div class="sticky w-full flex items-center justify-center">
|
||||
<button class="cta-submit" type="button" (click)="save()" [disabled]="form.invalid || form.pending">
|
||||
<ui-spinner [show]="busy$ | async">Speichern</ui-spinner>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
@if (formData$ | async; as data) {
|
||||
<form (keydown.enter)="$event.preventDefault()">
|
||||
<h1 class="title">Kundendaten erfassen</h1>
|
||||
<p class="description">
|
||||
Um Ihnen den ausgewählten Service
|
||||
<br />
|
||||
zu ermöglichen, legen wir Ihnen
|
||||
<br />
|
||||
gerne ein Kundenkonto an.
|
||||
<br />
|
||||
</p>
|
||||
<app-customer-type-selector
|
||||
[processId]="processId$ | async"
|
||||
[p4mUser]="false"
|
||||
customerType="guest"
|
||||
(valueChanges)="customerTypeChanged($event)"
|
||||
></app-customer-type-selector>
|
||||
<app-name-form-block
|
||||
#name
|
||||
[tabIndexStart]="1"
|
||||
[data]="data.name"
|
||||
(dataChanges)="patchFormData('name', $event)"
|
||||
(onInit)="addFormBlock('name', $event)"
|
||||
[requiredMarks]="nameRequiredMarks"
|
||||
[validatorFns]="nameValidationFns"
|
||||
></app-name-form-block>
|
||||
<p class="info">
|
||||
Wir werden Ihnen Werbung zu ähnlichen Produkten oder Dienstleistungen aus unserem Sortiment per E-Mail zusenden. Sie können der
|
||||
Verwendung Ihrer Daten jederzeit z.B. mittels der in den E-Mails enthaltenen Abmeldelinks widersprechen, ohne dass hierfür andere als
|
||||
die Übermittlungskosten nach den Basistarifen entstehen.
|
||||
</p>
|
||||
<app-email-form-block
|
||||
#email
|
||||
[tabIndexStart]="name.tabIndexEnd + 1"
|
||||
[data]="data.email"
|
||||
[requiredMark]="true"
|
||||
(dataChanges)="patchFormData('email', $event)"
|
||||
[validatorFns]="emailFormBlockValidators"
|
||||
(onInit)="addFormBlock('email', $event)"
|
||||
></app-email-form-block>
|
||||
<app-organisation-form-block
|
||||
#orga
|
||||
[tabIndexStart]="email.tabIndexStart + 1"
|
||||
[data]="data.organisation"
|
||||
(dataChanges)="patchFormData('organisation', $event)"
|
||||
(onInit)="addFormBlock('organisation', $event)"
|
||||
appearence="compact"
|
||||
></app-organisation-form-block>
|
||||
<app-address-form-block
|
||||
#address
|
||||
[tabIndexStart]="orga.tabIndexEnd + 1"
|
||||
[data]="data.address"
|
||||
(dataChanges)="patchFormData('address', $event)"
|
||||
(onInit)="addFormBlock('address', $event)"
|
||||
[requiredMarks]="addressRequiredMarks"
|
||||
[validatorFns]="addressValidators"
|
||||
[defaults]="{ country: 'DEU' }"
|
||||
></app-address-form-block>
|
||||
<app-phone-numbers-form-block
|
||||
#phoneNumbers
|
||||
[tabIndexStart]="address.tabIndexEnd + 1"
|
||||
[data]="data.phoneNumbers"
|
||||
(dataChanges)="patchFormData('phoneNumbers', $event)"
|
||||
(onInit)="addFormBlock('phoneNumbers', $event)"
|
||||
></app-phone-numbers-form-block>
|
||||
<app-birth-date-form-block
|
||||
#birthDate
|
||||
[tabIndexStart]="phoneNumbers.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('birthDate', $event)"
|
||||
[data]="data.birthDate"
|
||||
(dataChanges)="patchFormData('birthDate', $event)"
|
||||
></app-birth-date-form-block>
|
||||
<app-deviating-address-form-block
|
||||
[tabIndexStart]="birthDate.tabIndexEnd + 1"
|
||||
[data]="data.deviatingDeliveryAddress"
|
||||
(dataChanges)="patchFormData('deviatingDeliveryAddress', $event)"
|
||||
(onInit)="addFormBlock('deviatingDeliveryAddress', $event)"
|
||||
[nameRequiredMarks]="deviatingNameRequiredMarks"
|
||||
[nameValidatorFns]="deviatingNameValidationFns"
|
||||
[addressRequiredMarks]="addressRequiredMarks"
|
||||
[addressValidatorFns]="addressValidators"
|
||||
[defaults]="{ address: { country: 'DEU' } }"
|
||||
[organisation]="true"
|
||||
>
|
||||
Die Lieferadresse weicht von der Rechnungsadresse ab
|
||||
</app-deviating-address-form-block>
|
||||
<div class="spacer"></div>
|
||||
<div class="sticky w-full flex items-center justify-center">
|
||||
<button class="cta-submit" type="button" (click)="save()" [disabled]="form.invalid || form.pending">
|
||||
<ui-spinner [show]="busy$ | async">Speichern</ui-spinner>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<form *ngIf="formData$ | async; let data" (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> -->
|
||||
<!-- @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>
|
||||
</h1>
|
||||
|
||||
<p class="description">
|
||||
Um Sie als Kunde beim nächsten
|
||||
<br />
|
||||
@@ -12,7 +12,6 @@
|
||||
<br />
|
||||
wir Ihnen gerne eine Kundenkarte an.
|
||||
</p>
|
||||
|
||||
<app-customer-type-selector
|
||||
[processId]="processId$ | async"
|
||||
[p4mUser]="true"
|
||||
@@ -20,7 +19,6 @@
|
||||
(valueChanges)="customerTypeChanged($event)"
|
||||
[p4mReadonly]="data?._meta?.p4mRequired"
|
||||
></app-customer-type-selector>
|
||||
|
||||
<app-p4m-number-form-block
|
||||
#p4mBlock
|
||||
[tabIndexStart]="1"
|
||||
@@ -31,7 +29,6 @@
|
||||
[focusAfterInit]="!data?._meta?.p4mRequired"
|
||||
[asyncValidatorFns]="asyncLoyaltyCardValidatorFn"
|
||||
></app-p4m-number-form-block>
|
||||
|
||||
<app-accept-agb-form-block
|
||||
[tabIndexStart]="inBlock.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('agb', $event)"
|
||||
@@ -40,7 +37,6 @@
|
||||
[requiredMark]="true"
|
||||
[validatorFns]="agbValidatorFns"
|
||||
></app-accept-agb-form-block>
|
||||
|
||||
<app-newsletter-form-block
|
||||
class="mb-4"
|
||||
#newsletterBlock
|
||||
@@ -50,7 +46,6 @@
|
||||
(dataChanges)="patchFormData('newsletter', $event)"
|
||||
[focusAfterInit]="data?._meta?.p4mRequired"
|
||||
></app-newsletter-form-block>
|
||||
|
||||
<app-name-form-block
|
||||
#nameBlock
|
||||
[tabIndexStart]="newsletterBlock.tabIndexEnd + 1"
|
||||
@@ -60,13 +55,13 @@
|
||||
[validatorFns]="nameValidationFns"
|
||||
(dataChanges)="patchFormData('name', $event)"
|
||||
></app-name-form-block>
|
||||
|
||||
<p class="info" *ngIf="customerType === 'webshop-p4m'">
|
||||
Wir werden Ihnen Werbung zu ähnlichen Produkten oder Dienstleistungen aus unserem Sortiment per E-Mail zusenden. Sie können der
|
||||
Verwendung Ihrer Daten jederzeit z.B. mittels der in den E-Mails enthaltenen Abmeldelinks widersprechen, ohne dass hierfür andere als
|
||||
die Übermittlungskosten nach den Basistarifen entstehen.
|
||||
</p>
|
||||
|
||||
@if (customerType === 'webshop-p4m') {
|
||||
<p class="info">
|
||||
Wir werden Ihnen Werbung zu ähnlichen Produkten oder Dienstleistungen aus unserem Sortiment per E-Mail zusenden. Sie können der
|
||||
Verwendung Ihrer Daten jederzeit z.B. mittels der in den E-Mails enthaltenen Abmeldelinks widersprechen, ohne dass hierfür andere als
|
||||
die Übermittlungskosten nach den Basistarifen entstehen.
|
||||
</p>
|
||||
}
|
||||
<app-email-form-block
|
||||
class="flex-grow"
|
||||
#email
|
||||
@@ -78,7 +73,6 @@
|
||||
[validatorFns]="emailValidatorFn"
|
||||
[asyncValidatorFns]="asyncEmailVlaidtorFn"
|
||||
></app-email-form-block>
|
||||
|
||||
<app-organisation-form-block
|
||||
#orgBlock
|
||||
[tabIndexStart]="email.tabIndexEnd + 1"
|
||||
@@ -87,7 +81,6 @@
|
||||
[data]="data.organisation"
|
||||
(dataChanges)="patchFormData('organisation', $event)"
|
||||
></app-organisation-form-block>
|
||||
|
||||
<app-address-form-block
|
||||
[defaults]="{ country: 'DEU' }"
|
||||
#addressBlock
|
||||
@@ -98,7 +91,6 @@
|
||||
[requiredMarks]="addressRequiredMarks"
|
||||
[validatorFns]="addressValidatorFns"
|
||||
></app-address-form-block>
|
||||
|
||||
<app-deviating-address-form-block
|
||||
#ddaBlock
|
||||
[defaults]="{ address: { country: 'DEU' } }"
|
||||
@@ -110,10 +102,9 @@
|
||||
[nameValidatorFns]="nameValidationFns"
|
||||
[addressRequiredMarks]="shippingAddressRequiredMarks"
|
||||
[addressValidatorFns]="shippingAddressValidators"
|
||||
>
|
||||
>
|
||||
Die Lieferadresse weicht von der Rechnungsadresse ab
|
||||
</app-deviating-address-form-block>
|
||||
|
||||
<app-phone-numbers-form-block
|
||||
#phoneNumbers
|
||||
[tabIndexStart]="ddaBlock.tabIndexEnd + 1"
|
||||
@@ -121,7 +112,6 @@
|
||||
[data]="data.phoneNumbers"
|
||||
(dataChanges)="patchFormData('phoneNumbers', $event)"
|
||||
></app-phone-numbers-form-block>
|
||||
|
||||
<app-birth-date-form-block
|
||||
#bdBlock
|
||||
[tabIndexStart]="phoneNumbers.tabIndexEnd + 1"
|
||||
@@ -131,7 +121,6 @@
|
||||
[requiredMark]="true"
|
||||
[validatorFns]="birthDateValidatorFns"
|
||||
></app-birth-date-form-block>
|
||||
|
||||
<app-interests-form-block
|
||||
#inBlock
|
||||
[tabIndexStart]="bdBlock.tabIndexEnd + 1"
|
||||
@@ -139,12 +128,11 @@
|
||||
[data]="data.interests"
|
||||
(dataChanges)="patchFormData('interests', $event)"
|
||||
></app-interests-form-block>
|
||||
|
||||
<div class="spacer"></div>
|
||||
|
||||
<div class="sticky w-full flex items-center justify-center">
|
||||
<button class="cta-submit" type="button" [disabled]="form.invalid || form.pending" (click)="save()">
|
||||
<ui-spinner [show]="busy$ | async">Speichern</ui-spinner>
|
||||
</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,86 +1,78 @@
|
||||
<form *ngIf="formData$ | async; let data" (keydown.enter)="$event.preventDefault()">
|
||||
<h1 class="title">Kundendaten erfassen</h1>
|
||||
<p class="description">Wir legen Ihnen gerne ein Kundenkonto an</p>
|
||||
|
||||
<app-customer-type-selector
|
||||
[processId]="processId$ | async"
|
||||
[p4mUser]="false"
|
||||
customerType="store"
|
||||
(valueChanges)="customerTypeChanged($event)"
|
||||
></app-customer-type-selector>
|
||||
|
||||
<app-name-form-block
|
||||
#name
|
||||
[tabIndexStart]="1"
|
||||
(onInit)="addFormBlock('name', $event)"
|
||||
[requiredMarks]="nameRequiredMarks"
|
||||
[validatorFns]="nameValidationFns"
|
||||
[data]="data.name"
|
||||
(dataChanges)="patchFormData('name', $event)"
|
||||
></app-name-form-block>
|
||||
|
||||
<app-email-form-block
|
||||
#email
|
||||
[tabIndexStart]="name.tabIndexEnd + 1"
|
||||
[validatorFns]="emailFormBlockValidators"
|
||||
(onInit)="addFormBlock('email', $event)"
|
||||
[data]="data.email"
|
||||
(dataChanges)="patchFormData('email', $event)"
|
||||
></app-email-form-block>
|
||||
|
||||
<app-organisation-form-block
|
||||
#orga
|
||||
[tabIndexStart]="email.tabIndexEnd + 1"
|
||||
appearence="compact"
|
||||
(onInit)="addFormBlock('organisation', $event)"
|
||||
[data]="data.organisation"
|
||||
(dataChanges)="patchFormData('organisation', $event)"
|
||||
></app-organisation-form-block>
|
||||
|
||||
<app-address-form-block
|
||||
#address
|
||||
[tabIndexStart]="orga.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('address', $event)"
|
||||
[data]="data.address"
|
||||
(dataChanges)="patchFormData('address', $event)"
|
||||
></app-address-form-block>
|
||||
|
||||
<app-phone-numbers-form-block
|
||||
#phoneNumbers
|
||||
[tabIndexStart]="address.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('phoneNumbers', $event)"
|
||||
[data]="data.phoneNumbers"
|
||||
(dataChanges)="patchFormData('phoneNumbers', $event)"
|
||||
></app-phone-numbers-form-block>
|
||||
|
||||
<app-birth-date-form-block
|
||||
#birthDate
|
||||
[tabIndexStart]="phoneNumbers.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('birthDate', $event)"
|
||||
[data]="data.birthDate"
|
||||
(dataChanges)="patchFormData('birthDate', $event)"
|
||||
></app-birth-date-form-block>
|
||||
|
||||
<app-deviating-address-form-block
|
||||
[tabIndexStart]="birthDate.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('deviatingDeliveryAddress', $event)"
|
||||
[organisation]="true"
|
||||
[nameRequiredMarks]="nameRequiredMarks"
|
||||
[nameValidatorFns]="nameValidationFns"
|
||||
[addressRequiredMarks]="addressRequiredMarks"
|
||||
[addressValidatorFns]="deviatingAddressValidators"
|
||||
[defaults]="{ address: { country: 'DEU' } }"
|
||||
[data]="data.deviatingDeliveryAddress"
|
||||
(dataChanges)="patchFormData('deviatingDeliveryAddress', $event)"
|
||||
>
|
||||
Die Lieferadresse weicht von der Rechnungsadresse ab
|
||||
</app-deviating-address-form-block>
|
||||
|
||||
<div class="spacer"></div>
|
||||
|
||||
<div class="sticky w-full flex items-center justify-center">
|
||||
<button class="cta-submit" type="button" [disabled]="form.invalid || form.pending" (click)="save()">
|
||||
<ui-spinner [show]="busy$ | async">Speichern</ui-spinner>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
@if (formData$ | async; as data) {
|
||||
<form (keydown.enter)="$event.preventDefault()">
|
||||
<h1 class="title">Kundendaten erfassen</h1>
|
||||
<p class="description">Wir legen Ihnen gerne ein Kundenkonto an</p>
|
||||
<app-customer-type-selector
|
||||
[processId]="processId$ | async"
|
||||
[p4mUser]="false"
|
||||
customerType="store"
|
||||
(valueChanges)="customerTypeChanged($event)"
|
||||
></app-customer-type-selector>
|
||||
<app-name-form-block
|
||||
#name
|
||||
[tabIndexStart]="1"
|
||||
(onInit)="addFormBlock('name', $event)"
|
||||
[requiredMarks]="nameRequiredMarks"
|
||||
[validatorFns]="nameValidationFns"
|
||||
[data]="data.name"
|
||||
(dataChanges)="patchFormData('name', $event)"
|
||||
></app-name-form-block>
|
||||
<app-email-form-block
|
||||
#email
|
||||
[tabIndexStart]="name.tabIndexEnd + 1"
|
||||
[validatorFns]="emailFormBlockValidators"
|
||||
(onInit)="addFormBlock('email', $event)"
|
||||
[data]="data.email"
|
||||
(dataChanges)="patchFormData('email', $event)"
|
||||
></app-email-form-block>
|
||||
<app-organisation-form-block
|
||||
#orga
|
||||
[tabIndexStart]="email.tabIndexEnd + 1"
|
||||
appearence="compact"
|
||||
(onInit)="addFormBlock('organisation', $event)"
|
||||
[data]="data.organisation"
|
||||
(dataChanges)="patchFormData('organisation', $event)"
|
||||
></app-organisation-form-block>
|
||||
<app-address-form-block
|
||||
#address
|
||||
[tabIndexStart]="orga.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('address', $event)"
|
||||
[data]="data.address"
|
||||
(dataChanges)="patchFormData('address', $event)"
|
||||
></app-address-form-block>
|
||||
<app-phone-numbers-form-block
|
||||
#phoneNumbers
|
||||
[tabIndexStart]="address.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('phoneNumbers', $event)"
|
||||
[data]="data.phoneNumbers"
|
||||
(dataChanges)="patchFormData('phoneNumbers', $event)"
|
||||
></app-phone-numbers-form-block>
|
||||
<app-birth-date-form-block
|
||||
#birthDate
|
||||
[tabIndexStart]="phoneNumbers.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('birthDate', $event)"
|
||||
[data]="data.birthDate"
|
||||
(dataChanges)="patchFormData('birthDate', $event)"
|
||||
></app-birth-date-form-block>
|
||||
<app-deviating-address-form-block
|
||||
[tabIndexStart]="birthDate.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('deviatingDeliveryAddress', $event)"
|
||||
[organisation]="true"
|
||||
[nameRequiredMarks]="nameRequiredMarks"
|
||||
[nameValidatorFns]="nameValidationFns"
|
||||
[addressRequiredMarks]="addressRequiredMarks"
|
||||
[addressValidatorFns]="deviatingAddressValidators"
|
||||
[defaults]="{ address: { country: 'DEU' } }"
|
||||
[data]="data.deviatingDeliveryAddress"
|
||||
(dataChanges)="patchFormData('deviatingDeliveryAddress', $event)"
|
||||
>
|
||||
Die Lieferadresse weicht von der Rechnungsadresse ab
|
||||
</app-deviating-address-form-block>
|
||||
<div class="spacer"></div>
|
||||
<div class="sticky w-full flex items-center justify-center">
|
||||
<button class="cta-submit" type="button" [disabled]="form.invalid || form.pending" (click)="save()">
|
||||
<ui-spinner [show]="busy$ | async">Speichern</ui-spinner>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
|
||||
@@ -1,104 +1,95 @@
|
||||
<form *ngIf="formData$ | async; let data" (keydown.enter)="$event.preventDefault()">
|
||||
<h1 class="title">Kundendaten erfassen</h1>
|
||||
<p class="description">
|
||||
Wenn Sie möchten legen wir Ihnen
|
||||
<br />
|
||||
gerne ein Onlinekonto an. Dort können
|
||||
<br />
|
||||
Sie Ihre Bestellungen einsehen.
|
||||
</p>
|
||||
|
||||
<app-customer-type-selector
|
||||
[processId]="processId$ | async"
|
||||
[p4mUser]="false"
|
||||
customerType="webshop"
|
||||
(valueChanges)="customerTypeChanged($event)"
|
||||
></app-customer-type-selector>
|
||||
|
||||
<app-name-form-block
|
||||
#name
|
||||
[tabIndexStart]="1"
|
||||
(onInit)="addFormBlock('name', $event)"
|
||||
[requiredMarks]="nameRequiredMarks"
|
||||
[validatorFns]="nameValidationFns"
|
||||
[data]="data.name"
|
||||
(dataChanges)="patchFormData('name', $event)"
|
||||
></app-name-form-block>
|
||||
|
||||
<p class="info">
|
||||
Wir werden Ihnen Werbung zu ähnlichen Produkten oder Dienstleistungen aus unserem Sortiment per E-Mail zusenden. Sie können der
|
||||
Verwendung Ihrer Daten jederzeit z.B. mittels der in den E-Mails enthaltenen Abmeldelinks widersprechen, ohne dass hierfür andere als
|
||||
die Übermittlungskosten nach den Basistarifen entstehen.
|
||||
</p>
|
||||
|
||||
<app-email-form-block
|
||||
#email
|
||||
[tabIndexStart]="name.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('email', $event)"
|
||||
[requiredMark]="true"
|
||||
[asyncValidatorFns]="asyncEmailValidatorFns"
|
||||
[validatorFns]="emailValidatorFns"
|
||||
[data]="data.email"
|
||||
(dataChanges)="patchFormData('email', $event)"
|
||||
></app-email-form-block>
|
||||
|
||||
<app-organisation-form-block
|
||||
#org
|
||||
[tabIndexStart]="email.tabIndexEnd + 1"
|
||||
appearence="compact"
|
||||
(onInit)="addFormBlock('organisation', $event)"
|
||||
[data]="data.organisation"
|
||||
(dataChanges)="patchFormData('organisation', $event)"
|
||||
></app-organisation-form-block>
|
||||
|
||||
<app-address-form-block
|
||||
#address
|
||||
[tabIndexStart]="org.tabIndexEnd + 1"
|
||||
[requiredMarks]="addressRequiredMarks"
|
||||
[validatorFns]="addressValidators"
|
||||
(onInit)="addFormBlock('address', $event)"
|
||||
[defaults]="{ country: 'DEU' }"
|
||||
[data]="data.address"
|
||||
(dataChanges)="patchFormData('address', $event)"
|
||||
></app-address-form-block>
|
||||
|
||||
<p class="info">Das Anlegen geht für Sie noch schneller, wenn wir Ihnen das initiale Passwort per SMS auf Ihr Mobilgerät schicken.</p>
|
||||
|
||||
<app-phone-numbers-form-block
|
||||
#phoneNumbers
|
||||
[tabIndexStart]="address.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('phoneNumbers', $event)"
|
||||
[data]="data.phoneNumbers"
|
||||
(dataChanges)="patchFormData('phoneNumbers', $event)"
|
||||
></app-phone-numbers-form-block>
|
||||
|
||||
<app-birth-date-form-block
|
||||
#birthDate
|
||||
[tabIndexStart]="phoneNumbers.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('birthDate', $event)"
|
||||
[data]="data.birthDate"
|
||||
(dataChanges)="patchFormData('birthDate', $event)"
|
||||
></app-birth-date-form-block>
|
||||
|
||||
<app-deviating-address-form-block
|
||||
[tabIndexStart]="birthDate.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('deviatingDeliveryAddress', $event)"
|
||||
appearence="b2b"
|
||||
[nameRequiredMarks]="nameRequiredMarks"
|
||||
[nameValidatorFns]="nameValidationFns"
|
||||
[addressRequiredMarks]="addressRequiredMarks"
|
||||
[addressValidatorFns]="addressValidators"
|
||||
[defaults]="{ address: { country: 'DEU' } }"
|
||||
[data]="data.deviatingDeliveryAddress"
|
||||
(dataChanges)="patchFormData('deviatingDeliveryAddress', $event)"
|
||||
>
|
||||
Die Lieferadresse weicht von der Rechnungsadresse ab
|
||||
</app-deviating-address-form-block>
|
||||
|
||||
<div class="spacer"></div>
|
||||
<div class="sticky w-full flex items-center justify-center">
|
||||
<button class="cta-submit" type="button" [disabled]="form.invalid || form.pending" (click)="save()">
|
||||
<ui-spinner [show]="busy$ | async">Speichern</ui-spinner>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
@if (formData$ | async; as data) {
|
||||
<form (keydown.enter)="$event.preventDefault()">
|
||||
<h1 class="title">Kundendaten erfassen</h1>
|
||||
<p class="description">
|
||||
Wenn Sie möchten legen wir Ihnen
|
||||
<br />
|
||||
gerne ein Onlinekonto an. Dort können
|
||||
<br />
|
||||
Sie Ihre Bestellungen einsehen.
|
||||
</p>
|
||||
<app-customer-type-selector
|
||||
[processId]="processId$ | async"
|
||||
[p4mUser]="false"
|
||||
customerType="webshop"
|
||||
(valueChanges)="customerTypeChanged($event)"
|
||||
></app-customer-type-selector>
|
||||
<app-name-form-block
|
||||
#name
|
||||
[tabIndexStart]="1"
|
||||
(onInit)="addFormBlock('name', $event)"
|
||||
[requiredMarks]="nameRequiredMarks"
|
||||
[validatorFns]="nameValidationFns"
|
||||
[data]="data.name"
|
||||
(dataChanges)="patchFormData('name', $event)"
|
||||
></app-name-form-block>
|
||||
<p class="info">
|
||||
Wir werden Ihnen Werbung zu ähnlichen Produkten oder Dienstleistungen aus unserem Sortiment per E-Mail zusenden. Sie können der
|
||||
Verwendung Ihrer Daten jederzeit z.B. mittels der in den E-Mails enthaltenen Abmeldelinks widersprechen, ohne dass hierfür andere als
|
||||
die Übermittlungskosten nach den Basistarifen entstehen.
|
||||
</p>
|
||||
<app-email-form-block
|
||||
#email
|
||||
[tabIndexStart]="name.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('email', $event)"
|
||||
[requiredMark]="true"
|
||||
[asyncValidatorFns]="asyncEmailValidatorFns"
|
||||
[validatorFns]="emailValidatorFns"
|
||||
[data]="data.email"
|
||||
(dataChanges)="patchFormData('email', $event)"
|
||||
></app-email-form-block>
|
||||
<app-organisation-form-block
|
||||
#org
|
||||
[tabIndexStart]="email.tabIndexEnd + 1"
|
||||
appearence="compact"
|
||||
(onInit)="addFormBlock('organisation', $event)"
|
||||
[data]="data.organisation"
|
||||
(dataChanges)="patchFormData('organisation', $event)"
|
||||
></app-organisation-form-block>
|
||||
<app-address-form-block
|
||||
#address
|
||||
[tabIndexStart]="org.tabIndexEnd + 1"
|
||||
[requiredMarks]="addressRequiredMarks"
|
||||
[validatorFns]="addressValidators"
|
||||
(onInit)="addFormBlock('address', $event)"
|
||||
[defaults]="{ country: 'DEU' }"
|
||||
[data]="data.address"
|
||||
(dataChanges)="patchFormData('address', $event)"
|
||||
></app-address-form-block>
|
||||
<p class="info">Das Anlegen geht für Sie noch schneller, wenn wir Ihnen das initiale Passwort per SMS auf Ihr Mobilgerät schicken.</p>
|
||||
<app-phone-numbers-form-block
|
||||
#phoneNumbers
|
||||
[tabIndexStart]="address.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('phoneNumbers', $event)"
|
||||
[data]="data.phoneNumbers"
|
||||
(dataChanges)="patchFormData('phoneNumbers', $event)"
|
||||
></app-phone-numbers-form-block>
|
||||
<app-birth-date-form-block
|
||||
#birthDate
|
||||
[tabIndexStart]="phoneNumbers.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('birthDate', $event)"
|
||||
[data]="data.birthDate"
|
||||
(dataChanges)="patchFormData('birthDate', $event)"
|
||||
></app-birth-date-form-block>
|
||||
<app-deviating-address-form-block
|
||||
[tabIndexStart]="birthDate.tabIndexEnd + 1"
|
||||
(onInit)="addFormBlock('deviatingDeliveryAddress', $event)"
|
||||
appearence="b2b"
|
||||
[nameRequiredMarks]="nameRequiredMarks"
|
||||
[nameValidatorFns]="nameValidationFns"
|
||||
[addressRequiredMarks]="addressRequiredMarks"
|
||||
[addressValidatorFns]="addressValidators"
|
||||
[defaults]="{ address: { country: 'DEU' } }"
|
||||
[data]="data.deviatingDeliveryAddress"
|
||||
(dataChanges)="patchFormData('deviatingDeliveryAddress', $event)"
|
||||
>
|
||||
Die Lieferadresse weicht von der Rechnungsadresse ab
|
||||
</app-deviating-address-form-block>
|
||||
<div class="spacer"></div>
|
||||
<div class="sticky w-full flex items-center justify-center">
|
||||
<button class="cta-submit" type="button" [disabled]="form.invalid || form.pending" (click)="save()">
|
||||
<ui-spinner [show]="busy$ | async">Speichern</ui-spinner>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
|
||||
@@ -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,4 +1,4 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { Component, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { RouterModule } from '@angular/router';
|
||||
|
||||
@@ -7,7 +7,7 @@ import { RouterModule } from '@angular/router';
|
||||
templateUrl: 'customer-create-side-view.component.html',
|
||||
styleUrls: ['customer-create-side-view.component.css'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [CommonModule, RouterModule],
|
||||
imports: [RouterModule],
|
||||
})
|
||||
export class CustomerCreateSideViewComponent {
|
||||
constructor() {}
|
||||
|
||||
@@ -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';
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user