mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
Compare commits
350 Commits
hotfix-sca
...
feature/49
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2598ee976e | ||
|
|
492ef73215 | ||
|
|
fdfff237f2 | ||
|
|
def52fde63 | ||
|
|
0957617b93 | ||
|
|
a67305369e | ||
|
|
9c74dc15d2 | ||
|
|
e65085439e | ||
|
|
621a8a5dc7 | ||
|
|
beeba1004e | ||
|
|
4885a523ab | ||
|
|
f7a8cbf31d | ||
|
|
29d5c24e59 | ||
|
|
afff1ea8fd | ||
|
|
3c43d50f0f | ||
|
|
e04d88f2ce | ||
|
|
a50f02cb5b | ||
|
|
aff6d18888 | ||
|
|
8144253a18 | ||
|
|
718918e3dc | ||
|
|
82c1861fdc | ||
|
|
82e04917b7 | ||
|
|
cdcd41a884 | ||
|
|
3e14426d2e | ||
|
|
a93251f082 | ||
|
|
de47c493bf | ||
|
|
23876e3266 | ||
|
|
4ac84df25c | ||
|
|
c027791e27 | ||
|
|
3d18e45f59 | ||
|
|
03b132fc94 | ||
|
|
206586035d | ||
|
|
67d8902423 | ||
|
|
1c2d0421c4 | ||
|
|
b4caf3a177 | ||
|
|
ae3662dfd1 | ||
|
|
291386e4fd | ||
|
|
6a7d509aa4 | ||
|
|
620ffae55c | ||
|
|
59ad7710d9 | ||
|
|
8ca7977f7c | ||
|
|
62d0783e88 | ||
|
|
bd1e4f36e1 | ||
|
|
79356fa130 | ||
|
|
c1a40ae82f | ||
|
|
492dae14f7 | ||
|
|
9950c76482 | ||
|
|
7e7a5ebab9 | ||
|
|
41fc8e0fb1 | ||
|
|
41067a7e54 | ||
|
|
e1a50b0ce0 | ||
|
|
227af192e6 | ||
|
|
bd21b674bf | ||
|
|
b21395ed61 | ||
|
|
da27745ebe | ||
|
|
8a94da6868 | ||
|
|
81a7154470 | ||
|
|
0dee30062f | ||
|
|
eb0a0d3dc3 | ||
|
|
67dcb49a1d | ||
|
|
f3e2e9fee3 | ||
|
|
a4b092a021 | ||
|
|
aeacd0077f | ||
|
|
78a0e828b8 | ||
|
|
b508abefaf | ||
|
|
df49e3a79b | ||
|
|
34512f3b9a | ||
|
|
093bb3b484 | ||
|
|
921edf8066 | ||
|
|
858242c6dd | ||
|
|
119bcd9df9 | ||
|
|
b43d0fcea6 | ||
|
|
ddad3ad967 | ||
|
|
aaa161424e | ||
|
|
3bbec6a68d | ||
|
|
2a8a929fd7 | ||
|
|
3bcdfccb5c | ||
|
|
9696084f7b | ||
|
|
417bd649e2 | ||
|
|
d38fed297d | ||
|
|
3c110efdfa | ||
|
|
1cbabd2d7a | ||
|
|
133020ece1 | ||
|
|
549d419b69 | ||
|
|
8bbaf1c70c | ||
|
|
d0b7c95be2 | ||
|
|
b0dba2325d | ||
|
|
a9c606ec21 | ||
|
|
81bec4b153 | ||
|
|
0c2feb96ac | ||
|
|
1855b1970d | ||
|
|
0a46258588 | ||
|
|
ca2e529bdf | ||
|
|
82a2d70ce4 | ||
|
|
fa39b6d071 | ||
|
|
0f3f456909 | ||
|
|
727e0469ad | ||
|
|
7e3d6b4e61 | ||
|
|
453403cfde | ||
|
|
9001850c1f | ||
|
|
b97ad4f24b | ||
|
|
452de44f34 | ||
|
|
db7da0699e | ||
|
|
fbd5414e47 | ||
|
|
5310619211 | ||
|
|
edbdba6868 | ||
|
|
2d5fce8554 | ||
|
|
f5b7da5bd2 | ||
|
|
d4c1cdbc6e | ||
|
|
576d439a79 | ||
|
|
00fc978c4f | ||
|
|
44e596327e | ||
|
|
3d95bddb23 | ||
|
|
2210aeb1c2 | ||
|
|
a2b6847898 | ||
|
|
3f252639d5 | ||
|
|
3f7df0f748 | ||
|
|
703090eabd | ||
|
|
33694357bd | ||
|
|
91668e53fa | ||
|
|
39e4efff2b | ||
|
|
1a4d0a38da | ||
|
|
6c4641d2b7 | ||
|
|
815523b4ca | ||
|
|
57b5f30a66 | ||
|
|
94919efd83 | ||
|
|
cd0d740dc2 | ||
|
|
dc3970ceea | ||
|
|
5bba1dff8f | ||
|
|
7ff6e9495e | ||
|
|
04403179d7 | ||
|
|
a39706bff3 | ||
|
|
0ac34740bb | ||
|
|
24c2c1c77d | ||
|
|
a364a4f0e0 | ||
|
|
dcc70745da | ||
|
|
effce6f41c | ||
|
|
a5feaba5e3 | ||
|
|
d8bb42b8c6 | ||
|
|
f74494f34e | ||
|
|
abce5f43e2 | ||
|
|
ce4a6b36b6 | ||
|
|
298ea042f2 | ||
|
|
573d6a740e | ||
|
|
8eb5e09490 | ||
|
|
aa8869ceb1 | ||
|
|
e5f42c9de2 | ||
|
|
532c7e5e86 | ||
|
|
30ccd93967 | ||
|
|
b85538f98a | ||
|
|
d9dede4341 | ||
|
|
4a3c934fe0 | ||
|
|
944fb8a186 | ||
|
|
73fd487a13 | ||
|
|
592027f648 | ||
|
|
84243ac4e6 | ||
|
|
04b9422d5d | ||
|
|
584cb63eaf | ||
|
|
b9871bba54 | ||
|
|
6769e3864e | ||
|
|
cebb644da9 | ||
|
|
734a7b8739 | ||
|
|
e89d1999a6 | ||
|
|
b7cbd50e83 | ||
|
|
76aa04bc4c | ||
|
|
b951cf7024 | ||
|
|
f896d91ebb | ||
|
|
a884adc3a9 | ||
|
|
ce9bc9511a | ||
|
|
2653322232 | ||
|
|
86eb0bb494 | ||
|
|
73be50e7d2 | ||
|
|
da5151df78 | ||
|
|
df47b932b6 | ||
|
|
c096609a27 | ||
|
|
6b07b322f4 | ||
|
|
1b821db248 | ||
|
|
ecf446671c | ||
|
|
a2f204d0d6 | ||
|
|
c8678b7e91 | ||
|
|
ec41738def | ||
|
|
53a7f01507 | ||
|
|
7366f038e5 | ||
|
|
1c9cd2a0b0 | ||
|
|
591824196b | ||
|
|
09aa3f09cb | ||
|
|
eff67b9a06 | ||
|
|
c9b2762bbc | ||
|
|
c49b0625c1 | ||
|
|
42451e2144 | ||
|
|
f52fb00df7 | ||
|
|
b485bb768c | ||
|
|
3f77646f8a | ||
|
|
eb6149a6e3 | ||
|
|
5aa98bd90b | ||
|
|
d71404f400 | ||
|
|
f68eb33852 | ||
|
|
39d790c121 | ||
|
|
10349409fb | ||
|
|
0dc02abc8a | ||
|
|
388346e21b | ||
|
|
1847c6944e | ||
|
|
fd11cf19e4 | ||
|
|
8e7b067310 | ||
|
|
c6a174d93f | ||
|
|
9efbfab253 | ||
|
|
d474f555e3 | ||
|
|
c59a09c252 | ||
|
|
f743ce59fa | ||
|
|
99feb499a2 | ||
|
|
6d28662431 | ||
|
|
27174e4ed3 | ||
|
|
9b1b2c4682 | ||
|
|
674e2b7e1b | ||
|
|
035abde3c3 | ||
|
|
ca998c0685 | ||
|
|
bb5b6e2e59 | ||
|
|
f57988f83e | ||
|
|
17a68b9dbb | ||
|
|
b6aab4f743 | ||
|
|
a0c8035dbb | ||
|
|
34e96f0751 | ||
|
|
9ba05253e9 | ||
|
|
98a9346c1a | ||
|
|
fa66d2389a | ||
|
|
6743c8e630 | ||
|
|
f62e198aed | ||
|
|
387e6b08ed | ||
|
|
721fd06c76 | ||
|
|
0fcdb308b5 | ||
|
|
5492329a21 | ||
|
|
f97253e82a | ||
|
|
b926efb635 | ||
|
|
1becbec412 | ||
|
|
cdc2553d73 | ||
|
|
8781c50e34 | ||
|
|
05eb3cc756 | ||
|
|
6e1c434edf | ||
|
|
ed8e937924 | ||
|
|
1bd17fd887 | ||
|
|
c35c82eaab | ||
|
|
258faec021 | ||
|
|
4bcc523480 | ||
|
|
8900a77d7a | ||
|
|
895e2bd2ec | ||
|
|
4b10dd96d9 | ||
|
|
1126e4f0c1 | ||
|
|
e9f24a88d6 | ||
|
|
f30de35d51 | ||
|
|
0c6f8abbad | ||
|
|
54b37436eb | ||
|
|
02bae79e4a | ||
|
|
cb6779fc83 | ||
|
|
f2c95b6a16 | ||
|
|
d48680c59e | ||
|
|
775390b5df | ||
|
|
1788f566e3 | ||
|
|
d4e1088190 | ||
|
|
a8ecd1f07b | ||
|
|
2c239ac597 | ||
|
|
200eb7f217 | ||
|
|
694fc6d084 | ||
|
|
8ae990bcde | ||
|
|
301f5878c2 | ||
|
|
39c8a512f4 | ||
|
|
f37dfd41f1 | ||
|
|
a518fc50e2 | ||
|
|
c9236f191b | ||
|
|
d1584d1edb | ||
|
|
5f34b514ef | ||
|
|
7a1ef06a4c | ||
|
|
acc2f7f664 | ||
|
|
ad08e999a2 | ||
|
|
1d472ce3df | ||
|
|
92d760b8b4 | ||
|
|
1d19779dac | ||
|
|
294be5dcb4 | ||
|
|
90e671d285 | ||
|
|
9a2c520ab4 | ||
|
|
13d41a7a81 | ||
|
|
79b0a1324c | ||
|
|
0fd94273ce | ||
|
|
44abd4698e | ||
|
|
2b262cc8be | ||
|
|
5775e444b8 | ||
|
|
cdfe88c1cc | ||
|
|
c71d1f8886 | ||
|
|
a09eef038e | ||
|
|
f0a0189523 | ||
|
|
4d42c4ea45 | ||
|
|
cad2926c45 | ||
|
|
161d9c6fea | ||
|
|
1b33258728 | ||
|
|
73b6133306 | ||
|
|
eb6e93149e | ||
|
|
33fb44f20a | ||
|
|
8723f7aa7e | ||
|
|
03815586f7 | ||
|
|
86a11ff07a | ||
|
|
41be8533dc | ||
|
|
186afbc828 | ||
|
|
c3561339a9 | ||
|
|
5312073184 | ||
|
|
4dfe3bfa11 | ||
|
|
9b7a1b1c21 | ||
|
|
a290d3b249 | ||
|
|
ad348af551 | ||
|
|
f1bdba5d10 | ||
|
|
c4134e7f99 | ||
|
|
b7a16f5d30 | ||
|
|
4105709286 | ||
|
|
0c3b322fbd | ||
|
|
12096754c7 | ||
|
|
453d921a99 | ||
|
|
bad05fd098 | ||
|
|
363daf1e35 | ||
|
|
e0cb0974cf | ||
|
|
c3d9274766 | ||
|
|
bc16b841fb | ||
|
|
d5dc4e053d | ||
|
|
3c6833988c | ||
|
|
28fb4ebb48 | ||
|
|
2118bd996a | ||
|
|
8a6448cc17 | ||
|
|
f2c7d57ad6 | ||
|
|
9c9ddfaeec | ||
|
|
6eaa347de5 | ||
|
|
06e248d615 | ||
|
|
67cf380948 | ||
|
|
e0ae79bc2a | ||
|
|
8ccc29c85a | ||
|
|
c68706b54f | ||
|
|
b271ce9711 | ||
|
|
94888213b1 | ||
|
|
1041d92486 | ||
|
|
43d8d220c9 | ||
|
|
e0993d9c46 | ||
|
|
82656d9b27 | ||
|
|
df36d0934d | ||
|
|
920b8eb8e3 | ||
|
|
4db28b1aa7 | ||
|
|
3a9820aa54 | ||
|
|
30ad99332e | ||
|
|
4b48275910 | ||
|
|
d3e3316459 | ||
|
|
0c2a23e5d2 | ||
|
|
36bd2c1eba | ||
|
|
a38d2eede6 | ||
|
|
ed7dc10246 | ||
|
|
f5251d9069 |
36
.github/commit-instructions.md
vendored
Normal file
36
.github/commit-instructions.md
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
# Commit Message Instructions
|
||||
|
||||
## Format
|
||||
|
||||
Each commit message should follow this structure:
|
||||
|
||||
1. **Short Summary**: A brief summary of the changes (max 72 characters).
|
||||
2. **List of Changes**: A detailed list of changes with icons to indicate the type of change.
|
||||
|
||||
---
|
||||
|
||||
### Example
|
||||
|
||||
```
|
||||
Added a new module to handle user authentication, including login and registration.
|
||||
|
||||
- ✨ **Feature**: Implemented user login functionality
|
||||
- 🐛 **Fix**: Resolved session timeout issue
|
||||
- 🛠️ **Refactor**: Improved error handling in auth service
|
||||
- 🧪 **Test**: Added unit tests for login component
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Icons for Change Types
|
||||
|
||||
- ✨ **Feature**: New features or functionality
|
||||
- 🐛 **Fix**: Bug fixes
|
||||
- 🛠️ **Refactor**: Code improvements without changing functionality
|
||||
- 🧪 **Test**: Adding or updating tests
|
||||
- 📚 **Docs**: Documentation updates
|
||||
- 🗑️ **Chore**: Maintenance tasks (e.g., dependency updates)
|
||||
- 🚀 **Performance**: Performance improvements
|
||||
- 🎨 **Style**: Code style changes (e.g., formatting)
|
||||
- 🔒 **Security**: Security-related changes
|
||||
- ⚙️ **Config**: Configuration changes
|
||||
17
.github/copilot-instructions.md
vendored
Normal file
17
.github/copilot-instructions.md
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
# Spark Instructions
|
||||
|
||||
## Introduction
|
||||
|
||||
You are Spark, a mentor designed to help me with coding, preview my work, and assist me in improving by pointing out areas for enhancement.
|
||||
|
||||
## Tone and Personality
|
||||
|
||||
You are a mentor with a dual approach: when I make a mistake or my work needs improvement, you adopt a strict and technical tone to clearly explain what’s wrong and how to fix it. In all other cases, you are casual and friendly, like a supportive coding buddy, keeping the vibe light and encouraging.
|
||||
|
||||
## Behavioral Guidelines
|
||||
|
||||
- Focus on constructive feedback; avoid simply rewriting my code unless I ask for it.
|
||||
- If my question or code is unclear, ask me for clarification or more details.
|
||||
- Do not discourage me; always frame suggestions as opportunities for growth.
|
||||
- Avoid giving generic answers—tailor your advice to my specific code or problem.
|
||||
- Keep my preferences in mind: prioritize Type safety, follow Clean Code principles and emphasize good documentation.
|
||||
181
.github/review-instructions.md
vendored
Normal file
181
.github/review-instructions.md
vendored
Normal file
@@ -0,0 +1,181 @@
|
||||
# Code Review Instructions
|
||||
|
||||
## Summary
|
||||
|
||||
When conducting a code review, follow these steps to ensure a thorough and constructive process.
|
||||
**Ensure that all review guidelines are followed. If any guideline is not adhered to, make it explicitly clear which guideline needs to be followed.**
|
||||
|
||||
## Review Process
|
||||
|
||||
1. 🎯 **Key Issues**
|
||||
Identify critical issues in the code such as bugs, security vulnerabilities, or violations of the project's coding standards.
|
||||
_Include specific links to files and line numbers (e.g., file.js#L10) where applicable._
|
||||
|
||||
2. 💡 **Suggestions for Improvement**
|
||||
Highlight areas where the code can be enhanced in terms of readability, performance, maintainability, or adherence to best practices.
|
||||
_Clarify what constitutes a "Critical" versus a "Minor" issue to avoid ambiguity._
|
||||
|
||||
3. ✨ **Code Examples**
|
||||
Provide specific, concise code snippets that illustrate your suggestions.
|
||||
_Include both a "Before" (problematic code) and an "After" (improved version) example where beneficial._
|
||||
|
||||
4. 📚 **Relevant Documentation Links**
|
||||
Attach links to useful resources or official documentation to support the suggested changes.
|
||||
_For example, link to ESLint, Jest, or Angular Style Guide pages when relevant._
|
||||
|
||||
## Tone and Feedback
|
||||
|
||||
- Be constructive and supportive.
|
||||
Frame suggestions as opportunities for growth rather than criticism.
|
||||
- Use the following emojis to categorize your feedback:
|
||||
- 🚨 **Critical issues**
|
||||
- ❗ **Minor Issues**
|
||||
- ⚠️ **Warnings**
|
||||
- 💡 **Suggestions**
|
||||
- ✅ **Good practices**
|
||||
|
||||
## Additional Informations
|
||||
|
||||
- Treat missing tests and JSDocs as warnings
|
||||
- Tread missing unit test as warnings
|
||||
|
||||
### Review Template
|
||||
|
||||
````markdown
|
||||
# Code Review
|
||||
|
||||
## Summary
|
||||
|
||||
A brief overview of the code’s overall quality, highlighting key strengths and areas needing attention. This sets the stage for the detailed feedback below.
|
||||
|
||||
---
|
||||
|
||||
## 🚨 Critical Issues
|
||||
|
||||
High-priority issues that must be addressed immediately due to their potential to severely impact functionality, performance, or security.
|
||||
|
||||
### 1. High Priority: [Issue Title]
|
||||
|
||||
#### 🚨 Issue
|
||||
|
||||
Describe the issue clearly, including links to specific files and lines (e.g., file.js#L10). Explain why it’s critical—highlight crashes, security risks, or significant performance issues.
|
||||
|
||||
#### 💡 Suggestions for Improvement
|
||||
|
||||
Provide specific steps or alternative approaches to resolve the issue.
|
||||
|
||||
#### ✨ Code Example
|
||||
|
||||
**Current**: [file](file.js#L10) Problematic code with path to the file and line of the code
|
||||
|
||||
```typescript
|
||||
// Code...
|
||||
```
|
||||
|
||||
**Improvement**: Improved version
|
||||
|
||||
```typescript
|
||||
// Code...
|
||||
```
|
||||
|
||||
#### 📚 Relevant Documentation
|
||||
|
||||
Include URLs for further research (e.g., [Jest Documentation](https://jestjs.io/docs/getting-started)).
|
||||
|
||||
---
|
||||
|
||||
## ❗ Minor Issues
|
||||
|
||||
Issues that can improve code quality, maintainability, or adherence to best practices when resolved.
|
||||
|
||||
### 1. Medium Priority: [Issue Title]
|
||||
|
||||
#### ❗ Issue
|
||||
|
||||
Describe the issue clearly, including file and line references (e.g., file.js#L10). Explain the impact on the project.
|
||||
|
||||
#### 💡 Suggestions for Improvement
|
||||
|
||||
Offer concrete steps or alternative approaches to mitigate the issue.
|
||||
|
||||
#### ✨ Code Example
|
||||
|
||||
**Current**: [file](file.js#L10) Problematic code with path to the file and line of the code
|
||||
|
||||
```typescript
|
||||
// Code...
|
||||
```
|
||||
|
||||
**Improvement**: Improved version
|
||||
|
||||
```typescript
|
||||
// Code...
|
||||
```
|
||||
|
||||
#### 📚 Relevant Documentation
|
||||
|
||||
Provide links to further resources.
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Warnings
|
||||
|
||||
Low-priority issues or suggestions that could help prevent future problems or improve the code quality over time.
|
||||
|
||||
### 1. Low Priority: [Issue Title]
|
||||
|
||||
#### ⚠️ Issue
|
||||
|
||||
Describe the issue clearly with references (e.g., file.js#L10). Explain the potential impact if left unaddressed.
|
||||
|
||||
#### 💡 Suggestions for Improvement
|
||||
|
||||
Provide suggestions or alternative implementations to mitigate the issue.
|
||||
|
||||
#### ✨ Code Example
|
||||
|
||||
**Current**: [file](file.js#L10) Problematic code with path to the file and line of the code
|
||||
|
||||
```typescript
|
||||
// Code...
|
||||
```
|
||||
|
||||
**Improvement**: Improved version
|
||||
|
||||
```typescript
|
||||
// Code...
|
||||
```
|
||||
|
||||
#### 📚 Relevant Documentation
|
||||
|
||||
Include relevant resources for more information.
|
||||
|
||||
---
|
||||
|
||||
## 🛑 Bad Practices
|
||||
|
||||
Highlight up to five bad aspects of the code to reinforce improvements and encourage good practices. Use different funny emoji at the beginning of each bad practice.
|
||||
|
||||
- Emoji **Bad Practice 1**:
|
||||
Describe a specific weakness (e.g., clear code structure) with an example reference (e.g., file.js#L20). Explain why it’s bad.
|
||||
- Emoji **Bad Practice 2**:
|
||||
Outline another negative feature (e.g., effective error handling) with a snippet reference.
|
||||
|
||||
---
|
||||
|
||||
## ✅ Good Practices
|
||||
|
||||
Highlight up to five positive aspects of the code to reinforce well-implemented patterns and encourage good practices. Use different funny emoji at the beginning of each good practice.
|
||||
|
||||
- Emoji **Good Practice 1**:
|
||||
Describe a specific strength (e.g., clear code structure) with an example reference (e.g., file.js#L20). Explain why it’s commendable.
|
||||
- Emoji **Good Practice 2**:
|
||||
Outline another positive feature (e.g., effective error handling) with a snippet reference.
|
||||
|
||||
---
|
||||
|
||||
## 📓 Additional Notes
|
||||
|
||||
- **General Feedback**: Optional thoughts regarding the overall quality or potential areas for future improvement.
|
||||
- **Next Steps**: Outline follow-up actions or further examination areas as needed.
|
||||
````
|
||||
61
.github/testing-instructions.md
vendored
Normal file
61
.github/testing-instructions.md
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
# Testing Instructions
|
||||
|
||||
## Framework and Tools
|
||||
- Use **Jest** as the testing framework.
|
||||
- For unit tests, utilize **Spectator** to simplify Angular component testing.
|
||||
|
||||
## Guidelines
|
||||
1. **Error Case Testing**: Ensure all edge cases and error scenarios are thoroughly tested.
|
||||
2. **Arrange-Act-Assert Pattern**: Follow the Arrange-Act-Assert pattern for structuring your tests:
|
||||
- **Arrange**: Set up the testing environment and initialize required variables.
|
||||
- **Act**: Execute the functionality being tested.
|
||||
- **Assert**: Verify the expected outcomes.
|
||||
|
||||
## Best Practices
|
||||
- Write clear and descriptive test names.
|
||||
- Ensure tests are isolated and do not depend on each other.
|
||||
- Mock external dependencies to avoid side effects.
|
||||
- Aim for high code coverage without compromising test quality.
|
||||
|
||||
## Example Test Structure
|
||||
```typescript
|
||||
// Example using Jest and Spectator
|
||||
import { createComponentFactory, Spectator } from '@ngneat/spectator';
|
||||
import { MyComponent } from './my-component.component';
|
||||
|
||||
describe('MyComponent', () => {
|
||||
let spectator: Spectator<MyComponent>;
|
||||
const createComponent = createComponentFactory(MyComponent);
|
||||
|
||||
beforeEach(() => {
|
||||
spectator = createComponent();
|
||||
});
|
||||
|
||||
it('should display the correct title', () => {
|
||||
// Arrange
|
||||
const expectedTitle = 'Hello World';
|
||||
|
||||
// Act
|
||||
spectator.component.title = expectedTitle;
|
||||
spectator.detectChanges();
|
||||
|
||||
// Assert
|
||||
expect(spectator.query('h1')).toHaveText(expectedTitle);
|
||||
});
|
||||
|
||||
it('should handle error cases gracefully', () => {
|
||||
// Arrange
|
||||
const invalidInput = null;
|
||||
|
||||
// Act
|
||||
spectator.component.input = invalidInput;
|
||||
|
||||
// Assert
|
||||
expect(() => spectator.component.processInput()).toThrowError('Invalid input');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Additional Resources
|
||||
- [Jest Documentation](https://jestjs.io/docs/getting-started)
|
||||
- [Spectator Documentation](https://ngneat.github.io/spectator/)
|
||||
15
.gitignore
vendored
15
.gitignore
vendored
@@ -1,10 +1,14 @@
|
||||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
.matomo
|
||||
|
||||
# compiled output
|
||||
/dist
|
||||
/tmp
|
||||
/out-tsc
|
||||
|
||||
/
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
|
||||
@@ -47,4 +51,13 @@ testem.log
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
libs/swagger/src/lib/*
|
||||
libs/swagger/src/lib/*
|
||||
*storybook.log
|
||||
|
||||
|
||||
.nx/cache
|
||||
.nx/workspace-data
|
||||
.angular
|
||||
|
||||
|
||||
storybook-static
|
||||
1
.husky/pre-commit
Normal file
1
.husky/pre-commit
Normal file
@@ -0,0 +1 @@
|
||||
npx lint-staged
|
||||
1
.husky/pre-push
Normal file
1
.husky/pre-push
Normal file
@@ -0,0 +1 @@
|
||||
npm run ci
|
||||
7
.lintstagedrc.json
Normal file
7
.lintstagedrc.json
Normal file
@@ -0,0 +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"
|
||||
}
|
||||
@@ -1,10 +1,8 @@
|
||||
# Add files here to ignore them from prettier formatting
|
||||
|
||||
/dist
|
||||
/coverage
|
||||
/helmvalues
|
||||
/apps/swagger
|
||||
/ng-swagger-gen
|
||||
|
||||
*.json
|
||||
*.yml
|
||||
/.nx/cache
|
||||
/.nx/workspace-data
|
||||
/node_modules
|
||||
.angular
|
||||
.vscode
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"printWidth": 140
|
||||
}
|
||||
|
||||
15
.vscode/extensions.json
vendored
15
.vscode/extensions.json
vendored
@@ -1,7 +1,10 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"johnpapa.angular2",
|
||||
"esbenp.prettier-vscode",
|
||||
"angular.ng-template",
|
||||
]
|
||||
}
|
||||
"recommendations": [
|
||||
"johnpapa.angular2",
|
||||
"esbenp.prettier-vscode",
|
||||
"angular.ng-template",
|
||||
"nrwl.angular-console",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"firsttris.vscode-jest-runner"
|
||||
]
|
||||
}
|
||||
|
||||
90
.vscode/settings.json
vendored
90
.vscode/settings.json
vendored
@@ -1,15 +1,95 @@
|
||||
{
|
||||
"editor.accessibilitySupport": "off",
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"exportall.config.exclude": [".test.", ".spec.", ".stories."],
|
||||
"editor.formatOnSave": true,
|
||||
"typescriptHero.imports.insertSemicolons": false,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"[javascript]": {
|
||||
"eslint.validate": [
|
||||
"json"
|
||||
],
|
||||
"[html]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"css.validate": false,
|
||||
"less.validate": false,
|
||||
"scss.validate": false
|
||||
"[typescriptreact]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"exportall.config.folderListener": [
|
||||
"/libs/oms/data-access/src/lib/models",
|
||||
"/libs/oms/data-access/src/lib/schemas",
|
||||
"/libs/catalogue/data-access/src/lib/models"
|
||||
],
|
||||
"github.copilot.chat.commitMessageGeneration.instructions": [
|
||||
{
|
||||
"file": ".github/commit-instructions.md"
|
||||
}
|
||||
],
|
||||
"github.copilot.chat.codeGeneration.instructions": [
|
||||
{
|
||||
"file": ".github/copilot-instructions.md"
|
||||
},
|
||||
{
|
||||
"file": ".github/review-instructions.md"
|
||||
},
|
||||
{
|
||||
"file": ".github/testing-instructions.md"
|
||||
},
|
||||
{
|
||||
"file": "docs/tech-stack.md"
|
||||
},
|
||||
{
|
||||
"file": "docs/guidelines/code-style.md"
|
||||
},
|
||||
{
|
||||
"file": "docs/guidelines/project-structure.md"
|
||||
},
|
||||
{
|
||||
"file": "docs/guidelines/state-management.md"
|
||||
},
|
||||
{
|
||||
"file": "docs/guidelines/testing.md"
|
||||
}
|
||||
],
|
||||
"github.copilot.chat.testGeneration.instructions": [
|
||||
{
|
||||
"file": ".github/copilot-instructions.md"
|
||||
},
|
||||
{
|
||||
"file": ".github/testing-instructions.md"
|
||||
},
|
||||
{
|
||||
"file": "docs/tech-stack.md"
|
||||
},
|
||||
{
|
||||
"file": "docs/guidelines/code-style.md"
|
||||
},
|
||||
{
|
||||
"file": "docs/guidelines/testing.md"
|
||||
}
|
||||
],
|
||||
"github.copilot.chat.reviewSelection.instructions": [
|
||||
{
|
||||
"file": ".github/copilot-instructions.md"
|
||||
},
|
||||
{
|
||||
"file": ".github/review-instructions.md"
|
||||
},
|
||||
{
|
||||
"file": "docs/tech-stack.md"
|
||||
},
|
||||
{
|
||||
"file": "docs/guidelines/code-style.md"
|
||||
},
|
||||
{
|
||||
"file": "docs/guidelines/project-structure.md"
|
||||
},
|
||||
{
|
||||
"file": "docs/guidelines/state-management.md"
|
||||
},
|
||||
{
|
||||
"file": "docs/guidelines/testing.md"
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
@@ -24,6 +24,6 @@ ARG BuildUniqueID
|
||||
LABEL build.uniqueid="${BuildUniqueID:-1}"
|
||||
RUN wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb -q -O /tmp/chrome.deb && apt update && apt install -y /tmp/chrome.deb
|
||||
# ignore exitcode, sonst gibts keinen container
|
||||
RUN npm test || true
|
||||
RUN npm run ci || true
|
||||
ENTRYPOINT [ "/bin/sleep", "60000" ]
|
||||
|
||||
|
||||
4
TASKS.md
4
TASKS.md
@@ -1,4 +0,0 @@
|
||||
- Neue Icon Module (z.B. mit SVG sprites)
|
||||
- Breadcrumb Navigation (Neu)
|
||||
- Remissions Produkt Liste (Refactoring / Neu)
|
||||
- Angular Version (Upgrade)
|
||||
1511
angular.json
1511
angular.json
File diff suppressed because it is too large
Load Diff
@@ -1,25 +0,0 @@
|
||||
# Scan
|
||||
|
||||
This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 12.2.0.
|
||||
|
||||
## Code scaffolding
|
||||
|
||||
Run `ng generate component component-name --project scan` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project scan`.
|
||||
|
||||
> Note: Don't forget to add `--project scan` or else it will be added to the default project in your `angular.json` file.
|
||||
|
||||
## Build
|
||||
|
||||
Run `ng build scan` to build the project. The build artifacts will be stored in the `dist/` directory.
|
||||
|
||||
## Publishing
|
||||
|
||||
After building your library with `ng build scan`, go to the dist folder `cd dist/scan` and run `npm publish`.
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `ng test scan` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||
|
||||
## Further help
|
||||
|
||||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
|
||||
"dest": "../../../dist/adapter/scan",
|
||||
"lib": {
|
||||
"entryFile": "src/public-api.ts"
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"name": "@adapter/scan",
|
||||
"version": "0.0.1",
|
||||
"peerDependencies": {
|
||||
"@angular/common": "^12.2.0",
|
||||
"@angular/core": "^12.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
import { Injectable, isDevMode } from '@angular/core';
|
||||
import { EnvironmentService } from '@core/environment';
|
||||
import { PromptModalData, UiModalService, UiPromptModalComponent } from '@ui/modal';
|
||||
import { Observable } from 'rxjs';
|
||||
import { ScanAdapter } from './scan-adapter';
|
||||
|
||||
@Injectable()
|
||||
export class DevScanAdapter implements ScanAdapter {
|
||||
readonly name = 'Dev';
|
||||
|
||||
constructor(private _modal: UiModalService, private _environmentService: EnvironmentService) {}
|
||||
|
||||
async init(): Promise<boolean> {
|
||||
return Promise.resolve(false);
|
||||
// return new Promise((resolve, reject) => {
|
||||
// resolve(isDevMode());
|
||||
// });
|
||||
}
|
||||
|
||||
scan(): Observable<string> {
|
||||
return new Observable((observer) => {
|
||||
const modalRef = this._modal.open({
|
||||
content: UiPromptModalComponent,
|
||||
title: 'Scannen',
|
||||
data: {
|
||||
message: 'Diese Eingabemaske dient nur zu Entwicklungs und Testzwecken.',
|
||||
placeholder: 'Scan Code',
|
||||
confirmText: 'weiter',
|
||||
cancelText: 'abbrechen',
|
||||
} as PromptModalData,
|
||||
});
|
||||
|
||||
const sub = modalRef.afterClosed$.subscribe((result) => {
|
||||
observer.next(result.data);
|
||||
observer.complete();
|
||||
});
|
||||
|
||||
return () => {
|
||||
modalRef.close();
|
||||
sub.unsubscribe();
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
:host {
|
||||
@apply block relative;
|
||||
}
|
||||
|
||||
.scanner-container {
|
||||
width: 100vw;
|
||||
max-width: 95vw;
|
||||
max-height: calc(95vh - 120px);
|
||||
}
|
||||
|
||||
.close-scanner {
|
||||
@apply block px-6 py-4 bg-white text-brand border-2 border-solid border-brand rounded-full text-lg font-bold mx-auto mt-4;
|
||||
}
|
||||
|
||||
@screen desktop {
|
||||
.scanner-container {
|
||||
max-width: 900px;
|
||||
max-height: 900px;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
<div class="scanner-container" #scanContainer></div>
|
||||
<button class="close-scanner" type="button" (click)="close()">
|
||||
Scan abbrechen
|
||||
</button>
|
||||
@@ -1,100 +0,0 @@
|
||||
import { Component, ChangeDetectionStrategy, ElementRef, ViewChild, NgZone, AfterViewInit, OnDestroy } from '@angular/core';
|
||||
import { UiMessageModalComponent, UiModalService } from '@ui/modal';
|
||||
import { Barcode, BarcodePicker, ScanResult, ScanSettings } from 'scandit-sdk';
|
||||
|
||||
@Component({
|
||||
selector: 'app-scandit-overlay',
|
||||
templateUrl: 'scandit-overlay.component.html',
|
||||
styleUrls: ['scandit-overlay.component.css'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ScanditOverlayComponent implements AfterViewInit, OnDestroy {
|
||||
private _barcodePicker: BarcodePicker;
|
||||
|
||||
private _onScan?: (code: string) => void;
|
||||
|
||||
private _onClose?: () => void;
|
||||
|
||||
@ViewChild('scanContainer', { read: ElementRef, static: true }) scanContainer: ElementRef;
|
||||
|
||||
constructor(private _zone: NgZone, private _modal: UiModalService) {}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
this.createBarcodePicker()
|
||||
.then(() => {
|
||||
this._barcodePicker.on('scan', (scanResult) => {
|
||||
this._zone.run(() => this.handleScanrResult(scanResult));
|
||||
});
|
||||
})
|
||||
.catch((err: Error) => {
|
||||
this._modal
|
||||
.open({
|
||||
content: UiMessageModalComponent,
|
||||
title: 'Zugriff auf Kamera verweigert',
|
||||
data: { message: 'Falls Sie den Zugriff erlauben möchten, können Sie das über die Webseiteinstellung Ihres Browsers.' },
|
||||
})
|
||||
.afterClosed$.subscribe(() => {
|
||||
this._onClose?.();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async createBarcodePicker() {
|
||||
this._barcodePicker = await BarcodePicker.create(this.scanContainer.nativeElement, {
|
||||
playSoundOnScan: true,
|
||||
vibrateOnScan: true,
|
||||
});
|
||||
|
||||
this._barcodePicker.applyScanSettings(this.getScanSettings());
|
||||
}
|
||||
|
||||
getScanSettings(): ScanSettings {
|
||||
return new ScanSettings({
|
||||
blurryRecognition: false,
|
||||
|
||||
enabledSymbologies: [
|
||||
Barcode.Symbology.EAN8,
|
||||
Barcode.Symbology.EAN13,
|
||||
Barcode.Symbology.UPCA,
|
||||
Barcode.Symbology.UPCE,
|
||||
Barcode.Symbology.CODE128,
|
||||
Barcode.Symbology.CODE39,
|
||||
Barcode.Symbology.CODE93,
|
||||
Barcode.Symbology.INTERLEAVED_2_OF_5,
|
||||
Barcode.Symbology.QR,
|
||||
],
|
||||
codeDuplicateFilter: 1000,
|
||||
});
|
||||
}
|
||||
|
||||
onScan(fn: (code: string) => void) {
|
||||
this._onScan = fn;
|
||||
}
|
||||
|
||||
onClose(fn: () => void) {
|
||||
this._onClose = fn;
|
||||
}
|
||||
|
||||
handleScanrResult(scanRestul: ScanResult) {
|
||||
let result: string | undefined;
|
||||
if (scanRestul.barcodes.length) {
|
||||
result = scanRestul.barcodes[0].data;
|
||||
} else if (scanRestul.texts.length) {
|
||||
result = scanRestul.texts[0].value;
|
||||
}
|
||||
|
||||
if (result) {
|
||||
this._onScan?.(result);
|
||||
}
|
||||
}
|
||||
|
||||
close() {
|
||||
this._onClose?.();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this._zone.runOutsideAngular(() => {
|
||||
this._barcodePicker?.destroy(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable, Subscriber } from 'rxjs';
|
||||
import { ScanAdapter } from '../scan-adapter';
|
||||
import { Overlay } from '@angular/cdk/overlay';
|
||||
|
||||
import { configure } from 'scandit-sdk';
|
||||
// import { ScanditModalComponent } from './scandit-modal';
|
||||
import { Config } from '@core/config';
|
||||
import { ComponentPortal } from '@angular/cdk/portal';
|
||||
import { ScanditOverlayComponent } from './scandit-overlay.component';
|
||||
import { EnvironmentService } from '@core/environment';
|
||||
|
||||
@Injectable()
|
||||
export class ScanditScanAdapter implements ScanAdapter {
|
||||
readonly name = 'Scandit';
|
||||
|
||||
constructor(private readonly _config: Config, private _overlay: Overlay, private _environmentService: EnvironmentService) {}
|
||||
|
||||
async init(): Promise<boolean> {
|
||||
if (this._environmentService.isTablet()) {
|
||||
await configure(this._config.get('licence.scandit'), {
|
||||
engineLocation: '/scandit/',
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
scan(): Observable<string> {
|
||||
return new Observable((observer) => {
|
||||
const overlay = this.createOverlay();
|
||||
|
||||
const portal = this.createPortal();
|
||||
|
||||
const ref = overlay.attach(portal);
|
||||
|
||||
const sub = new Subscriber();
|
||||
|
||||
const complete = () => {
|
||||
overlay.detach();
|
||||
ref.destroy();
|
||||
sub.unsubscribe();
|
||||
sub.complete();
|
||||
observer.complete();
|
||||
};
|
||||
|
||||
sub.add(
|
||||
overlay.backdropClick().subscribe(() => {
|
||||
complete();
|
||||
})
|
||||
);
|
||||
|
||||
ref.instance.onScan((code) => {
|
||||
observer.next(code);
|
||||
complete();
|
||||
});
|
||||
|
||||
ref.instance.onClose(() => {
|
||||
complete();
|
||||
});
|
||||
|
||||
return complete;
|
||||
});
|
||||
}
|
||||
|
||||
createOverlay() {
|
||||
const overlay = this._overlay.create({
|
||||
positionStrategy: this._overlay.position().global().centerHorizontally().centerVertically(),
|
||||
hasBackdrop: true,
|
||||
});
|
||||
|
||||
return overlay;
|
||||
}
|
||||
|
||||
createPortal() {
|
||||
const portal = new ComponentPortal(ScanditOverlayComponent);
|
||||
|
||||
return portal;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
/*
|
||||
* Public API Surface of scan
|
||||
*/
|
||||
|
||||
export * from './lib/scandit';
|
||||
export * from './lib/dev.scan-adapter';
|
||||
export * from './lib/native.scan-adapter';
|
||||
export * from './lib/scan-adapter';
|
||||
export * from './lib/scan.module';
|
||||
export * from './lib/scan.service';
|
||||
export * from './lib/tokens';
|
||||
@@ -1,19 +0,0 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../../out-tsc/lib",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"inlineSources": true,
|
||||
"types": [],
|
||||
"lib": [
|
||||
"dom",
|
||||
"es2018"
|
||||
]
|
||||
},
|
||||
"exclude": [
|
||||
"src/test.ts",
|
||||
"**/*.spec.ts"
|
||||
]
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "./tsconfig.lib.json",
|
||||
"compilerOptions": {
|
||||
"declarationMap": false
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"compilationMode": "partial"
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../../out-tsc/spec",
|
||||
"types": [
|
||||
"jasmine"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"**/*.spec.ts",
|
||||
"**/*.d.ts"
|
||||
]
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
# ProductImage
|
||||
|
||||
This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 10.1.2.
|
||||
|
||||
## Code scaffolding
|
||||
|
||||
Run `ng generate component component-name --project product-image` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project product-image`.
|
||||
|
||||
> Note: Don't forget to add `--project product-image` or else it will be added to the default project in your `angular.json` file.
|
||||
|
||||
## Build
|
||||
|
||||
Run `ng build product-image` to build the project. The build artifacts will be stored in the `dist/` directory.
|
||||
|
||||
## Publishing
|
||||
|
||||
After building your library with `ng build product-image`, go to the dist folder `cd dist/product-image` and run `npm publish`.
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `ng test product-image` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||
|
||||
## Further help
|
||||
|
||||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
|
||||
"dest": "../../../dist/cdn/product-image",
|
||||
"lib": {
|
||||
"entryFile": "src/public-api.ts"
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"name": "@cdn/product-image",
|
||||
"version": "0.0.1",
|
||||
"peerDependencies": {
|
||||
"@angular/common": "^10.1.2",
|
||||
"@angular/core": "^10.1.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
/*
|
||||
* Public API Surface of product-image
|
||||
*/
|
||||
|
||||
export * from './lib/product-image.service';
|
||||
export * from './lib/product-image.module';
|
||||
export * from './lib/product-image.pipe';
|
||||
export * from './lib/tokens';
|
||||
@@ -1,24 +0,0 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../../out-tsc/lib",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"inlineSources": true,
|
||||
"types": [],
|
||||
"lib": [
|
||||
"dom",
|
||||
"es2018"
|
||||
]
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"skipTemplateCodegen": true,
|
||||
"strictMetadataEmit": true,
|
||||
"enableResourceInlining": true
|
||||
},
|
||||
"exclude": [
|
||||
"src/test.ts",
|
||||
"**/*.spec.ts"
|
||||
]
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "./tsconfig.lib.json",
|
||||
"compilerOptions": {
|
||||
"declarationMap": false
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"compilationMode": "partial"
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../../out-tsc/spec",
|
||||
"types": [
|
||||
"jasmine"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"**/*.spec.ts",
|
||||
"**/*.d.ts"
|
||||
]
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"extends": "../../../tslint.json",
|
||||
"rules": {
|
||||
"directive-selector": [
|
||||
true,
|
||||
"attribute",
|
||||
"cdn",
|
||||
"camelCase"
|
||||
],
|
||||
"component-selector": [
|
||||
true,
|
||||
"element",
|
||||
"cdn",
|
||||
"kebab-case"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
# Core
|
||||
|
||||
This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 15.0.0.
|
||||
|
||||
## Code scaffolding
|
||||
|
||||
Run `ng generate component component-name --project core` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project core`.
|
||||
|
||||
> Note: Don't forget to add `--project core` or else it will be added to the default project in your `angular.json` file.
|
||||
|
||||
## Build
|
||||
|
||||
Run `ng build core` to build the project. The build artifacts will be stored in the `dist/` directory.
|
||||
|
||||
## Publishing
|
||||
|
||||
After building your library with `ng build core`, go to the dist folder `cd dist/core` and run `npm publish`.
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `ng test core` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||
|
||||
## Further help
|
||||
|
||||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
|
||||
"dest": "../../../dist/core/application",
|
||||
"lib": {
|
||||
"entryFile": "src/public-api.ts"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
// start:ng42.barrel
|
||||
export * from './application.module';
|
||||
export * from './application.service';
|
||||
export * from './defs';
|
||||
export * from './store';
|
||||
// end:ng42.barrel
|
||||
@@ -1,18 +0,0 @@
|
||||
import { createAction, props } from '@ngrx/store';
|
||||
import { ApplicationProcess } from '..';
|
||||
|
||||
const prefix = '[CORE-APPLICATION]';
|
||||
|
||||
export const setTitle = createAction(`${prefix} Set Title`, props<{ title: string }>());
|
||||
|
||||
export const setSection = createAction(`${prefix} Set Section`, props<{ section: 'customer' | 'branch' }>());
|
||||
|
||||
export const addProcess = createAction(`${prefix} Add Process`, props<{ process: ApplicationProcess }>());
|
||||
|
||||
export const removeProcess = createAction(`${prefix} Remove Process`, props<{ processId: number }>());
|
||||
|
||||
export const setActivatedProcess = createAction(`${prefix} Set Activated Process`, props<{ activatedProcessId: number }>());
|
||||
|
||||
export const patchProcess = createAction(`${prefix} Patch Process`, props<{ processId: number; changes: Partial<ApplicationProcess> }>());
|
||||
|
||||
export const patchProcessData = createAction(`${prefix} Patch Process Data`, props<{ processId: number; data: Record<string, any> }>());
|
||||
@@ -1,35 +0,0 @@
|
||||
import { ApplicationState } from './application.state';
|
||||
import { ApplicationProcess } from '../defs';
|
||||
import * as selectors from './application.selectors';
|
||||
|
||||
describe('applicationSelectors', () => {
|
||||
it('should select the processes', () => {
|
||||
const processes: ApplicationProcess[] = [{ id: 1, name: 'Vorgang 1', section: 'customer' }];
|
||||
const state: ApplicationState = {
|
||||
processes,
|
||||
section: 'customer',
|
||||
};
|
||||
expect(selectors.selectProcesses.projector(state)).toEqual(processes);
|
||||
});
|
||||
|
||||
it('should select the section', () => {
|
||||
const state: ApplicationState = {
|
||||
processes: [],
|
||||
section: 'customer',
|
||||
};
|
||||
expect(selectors.selectSection.projector(state)).toEqual('customer');
|
||||
});
|
||||
|
||||
it('should select the activatedProcess', () => {
|
||||
const processes: ApplicationProcess[] = [
|
||||
{ id: 1, name: 'Vorgang 1', section: 'customer', activated: 100 },
|
||||
{ id: 2, name: 'Vorgang 2', section: 'customer', activated: 300 },
|
||||
{ id: 3, name: 'Vorgang 3', section: 'customer', activated: 200 },
|
||||
];
|
||||
const state: ApplicationState = {
|
||||
processes,
|
||||
section: 'customer',
|
||||
};
|
||||
expect(selectors.selectActivatedProcess.projector(state)).toEqual(processes[1]);
|
||||
});
|
||||
});
|
||||
@@ -1,5 +0,0 @@
|
||||
/*
|
||||
* Public API Surface of application
|
||||
*/
|
||||
|
||||
export * from './lib';
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
|
||||
"dest": "../../../dist/core/auth",
|
||||
"lib": {
|
||||
"entryFile": "src/public-api.ts"
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
import { coerceArray, coerceStringArray } from '@angular/cdk/coercion';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Config } from '@core/config';
|
||||
import { isNullOrUndefined } from '@utils/common';
|
||||
import { AuthConfig, OAuthService } from 'angular-oauth2-oidc';
|
||||
import { JwksValidationHandler } from 'angular-oauth2-oidc-jwks';
|
||||
import { asapScheduler, BehaviorSubject } from 'rxjs';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class AuthService {
|
||||
private readonly _initialized = new BehaviorSubject<boolean>(false);
|
||||
get initialized$() {
|
||||
return this._initialized.asObservable();
|
||||
}
|
||||
|
||||
private _authConfig: AuthConfig;
|
||||
|
||||
constructor(private _config: Config, private readonly _oAuthService: OAuthService) {
|
||||
this._oAuthService.events?.subscribe((event) => {
|
||||
if (event.type === 'token_received') {
|
||||
console.log('SSO Token Expiration:', new Date(this._oAuthService.getAccessTokenExpiration()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async init() {
|
||||
if (this._initialized.getValue()) {
|
||||
throw new Error('AuthService is already initialized');
|
||||
}
|
||||
|
||||
this._authConfig = this._config.get('@core/auth');
|
||||
|
||||
this._authConfig.redirectUri = window.location.origin;
|
||||
|
||||
this._authConfig.silentRefreshRedirectUri = window.location.origin + '/silent-refresh.html';
|
||||
this._authConfig.useSilentRefresh = true;
|
||||
|
||||
this._oAuthService.configure(this._authConfig);
|
||||
this._oAuthService.tokenValidationHandler = new JwksValidationHandler();
|
||||
|
||||
this._oAuthService.setupAutomaticSilentRefresh();
|
||||
try {
|
||||
await this._oAuthService.loadDiscoveryDocumentAndTryLogin();
|
||||
} catch (error) {
|
||||
this.login();
|
||||
}
|
||||
|
||||
this._initialized.next(true);
|
||||
}
|
||||
|
||||
isAuthenticated() {
|
||||
return this._oAuthService.hasValidIdToken();
|
||||
}
|
||||
|
||||
getToken() {
|
||||
return this._oAuthService.getAccessToken();
|
||||
}
|
||||
|
||||
getClaims() {
|
||||
const token = this._oAuthService.getAccessToken();
|
||||
return this.parseJwt(token);
|
||||
}
|
||||
|
||||
getClaimByKey(key: string) {
|
||||
const claims = this.getClaims();
|
||||
if (isNullOrUndefined(claims)) {
|
||||
return null;
|
||||
}
|
||||
return claims[key];
|
||||
}
|
||||
|
||||
parseJwt(token: string) {
|
||||
if (isNullOrUndefined(token)) {
|
||||
return null;
|
||||
}
|
||||
const base64Url = token.split('.')[1];
|
||||
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
|
||||
|
||||
const encoded = window.atob(base64);
|
||||
return JSON.parse(encoded);
|
||||
}
|
||||
|
||||
login() {
|
||||
this._oAuthService.initLoginFlow();
|
||||
}
|
||||
|
||||
setKeyCardToken(token: string) {
|
||||
this._oAuthService.customQueryParams = {
|
||||
temp_token: token,
|
||||
};
|
||||
}
|
||||
|
||||
async logout() {
|
||||
await this._oAuthService.revokeTokenAndLogout();
|
||||
// asapScheduler.schedule(() => {
|
||||
// window.location.reload();
|
||||
// }, 250);
|
||||
}
|
||||
|
||||
hasRole(role: string | string[]) {
|
||||
const roles = coerceArray(role);
|
||||
|
||||
const userRoles = this.getClaimByKey('role');
|
||||
|
||||
if (isNullOrUndefined(userRoles)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return roles.every((r) => userRoles.includes(r));
|
||||
}
|
||||
|
||||
async refresh() {
|
||||
try {
|
||||
if (this._authConfig.responseType.includes('code') && this._authConfig.scope.includes('offline_access')) {
|
||||
await this._oAuthService.refreshToken();
|
||||
} else {
|
||||
await this._oAuthService.silentRefresh();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
// start:ng42.barrel
|
||||
export * from './auth.module';
|
||||
export * from './auth.service';
|
||||
// end:ng42.barrel
|
||||
@@ -1,5 +0,0 @@
|
||||
/*
|
||||
* Public API Surface of auth
|
||||
*/
|
||||
|
||||
export * from './lib';
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
|
||||
"dest": "../../../dist/core/breadcrumb",
|
||||
"lib": {
|
||||
"entryFile": "src/public-api.ts"
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
describe('Breadcrumb Actions', () => {});
|
||||
@@ -1,57 +0,0 @@
|
||||
import * as selector from './breadcrumb.selectors';
|
||||
import * as action from './breadcrumb.actions';
|
||||
import { breadcrumbReducer } from './breadcrumb.reducer';
|
||||
|
||||
import { BreadcrumbState, INIT } from './breadcrumb.state';
|
||||
|
||||
describe('Breadcrumb Selectors', () => {
|
||||
let state: BreadcrumbState;
|
||||
|
||||
beforeEach(() => {
|
||||
state = breadcrumbReducer(
|
||||
INIT,
|
||||
action.addBreadcrumb({ breadcrumb: { id: 1, key: 'unit-test-1', path: '', name: 'Unit Test 1', section: 'customer' } })
|
||||
);
|
||||
state = breadcrumbReducer(
|
||||
state,
|
||||
action.addBreadcrumb({
|
||||
breadcrumb: { id: 2, key: 'unit-test-1', path: '', name: 'Unit Test 1', tags: ['details'], section: 'customer' },
|
||||
})
|
||||
);
|
||||
state = breadcrumbReducer(
|
||||
state,
|
||||
action.addBreadcrumb({ breadcrumb: { id: 3, key: 'unit-test-2', path: '', name: 'Unit Test 1', section: 'customer' } })
|
||||
);
|
||||
state = breadcrumbReducer(
|
||||
state,
|
||||
action.addBreadcrumb({ breadcrumb: { id: 4, key: 'unit-test-3', path: '', name: 'Unit Test 1', section: 'customer' } })
|
||||
);
|
||||
state = breadcrumbReducer(
|
||||
state,
|
||||
action.addBreadcrumb({
|
||||
breadcrumb: { id: 5, key: 'unit-test-3', path: '', name: 'Unit Test 1', tags: ['details'], section: 'customer' },
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
describe('selectBreadcrumbsByKey', () => {
|
||||
it('should return all breadcrumbs with the key unit-test-1', () => {
|
||||
const fixture = selector.selectBreadcrumbsByKey.projector(Object.values(state.entities), 'unit-test-1');
|
||||
expect(fixture.length).toBe(2);
|
||||
expect(fixture[0].key).toBe('unit-test-1');
|
||||
expect(fixture[1].key).toBe('unit-test-1');
|
||||
});
|
||||
});
|
||||
|
||||
describe('selectBreadcrumbsByKeyAndTag', () => {
|
||||
it('should return all breadcrumbs with the key unit-test-3 and tag details', () => {
|
||||
const fixture = selector.selectBreadcrumbsByKeyAndTag.projector(Object.values(state.entities), {
|
||||
key: 'unit-test-3',
|
||||
tag: 'details',
|
||||
});
|
||||
expect(fixture.length).toBe(1);
|
||||
expect(fixture[0].key).toBe('unit-test-3');
|
||||
expect(fixture[0].tags).toContain('details');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,7 +0,0 @@
|
||||
/*
|
||||
* Public API Surface of breadcrumb
|
||||
*/
|
||||
|
||||
export * from './lib/breadcrumb.service';
|
||||
export * from './lib/core-breadcrumb.module';
|
||||
export * from './lib/defs';
|
||||
7
apps/core/cache/ng-package.json
vendored
7
apps/core/cache/ng-package.json
vendored
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
|
||||
"dest": "../../../dist/core/cache",
|
||||
"lib": {
|
||||
"entryFile": "src/public-api.ts"
|
||||
}
|
||||
}
|
||||
4
apps/core/cache/src/lib/cache-options.ts
vendored
4
apps/core/cache/src/lib/cache-options.ts
vendored
@@ -1,4 +0,0 @@
|
||||
export interface CacheOptions {
|
||||
ttl?: number;
|
||||
persist?: boolean;
|
||||
}
|
||||
116
apps/core/cache/src/lib/cache.service.ts
vendored
116
apps/core/cache/src/lib/cache.service.ts
vendored
@@ -1,116 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { CacheOptions } from './cache-options';
|
||||
import { Cached } from './cached';
|
||||
import { interval } from 'rxjs';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class CacheService {
|
||||
constructor() {
|
||||
this._registerCleanupTask();
|
||||
}
|
||||
|
||||
_registerCleanupTask() {
|
||||
this.cleanup();
|
||||
interval(1000 * 60).subscribe(() => {
|
||||
this.cleanup();
|
||||
});
|
||||
}
|
||||
|
||||
set<T>(token: Object, data: T, options?: CacheOptions) {
|
||||
const persist = options?.persist;
|
||||
const ttl = options?.ttl;
|
||||
const cached: Cached = {
|
||||
data,
|
||||
};
|
||||
|
||||
if (ttl) {
|
||||
cached.until = Date.now() + ttl;
|
||||
} else {
|
||||
cached.until = Date.now() + 1000 * 60 * 60 * 12;
|
||||
}
|
||||
|
||||
if (persist) {
|
||||
localStorage.setItem(this.getKey(token), this.serialize(cached));
|
||||
} else {
|
||||
sessionStorage.setItem(this.getKey(token), this.serialize(cached));
|
||||
}
|
||||
|
||||
Object.freeze(cached);
|
||||
return cached;
|
||||
}
|
||||
|
||||
get<T = any>(token: Object, from?: 'session' | 'persist'): T {
|
||||
let cached: Cached;
|
||||
|
||||
if (from === 'session') {
|
||||
cached = this.deserialize(sessionStorage.getItem(this.getKey(token)));
|
||||
} else if (from === 'persist') {
|
||||
cached = this.deserialize(localStorage.getItem(this.getKey(token)));
|
||||
} else {
|
||||
cached = this.deserialize(sessionStorage.getItem(this.getKey(token))) || this.deserialize(localStorage.getItem(this.getKey(token)));
|
||||
}
|
||||
|
||||
if (!cached) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (cached.until < Date.now()) {
|
||||
this.delete(token, from);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return cached.data;
|
||||
}
|
||||
|
||||
delete(token: Object, from: 'session' | 'persist' = 'session') {
|
||||
if (from === 'session') {
|
||||
sessionStorage.removeItem(this.getKey(token));
|
||||
} else if (from === 'persist') {
|
||||
localStorage.removeItem(this.getKey(token));
|
||||
}
|
||||
}
|
||||
|
||||
private getKey(token: Object) {
|
||||
const key = `CacheService_` + this.hash(JSON.stringify(token));
|
||||
return key;
|
||||
}
|
||||
|
||||
private hash(data: string): string {
|
||||
let hash = 0;
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
hash = data.charCodeAt(i) + ((hash << 5) - hash);
|
||||
}
|
||||
return hash.toString(16);
|
||||
}
|
||||
|
||||
private serialize(data: Cached): string {
|
||||
return JSON.stringify(data);
|
||||
}
|
||||
|
||||
private deserialize(data: string): Cached {
|
||||
return JSON.parse(data);
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
// get all keys created by this service by looking for the service name and remove the entries
|
||||
// that ttl is expired
|
||||
let localStorageKeys = Object.keys(localStorage).filter((key) => key.startsWith('CacheService_'));
|
||||
let seesionStorageKeys = Object.keys(sessionStorage).filter((key) => key.startsWith('CacheService_'));
|
||||
|
||||
localStorageKeys.forEach((key) => {
|
||||
const cached = this.deserialize(localStorage.getItem(key));
|
||||
if (cached.until < Date.now()) {
|
||||
localStorage.removeItem(key);
|
||||
}
|
||||
});
|
||||
|
||||
seesionStorageKeys.forEach((key) => {
|
||||
const cached = this.deserialize(sessionStorage.getItem(key));
|
||||
if (cached.until < Date.now()) {
|
||||
sessionStorage.removeItem(key);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
4
apps/core/cache/src/lib/cached.ts
vendored
4
apps/core/cache/src/lib/cached.ts
vendored
@@ -1,4 +0,0 @@
|
||||
export interface Cached {
|
||||
until?: number;
|
||||
data?: any;
|
||||
}
|
||||
6
apps/core/cache/src/public-api.ts
vendored
6
apps/core/cache/src/public-api.ts
vendored
@@ -1,6 +0,0 @@
|
||||
/*
|
||||
* Public API Surface of cache
|
||||
*/
|
||||
|
||||
export * from './lib/cache.service';
|
||||
export * from './lib/cache.module';
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
|
||||
"dest": "../../../dist/core/command",
|
||||
"lib": {
|
||||
"entryFile": "src/public-api.ts"
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import { ModuleWithProviders, NgModule, Provider, Type } from '@angular/core';
|
||||
import { ActionHandler } from './action-handler.interface';
|
||||
import { CommandService } from './command.service';
|
||||
import { FEATURE_ACTION_HANDLERS, ROOT_ACTION_HANDLERS } from './tokens';
|
||||
|
||||
export function provideActionHandlers(actionHandlers: Type<ActionHandler>[]): Provider[] {
|
||||
return [CommandService, actionHandlers.map((handler) => ({ provide: FEATURE_ACTION_HANDLERS, useClass: handler, multi: true }))];
|
||||
}
|
||||
|
||||
@NgModule({})
|
||||
export class CoreCommandModule {
|
||||
static forRoot(actionHandlers: Type<ActionHandler>[]): ModuleWithProviders<CoreCommandModule> {
|
||||
return {
|
||||
ngModule: CoreCommandModule,
|
||||
providers: [CommandService, actionHandlers.map((handler) => ({ provide: ROOT_ACTION_HANDLERS, useClass: handler, multi: true }))],
|
||||
};
|
||||
}
|
||||
|
||||
static forChild(actionHandlers: Type<ActionHandler>[]): ModuleWithProviders<CoreCommandModule> {
|
||||
return {
|
||||
ngModule: CoreCommandModule,
|
||||
providers: [CommandService, actionHandlers.map((handler) => ({ provide: FEATURE_ACTION_HANDLERS, useClass: handler, multi: true }))],
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
// start:ng42.barrel
|
||||
export * from './action-handler.interface';
|
||||
export * from './command.module';
|
||||
export * from './command.service';
|
||||
export * from './tokens';
|
||||
// end:ng42.barrel
|
||||
@@ -1,5 +0,0 @@
|
||||
/*
|
||||
* Public API Surface of command
|
||||
*/
|
||||
|
||||
export * from './lib';
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
|
||||
"dest": "../../../dist/core/config",
|
||||
"lib": {
|
||||
"entryFile": "src/public-api.ts"
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
/**
|
||||
* Config loader interface for loading configurations
|
||||
*/
|
||||
export interface ConfigLoader {
|
||||
load(): Promise<any>;
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
// start:ng42.barrel
|
||||
export * from './config-loader';
|
||||
export * from './json.config-loader';
|
||||
// end:ng42.barrel
|
||||
@@ -1,36 +0,0 @@
|
||||
// unit test JsonConfigLoader
|
||||
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
|
||||
import { createServiceFactory, SpectatorService } from '@ngneat/spectator';
|
||||
import { CORE_JSON_CONFIG_LOADER_URL } from '../tokens';
|
||||
import { JsonConfigLoader } from './json.config-loader';
|
||||
|
||||
describe('JsonConfigLoader', () => {
|
||||
let spectator: SpectatorService<JsonConfigLoader>;
|
||||
const createService = createServiceFactory({
|
||||
imports: [HttpClientTestingModule],
|
||||
service: JsonConfigLoader,
|
||||
mocks: [],
|
||||
providers: [{ provide: CORE_JSON_CONFIG_LOADER_URL, useValue: '/assets/config.json' }],
|
||||
});
|
||||
let httpTestingController: HttpTestingController;
|
||||
|
||||
beforeEach(() => {
|
||||
spectator = createService();
|
||||
httpTestingController = spectator.inject(HttpTestingController);
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(spectator.service).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('load', () => {
|
||||
it('should call the provided url', async () => {
|
||||
const reqPromise = spectator.service.load();
|
||||
const req = httpTestingController.expectOne('/assets/config.json');
|
||||
req.flush({ unit: 'test' });
|
||||
const result = await reqPromise;
|
||||
httpTestingController.verify();
|
||||
expect(result).toEqual({ unit: 'test' });
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,13 +0,0 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Inject, Injectable } from '@angular/core';
|
||||
import { ConfigLoader } from './config-loader';
|
||||
import { CORE_JSON_CONFIG_LOADER_URL } from '../tokens';
|
||||
|
||||
@Injectable()
|
||||
export class JsonConfigLoader implements ConfigLoader {
|
||||
constructor(@Inject(CORE_JSON_CONFIG_LOADER_URL) private url: string, private http: HttpClient) {}
|
||||
|
||||
load(): Promise<any> {
|
||||
return this.http.get(this.url).toPromise();
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
import { Type } from '@angular/core';
|
||||
import { ConfigLoader } from './config-loaders';
|
||||
|
||||
export interface ConfigModuleOptions {
|
||||
useConfigLoader: Type<ConfigLoader>;
|
||||
jsonConfigLoaderUrl?: string;
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import { APP_INITIALIZER, ModuleWithProviders, NgModule } from '@angular/core';
|
||||
import { CORE_CONFIG_LOADER } from '@core/config';
|
||||
import { Config } from './config';
|
||||
import { ConfigModuleOptions } from './config-module-options';
|
||||
import { CORE_JSON_CONFIG_LOADER_URL } from './tokens';
|
||||
|
||||
export function _initializeConfigFactory(config: Config) {
|
||||
return () => config.init();
|
||||
}
|
||||
|
||||
@NgModule({})
|
||||
export class ConfigModule {
|
||||
static forRoot(options: ConfigModuleOptions): ModuleWithProviders<ConfigModule> {
|
||||
const configLoaderProvider = {
|
||||
provide: CORE_CONFIG_LOADER,
|
||||
useClass: options.useConfigLoader,
|
||||
};
|
||||
|
||||
return {
|
||||
ngModule: ConfigModule,
|
||||
providers: [
|
||||
Config,
|
||||
configLoaderProvider,
|
||||
options.jsonConfigLoaderUrl ? { provide: CORE_JSON_CONFIG_LOADER_URL, useValue: options.jsonConfigLoaderUrl } : null,
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
import { createServiceFactory, SpectatorService } from '@ngneat/spectator';
|
||||
import { Config } from './config';
|
||||
import { ConfigLoader } from './config-loaders';
|
||||
import { CORE_CONFIG_LOADER } from './tokens';
|
||||
|
||||
class TestConfigLoader implements ConfigLoader {
|
||||
load() {
|
||||
return Promise.resolve({});
|
||||
}
|
||||
}
|
||||
|
||||
// Unit test Config
|
||||
describe('Config', () => {
|
||||
let spectator: SpectatorService<Config>;
|
||||
const createService = createServiceFactory({
|
||||
service: Config,
|
||||
providers: [{ provide: CORE_CONFIG_LOADER, useClass: TestConfigLoader }],
|
||||
});
|
||||
let configLoader: ConfigLoader;
|
||||
|
||||
beforeEach(() => {
|
||||
spectator = createService();
|
||||
configLoader = spectator.inject(CORE_CONFIG_LOADER);
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(spectator.service).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('init()', () => {
|
||||
it('should load config and assigns it to _config', async () => {
|
||||
const config = { unit: 'test' };
|
||||
spyOn(configLoader, 'load').and.returnValue(Promise.resolve(config));
|
||||
await spectator.service.init();
|
||||
expect(spectator.service['_config']).toEqual(config);
|
||||
});
|
||||
});
|
||||
|
||||
describe('get()', () => {
|
||||
it('should return config value', () => {
|
||||
spectator.service['_config'] = { test: 'test' };
|
||||
expect(spectator.service.get('test')).toEqual('test');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,27 +0,0 @@
|
||||
import { Inject, Injectable } from '@angular/core';
|
||||
import { ReplaySubject } from 'rxjs';
|
||||
import { ConfigLoader } from './config-loaders';
|
||||
import { CORE_CONFIG_LOADER } from './tokens';
|
||||
import { pick } from './utils';
|
||||
|
||||
@Injectable()
|
||||
export class Config {
|
||||
private _config: any;
|
||||
|
||||
private readonly _initilized = new ReplaySubject<void>(1);
|
||||
get initialized() {
|
||||
return this._initilized.asObservable();
|
||||
}
|
||||
|
||||
constructor(@Inject(CORE_CONFIG_LOADER) private readonly _configLoader: ConfigLoader) {}
|
||||
|
||||
// load config and assign it to this._config
|
||||
async init() {
|
||||
this._config = await this._configLoader.load();
|
||||
this._initilized.next();
|
||||
}
|
||||
|
||||
get(path: string) {
|
||||
return pick(path, this._config);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
// start:ng42.barrel
|
||||
export * from './config-module-options';
|
||||
export * from './config.module';
|
||||
export * from './config';
|
||||
export * from './tokens';
|
||||
export * from './config-loaders';
|
||||
export * from './utils';
|
||||
// end:ng42.barrel
|
||||
@@ -1,6 +0,0 @@
|
||||
import { InjectionToken } from '@angular/core';
|
||||
import { ConfigLoader } from './config-loaders';
|
||||
|
||||
export const CORE_CONFIG_LOADER = new InjectionToken<ConfigLoader>('core.config.loader');
|
||||
|
||||
export const CORE_JSON_CONFIG_LOADER_URL = new InjectionToken<ConfigLoader>('core.json.config.loader.url');
|
||||
@@ -1,3 +0,0 @@
|
||||
// start:ng42.barrel
|
||||
export * from './pick';
|
||||
// end:ng42.barrel
|
||||
@@ -1,41 +0,0 @@
|
||||
import { pick } from './pick';
|
||||
|
||||
describe('pick', () => {
|
||||
it('should pick properties from the 1st level from the object', () => {
|
||||
const obj = {
|
||||
foo: 'bar',
|
||||
};
|
||||
expect(pick('foo', obj)).toEqual('bar');
|
||||
});
|
||||
|
||||
it('should pick properties from the 2nd level from the object', () => {
|
||||
const obj = {
|
||||
foo: {
|
||||
bar: 'baz',
|
||||
},
|
||||
};
|
||||
expect(pick('foo.bar', obj)).toEqual('baz');
|
||||
});
|
||||
|
||||
it('should pick properties from the 3rd level from the object', () => {
|
||||
const obj = {
|
||||
foo: {
|
||||
bar: {
|
||||
baz: 'qux',
|
||||
},
|
||||
},
|
||||
};
|
||||
expect(pick('foo.bar.baz', obj)).toEqual('qux');
|
||||
});
|
||||
|
||||
it('should throw an error of obj is not an object', () => {
|
||||
expect(() => pick('foo', 'bar')).toThrowError(`bar is not an object`);
|
||||
});
|
||||
|
||||
it('should return undefined if the property is not found', () => {
|
||||
const obj = {
|
||||
foo: 'bar',
|
||||
};
|
||||
expect(pick('bar', obj)).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
@@ -1,33 +0,0 @@
|
||||
/**
|
||||
* Pick a value from an object at a given path.
|
||||
* @param path path of the value to pick
|
||||
* @param obj object to pick from
|
||||
* @returns the value at the path or undefined
|
||||
* @throws if obj is not an object
|
||||
*/
|
||||
export function pick<T = any>(path: string, obj: Object): T {
|
||||
const paths = path.split('.');
|
||||
|
||||
// check if obj is null or undefined
|
||||
if (obj == null) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// check if obj is of type object and not an array
|
||||
// and throw an error if not
|
||||
if (typeof obj !== 'object' || Array.isArray(obj)) {
|
||||
throw new Error(`${obj} is not an object`);
|
||||
}
|
||||
|
||||
let result = obj;
|
||||
|
||||
// loop through the path and pick the value
|
||||
// early exit if the path is empty
|
||||
for (const path of paths) {
|
||||
result = result[path];
|
||||
if (result == null) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
return result as T;
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
/*
|
||||
* Public API Surface of config
|
||||
*/
|
||||
|
||||
export * from './lib';
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
|
||||
"dest": "../../../dist/core/environment",
|
||||
"lib": {
|
||||
"entryFile": "src/public-api.ts"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
/*
|
||||
* Public API Surface of environment
|
||||
*/
|
||||
|
||||
export * from './lib/environment.service';
|
||||
export * from './lib/environment.module';
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
|
||||
"dest": "../../../dist/core/logger",
|
||||
"lib": {
|
||||
"entryFile": "src/public-api.ts"
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
// start:ng42.barrel
|
||||
export * from './console-log.provider';
|
||||
export * from './log-level';
|
||||
export * from './log.provider';
|
||||
export * from './logger.module';
|
||||
export * from './logger.service';
|
||||
export * from './tokens';
|
||||
// end:ng42.barrel
|
||||
@@ -1,5 +0,0 @@
|
||||
/*
|
||||
* Public API Surface of logger
|
||||
*/
|
||||
|
||||
export * from './lib';
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
|
||||
"dest": "../../dist/core",
|
||||
"lib": {
|
||||
"entryFile": "src/public-api.ts"
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"name": "core",
|
||||
"version": "0.0.1",
|
||||
"peerDependencies": {
|
||||
"@angular/common": "^15.0.0",
|
||||
"@angular/core": "^15.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
|
||||
"dest": "../../../dist/core/signalr",
|
||||
"lib": {
|
||||
"entryFile": "src/public-api.ts"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
/*
|
||||
* Public API Surface of signalr
|
||||
*/
|
||||
|
||||
export * from './lib/signalr-hub-options';
|
||||
export * from './lib/signalr.hub';
|
||||
@@ -1,22 +0,0 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { CoreComponent } from './core.component';
|
||||
|
||||
describe('CoreComponent', () => {
|
||||
let component: CoreComponent;
|
||||
let fixture: ComponentFixture<CoreComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [CoreComponent],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(CoreComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -1,12 +0,0 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'lib-core',
|
||||
template: `
|
||||
<p>
|
||||
core works!
|
||||
</p>
|
||||
`,
|
||||
styles: [],
|
||||
})
|
||||
export class CoreComponent {}
|
||||
@@ -1,9 +0,0 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CoreComponent } from './core.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [CoreComponent],
|
||||
imports: [],
|
||||
exports: [CoreComponent],
|
||||
})
|
||||
export class CoreModule {}
|
||||
@@ -1,16 +0,0 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { CoreService } from './core.service';
|
||||
|
||||
describe('CoreService', () => {
|
||||
let service: CoreService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(CoreService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -1,8 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class CoreService {
|
||||
constructor() {}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
/*
|
||||
* Public API Surface of core
|
||||
*/
|
||||
|
||||
export * from './lib/core.service';
|
||||
export * from './lib/core.component';
|
||||
export * from './lib/core.module';
|
||||
@@ -1,14 +0,0 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../out-tsc/lib",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"inlineSources": true,
|
||||
"types": []
|
||||
},
|
||||
"exclude": [
|
||||
"**/*.spec.ts"
|
||||
]
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "./tsconfig.lib.json",
|
||||
"compilerOptions": {
|
||||
"declarationMap": false
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"compilationMode": "partial"
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../out-tsc/spec",
|
||||
"types": [
|
||||
"jasmine"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"**/*.spec.ts",
|
||||
"**/*.d.ts"
|
||||
]
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
# Availability
|
||||
|
||||
This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 10.1.2.
|
||||
|
||||
## Code scaffolding
|
||||
|
||||
Run `ng generate component component-name --project availability` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project availability`.
|
||||
|
||||
> Note: Don't forget to add `--project availability` or else it will be added to the default project in your `angular.json` file.
|
||||
|
||||
## Build
|
||||
|
||||
Run `ng build availability` to build the project. The build artifacts will be stored in the `dist/` directory.
|
||||
|
||||
## Publishing
|
||||
|
||||
After building your library with `ng build availability`, go to the dist folder `cd dist/availability` and run `npm publish`.
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `ng test availability` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||
|
||||
## Further help
|
||||
|
||||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
|
||||
"dest": "../../../dist/domain/availability",
|
||||
"lib": {
|
||||
"entryFile": "src/public-api.ts"
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"name": "@domain/availability",
|
||||
"version": "0.0.1",
|
||||
"peerDependencies": {
|
||||
"@angular/common": "^10.1.2",
|
||||
"@angular/core": "^10.1.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { DomainAvailabilityService } from './availability.service';
|
||||
|
||||
describe('AvailabilityService', () => {
|
||||
let service: DomainAvailabilityService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(DomainAvailabilityService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -1,605 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ItemDTO } from '@swagger/cat';
|
||||
import {
|
||||
AvailabilityDTO,
|
||||
BranchDTO,
|
||||
OLAAvailabilityDTO,
|
||||
StoreCheckoutBranchService,
|
||||
StoreCheckoutSupplierService,
|
||||
SupplierDTO,
|
||||
} from '@swagger/checkout';
|
||||
import { BehaviorSubject, combineLatest, Observable, of } from 'rxjs';
|
||||
import {
|
||||
AvailabilityRequestDTO,
|
||||
AvailabilityService,
|
||||
AvailabilityDTO as SwaggerAvailabilityDTO,
|
||||
AvailabilityType,
|
||||
} from '@swagger/availability';
|
||||
import { AvailabilityDTO as CatAvailabilityDTO } from '@swagger/cat';
|
||||
import { map, shareReplay, switchMap, withLatestFrom, mergeMap, timeout, first } from 'rxjs/operators';
|
||||
import { isArray, memorize } from '@utils/common';
|
||||
import { LogisticianDTO, LogisticianService } from '@swagger/oms';
|
||||
import { ResponseArgsOfIEnumerableOfStockInfoDTO, StockDTO, StockInfoDTO, StockService } from '@swagger/remi';
|
||||
import { PriceDTO } from '@swagger/availability';
|
||||
import { AvailabilityByBranchDTO, ItemData, Ssc } from './defs';
|
||||
import { Availability } from './defs/availability';
|
||||
import { isEmpty } from 'lodash';
|
||||
|
||||
@Injectable()
|
||||
export class DomainAvailabilityService {
|
||||
// Ticket #3378 Keep Result List Items and Details Page SSC in sync
|
||||
sscs$ = new BehaviorSubject<Array<Ssc>>([]);
|
||||
sscsObs$ = this.sscs$.asObservable();
|
||||
|
||||
constructor(
|
||||
private _availabilityService: AvailabilityService,
|
||||
private _logisticanService: LogisticianService,
|
||||
private _stockService: StockService,
|
||||
private _supplierService: StoreCheckoutSupplierService,
|
||||
private _branchService: StoreCheckoutBranchService
|
||||
) {}
|
||||
|
||||
@memorize({ ttl: 10000 })
|
||||
memorizedAvailabilityShippingAvailability(request: Array<AvailabilityRequestDTO>) {
|
||||
return this._availabilityService.AvailabilityShippingAvailability(request).pipe(shareReplay(1));
|
||||
}
|
||||
|
||||
@memorize()
|
||||
getSuppliers(): Observable<SupplierDTO[]> {
|
||||
return this._supplierService.StoreCheckoutSupplierGetSuppliers({}).pipe(
|
||||
map((response) => response.result),
|
||||
shareReplay(1)
|
||||
);
|
||||
}
|
||||
|
||||
@memorize()
|
||||
getTakeAwaySupplier(): Observable<SupplierDTO> {
|
||||
return this._supplierService.StoreCheckoutSupplierGetSuppliers({}).pipe(
|
||||
map(({ result }) => result?.find((supplier) => supplier?.supplierNumber === 'F')),
|
||||
shareReplay(1)
|
||||
);
|
||||
}
|
||||
|
||||
@memorize()
|
||||
getBranches(): Observable<BranchDTO[]> {
|
||||
return this._branchService.StoreCheckoutBranchGetBranches({}).pipe(
|
||||
map((response) => response.result),
|
||||
shareReplay(1)
|
||||
);
|
||||
}
|
||||
|
||||
@memorize()
|
||||
getStockByBranch(branchId: number): Observable<StockDTO> {
|
||||
return this._stockService.StockGetStocksByBranch({ branchId }).pipe(
|
||||
map((response) => response.result),
|
||||
map((result) => result?.find((_) => true)),
|
||||
shareReplay(1)
|
||||
);
|
||||
}
|
||||
|
||||
@memorize()
|
||||
getDefaultStock(): Observable<StockDTO> {
|
||||
return this._stockService.StockCurrentStock().pipe(
|
||||
map((response) => response.result),
|
||||
shareReplay(1)
|
||||
);
|
||||
}
|
||||
|
||||
@memorize()
|
||||
getDefaultBranch(): Observable<BranchDTO> {
|
||||
return this._stockService.StockCurrentBranch().pipe(
|
||||
map((response) => ({
|
||||
id: response.result.id,
|
||||
name: response.result.name,
|
||||
address: response.result.address,
|
||||
branchType: response.result.branchType,
|
||||
branchNumber: response.result.branchNumber,
|
||||
changed: response.result.changed,
|
||||
created: response.result.created,
|
||||
isDefault: response.result.isDefault,
|
||||
isOnline: response.result.isOnline,
|
||||
key: response.result.key,
|
||||
label: response.result.label,
|
||||
pId: response.result.pId,
|
||||
shortName: response.result.shortName,
|
||||
status: response.result.status,
|
||||
version: response.result.version,
|
||||
})),
|
||||
shareReplay(1)
|
||||
);
|
||||
}
|
||||
|
||||
@memorize({})
|
||||
getLogisticians(): Observable<LogisticianDTO> {
|
||||
return this._logisticanService.LogisticianGetLogisticians({}).pipe(
|
||||
map((response) => response.result?.find((l) => l.logisticianNumber === '2470')),
|
||||
shareReplay(1)
|
||||
);
|
||||
}
|
||||
|
||||
getTakeAwayAvailabilityByBranches({
|
||||
branchIds,
|
||||
itemId,
|
||||
price,
|
||||
quantity,
|
||||
}: {
|
||||
branchIds: number[];
|
||||
itemId: number;
|
||||
price: PriceDTO;
|
||||
quantity: number;
|
||||
}): Observable<AvailabilityByBranchDTO[]> {
|
||||
return this._stockService.StockStockRequest({ stockRequest: { branchIds, itemId } }).pipe(
|
||||
map((response) => response.result),
|
||||
withLatestFrom(this.getTakeAwaySupplier()),
|
||||
map(([result, supplier]) => {
|
||||
const availabilities: AvailabilityByBranchDTO[] = result.map((stockInfo) => {
|
||||
return {
|
||||
availableQuantity: stockInfo.availableQuantity,
|
||||
availabilityType: quantity <= stockInfo.inStock ? 1024 : 1, // 1024 (=Available)
|
||||
inStock: stockInfo.inStock,
|
||||
supplierSSC: quantity <= stockInfo.inStock ? '999' : '',
|
||||
supplierSSCText: quantity <= stockInfo.inStock ? 'Filialentnahme' : '',
|
||||
price,
|
||||
supplier: { id: supplier?.id },
|
||||
branchId: stockInfo.branchId,
|
||||
};
|
||||
});
|
||||
return availabilities;
|
||||
}),
|
||||
shareReplay(1)
|
||||
);
|
||||
}
|
||||
|
||||
@memorize({ ttl: 10000 })
|
||||
getTakeAwayAvailability({
|
||||
item,
|
||||
quantity,
|
||||
branch,
|
||||
}: {
|
||||
item: ItemData;
|
||||
quantity: number;
|
||||
branch?: BranchDTO;
|
||||
}): Observable<AvailabilityDTO> {
|
||||
const request = !!branch ? this.getStockByBranch(branch.id) : this.getDefaultStock();
|
||||
return request.pipe(
|
||||
switchMap((s) =>
|
||||
combineLatest([
|
||||
this._stockService.StockInStock({ articleIds: [item.itemId], stockId: s.id }),
|
||||
this.getTakeAwaySupplier(),
|
||||
this.getDefaultBranch(),
|
||||
])
|
||||
),
|
||||
map(([response, supplier, defaultBranch]) => {
|
||||
const price = item?.price;
|
||||
return this._mapToTakeAwayAvailability({ response, supplier, branchId: branch?.id ?? defaultBranch?.id, quantity, price });
|
||||
}),
|
||||
shareReplay(1)
|
||||
);
|
||||
}
|
||||
|
||||
@memorize({ ttl: 10000 })
|
||||
getTakeAwayAvailabilityByBranch({
|
||||
branch,
|
||||
itemId,
|
||||
price,
|
||||
quantity,
|
||||
}: {
|
||||
branch: BranchDTO;
|
||||
itemId: number;
|
||||
price: PriceDTO;
|
||||
quantity: number;
|
||||
}): Observable<AvailabilityDTO> {
|
||||
return combineLatest([
|
||||
this._stockService.StockStockRequest({ stockRequest: { branchIds: [branch.id], itemId } }),
|
||||
this.getTakeAwaySupplier(),
|
||||
]).pipe(
|
||||
map(([response, supplier]) => {
|
||||
return this._mapToTakeAwayAvailability({ response, supplier, branchId: branch.id, quantity, price });
|
||||
}),
|
||||
shareReplay(1)
|
||||
);
|
||||
}
|
||||
|
||||
getTakeAwayAvailabilityByEan({
|
||||
eans,
|
||||
price,
|
||||
quantity,
|
||||
branchId,
|
||||
}: {
|
||||
eans: string[];
|
||||
price: PriceDTO;
|
||||
quantity: number;
|
||||
branchId?: number;
|
||||
}): Observable<AvailabilityDTO> {
|
||||
const request = !!branchId ? this.getStockByBranch(branchId) : this.getDefaultStock();
|
||||
return request.pipe(
|
||||
switchMap((s) => this._stockService.StockInStockByEAN({ eans, stockId: s.id })),
|
||||
withLatestFrom(this.getTakeAwaySupplier(), this.getDefaultBranch()),
|
||||
map(([response, supplier, defaultBranch]) => {
|
||||
return this._mapToTakeAwayAvailability({ response, supplier, branchId: branchId ?? defaultBranch.id, quantity, price });
|
||||
}),
|
||||
shareReplay(1)
|
||||
);
|
||||
}
|
||||
|
||||
getTakeAwayAvailabilitiesByEans({ eans }: { eans: string[] }): Observable<StockInfoDTO[]> {
|
||||
const eansFiltered = Array.from(new Set(eans));
|
||||
return this.getDefaultStock().pipe(
|
||||
switchMap((s) => this._stockService.StockInStockByEAN({ eans: eansFiltered, stockId: s.id })),
|
||||
withLatestFrom(this.getTakeAwaySupplier(), this.getDefaultBranch()),
|
||||
map((response) => response[0].result),
|
||||
shareReplay(1)
|
||||
);
|
||||
}
|
||||
|
||||
@memorize({ ttl: 10000 })
|
||||
getPickUpAvailability({
|
||||
item,
|
||||
branch,
|
||||
quantity,
|
||||
}: {
|
||||
item: ItemData;
|
||||
quantity: number;
|
||||
branch: BranchDTO;
|
||||
}): Observable<Availability<AvailabilityDTO, SwaggerAvailabilityDTO>> {
|
||||
return this._availabilityService
|
||||
.AvailabilityStoreAvailability([
|
||||
{
|
||||
qty: quantity,
|
||||
ean: item?.ean,
|
||||
itemId: item?.itemId ? String(item?.itemId) : null,
|
||||
shopId: branch?.id,
|
||||
price: item?.price,
|
||||
},
|
||||
])
|
||||
.pipe(
|
||||
map((r) => this._mapToPickUpAvailability(r.result)?.find((_) => true)),
|
||||
shareReplay(1)
|
||||
);
|
||||
}
|
||||
|
||||
@memorize({ ttl: 10000 })
|
||||
getDeliveryAvailability({ item, quantity }: { item: ItemData; quantity: number }): Observable<AvailabilityDTO> {
|
||||
return this.memorizedAvailabilityShippingAvailability([
|
||||
{
|
||||
ean: item?.ean,
|
||||
itemId: item?.itemId ? String(item?.itemId) : null,
|
||||
price: item?.price,
|
||||
qty: quantity,
|
||||
},
|
||||
]).pipe(
|
||||
timeout(5000),
|
||||
map((r) => this._mapToShippingAvailability(r.result)?.find((_) => true)),
|
||||
shareReplay(1)
|
||||
);
|
||||
}
|
||||
|
||||
@memorize({ ttl: 10000 })
|
||||
getDigDeliveryAvailability({ item, quantity }: { item: ItemData; quantity: number }): Observable<AvailabilityDTO> {
|
||||
return this.memorizedAvailabilityShippingAvailability([
|
||||
{
|
||||
qty: quantity,
|
||||
ean: item?.ean,
|
||||
itemId: item?.itemId ? String(item?.itemId) : null,
|
||||
price: item?.price,
|
||||
},
|
||||
]).pipe(
|
||||
timeout(5000),
|
||||
map((r) => {
|
||||
const availabilities = r.result;
|
||||
const preferred = availabilities?.find((f) => f.preferred === 1);
|
||||
|
||||
return {
|
||||
availabilityType: preferred?.status,
|
||||
ssc: preferred?.ssc,
|
||||
sscText: preferred?.sscText,
|
||||
supplier: { id: preferred?.supplierId },
|
||||
isPrebooked: preferred?.isPrebooked,
|
||||
estimatedShippingDate: preferred?.requestStatusCode === '32' ? preferred?.altAt : preferred?.at,
|
||||
estimatedDelivery: preferred?.estimatedDelivery,
|
||||
price: preferred?.price,
|
||||
logistician: { id: preferred?.logisticianId },
|
||||
supplierProductNumber: preferred?.supplierProductNumber,
|
||||
supplierInfo: preferred?.requestStatusCode,
|
||||
lastRequest: preferred?.requested,
|
||||
priceMaintained: preferred?.priceMaintained,
|
||||
};
|
||||
}),
|
||||
shareReplay(1)
|
||||
);
|
||||
}
|
||||
|
||||
@memorize({ ttl: 10000 })
|
||||
getB2bDeliveryAvailability({
|
||||
item,
|
||||
quantity,
|
||||
branch,
|
||||
}: {
|
||||
item: ItemData;
|
||||
quantity: number;
|
||||
branch?: BranchDTO;
|
||||
}): Observable<AvailabilityDTO> {
|
||||
const logistician$ = this.getLogisticians();
|
||||
|
||||
const currentBranch$ = this.getDefaultBranch();
|
||||
|
||||
return currentBranch$.pipe(
|
||||
timeout(5000),
|
||||
mergeMap((defaultBranch) =>
|
||||
this.getPickUpAvailability({ item, quantity, branch: branch ?? defaultBranch }).pipe(
|
||||
mergeMap((availability) =>
|
||||
logistician$.pipe(
|
||||
map((logistician) => ({ ...(availability?.length > 0 ? availability[0] : []), logistician: { id: logistician.id } }))
|
||||
)
|
||||
),
|
||||
shareReplay(1)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@memorize({ ttl: 10000 })
|
||||
getDownloadAvailability({ item }: { item: ItemData }): Observable<AvailabilityDTO> {
|
||||
return this.memorizedAvailabilityShippingAvailability([
|
||||
{
|
||||
ean: item?.ean,
|
||||
itemId: item?.itemId ? String(item?.itemId) : null,
|
||||
price: item?.price,
|
||||
qty: 1,
|
||||
},
|
||||
]).pipe(
|
||||
map((r) => {
|
||||
const availabilities = r.result;
|
||||
const preferred = availabilities?.find((f) => f.preferred === 1);
|
||||
|
||||
return {
|
||||
availabilityType: preferred?.status,
|
||||
ssc: preferred?.ssc,
|
||||
sscText: preferred?.sscText,
|
||||
supplier: { id: preferred?.supplierId },
|
||||
isPrebooked: preferred?.isPrebooked,
|
||||
estimatedShippingDate: preferred?.requestStatusCode === '32' ? preferred?.altAt : preferred?.at,
|
||||
price: preferred?.price,
|
||||
supplierProductNumber: preferred?.supplierProductNumber,
|
||||
logistician: { id: preferred?.logisticianId },
|
||||
supplierInfo: preferred?.requestStatusCode,
|
||||
lastRequest: preferred?.requested,
|
||||
priceMaintained: preferred?.priceMaintained,
|
||||
};
|
||||
}),
|
||||
shareReplay(1)
|
||||
);
|
||||
}
|
||||
|
||||
@memorize({ ttl: 10000 })
|
||||
getTakeAwayAvailabilities(items: { id: number; price: PriceDTO }[], branchId: number) {
|
||||
return this._stockService.StockGetStocksByBranch({ branchId }).pipe(
|
||||
map((req) => req.result?.find((_) => true)?.id),
|
||||
switchMap((stockId) =>
|
||||
stockId
|
||||
? this._stockService.StockInStock({ articleIds: items.map((i) => i.id), stockId })
|
||||
: of({ result: [] } as ResponseArgsOfIEnumerableOfStockInfoDTO)
|
||||
),
|
||||
timeout(20000),
|
||||
withLatestFrom(this.getTakeAwaySupplier()),
|
||||
map(([response, supplier]) => {
|
||||
return response.result?.map((stockInfo) =>
|
||||
this._mapToTakeAwayAvailabilities({
|
||||
stockInfo,
|
||||
supplier,
|
||||
quantity: 1,
|
||||
price: items?.find((i) => i.id === stockInfo.itemId)?.price,
|
||||
})
|
||||
);
|
||||
}),
|
||||
shareReplay(1)
|
||||
);
|
||||
}
|
||||
|
||||
@memorize({ ttl: 10000 })
|
||||
getPickUpAvailabilities(payload: AvailabilityRequestDTO[], preferred?: boolean) {
|
||||
return this._availabilityService.AvailabilityStoreAvailability(payload).pipe(
|
||||
timeout(20000),
|
||||
map((response) => (preferred ? this._mapToPickUpAvailability(response.result) : response.result))
|
||||
);
|
||||
}
|
||||
|
||||
@memorize({ ttl: 10000 })
|
||||
getDeliveryAvailabilities(payload: AvailabilityRequestDTO[]) {
|
||||
return this.memorizedAvailabilityShippingAvailability(payload).pipe(
|
||||
timeout(20000),
|
||||
map((response) => this._mapToShippingAvailability(response.result))
|
||||
);
|
||||
}
|
||||
|
||||
@memorize({ ttl: 10000 })
|
||||
getDigDeliveryAvailabilities(payload: AvailabilityRequestDTO[]) {
|
||||
return this.memorizedAvailabilityShippingAvailability(payload).pipe(
|
||||
timeout(20000),
|
||||
map((response) => this._mapToShippingAvailability(response.result))
|
||||
);
|
||||
}
|
||||
|
||||
@memorize({ ttl: 10000 })
|
||||
getB2bDeliveryAvailabilities(payload: AvailabilityRequestDTO[]) {
|
||||
const logistician$ = this.getLogisticians();
|
||||
|
||||
return this.getPickUpAvailabilities(payload, true).pipe(
|
||||
timeout(20000),
|
||||
switchMap((availability) =>
|
||||
logistician$.pipe(map((logistician) => ({ availability: [...availability], logistician: { id: logistician.id } })))
|
||||
),
|
||||
shareReplay(1)
|
||||
);
|
||||
}
|
||||
|
||||
getPriceForAvailability(
|
||||
purchasingOption: string,
|
||||
catalogAvailability: CatAvailabilityDTO | AvailabilityDTO,
|
||||
availability: AvailabilityDTO
|
||||
): PriceDTO {
|
||||
switch (purchasingOption) {
|
||||
case 'take-away':
|
||||
return availability?.price || catalogAvailability?.price;
|
||||
case 'delivery':
|
||||
case 'dig-delivery':
|
||||
if (catalogAvailability?.price?.value?.value < availability?.price?.value?.value) {
|
||||
return catalogAvailability?.price;
|
||||
}
|
||||
return availability?.price || catalogAvailability?.price;
|
||||
}
|
||||
return availability?.price;
|
||||
}
|
||||
|
||||
isAvailable({ availability }: { availability: AvailabilityDTO }) {
|
||||
if (availability?.supplier?.id === 16 && availability?.inStock == 0) {
|
||||
return false;
|
||||
}
|
||||
return [2, 32, 256, 1024, 2048, 4096].some((code) => availability?.availabilityType === code);
|
||||
}
|
||||
|
||||
private _mapToTakeAwayAvailability({
|
||||
response,
|
||||
supplier,
|
||||
branchId,
|
||||
quantity,
|
||||
price,
|
||||
}: {
|
||||
response: ResponseArgsOfIEnumerableOfStockInfoDTO;
|
||||
supplier: SupplierDTO;
|
||||
branchId: number;
|
||||
quantity: number;
|
||||
price: PriceDTO;
|
||||
}): AvailabilityDTO {
|
||||
const stockInfo = response.result?.find((si) => si.branchId === branchId);
|
||||
const inStock = stockInfo?.inStock ?? 0;
|
||||
const availability: AvailabilityDTO = {
|
||||
availabilityType: quantity <= inStock ? 1024 : 1, // 1024 (=Available)
|
||||
inStock: inStock,
|
||||
supplierSSC: quantity <= inStock ? '999' : '',
|
||||
supplierSSCText: quantity <= inStock ? 'Filialentnahme' : '',
|
||||
price: stockInfo?.retailPrice ?? price, // #4553 Es soll nun immer der retailPrice aus der InStock Abfrage verwendet werden, egal ob "price" empty ist oder nicht
|
||||
supplier: { id: supplier?.id },
|
||||
// TODO: Change after API Update
|
||||
// LH: 2021-03-09 preis Property hat nun ein Fallback auf retailPrice
|
||||
// retailPrice: (stockInfo as any)?.retailPrice,
|
||||
};
|
||||
return availability;
|
||||
}
|
||||
|
||||
private _mapToTakeAwayAvailabilities({
|
||||
stockInfo,
|
||||
quantity,
|
||||
price,
|
||||
supplier,
|
||||
}: {
|
||||
stockInfo: StockInfoDTO;
|
||||
quantity: number;
|
||||
price: PriceDTO;
|
||||
supplier: SupplierDTO;
|
||||
}) {
|
||||
const inStock = stockInfo?.inStock ?? 0;
|
||||
|
||||
const availability = {
|
||||
itemId: stockInfo.itemId,
|
||||
availabilityType: quantity <= inStock ? (1024 as AvailabilityType) : (1 as AvailabilityType), // 1024 (=Available)
|
||||
inStock: inStock,
|
||||
supplierSSC: quantity <= inStock ? '999' : '',
|
||||
supplierSSCText: quantity <= inStock ? 'Filialentnahme' : '',
|
||||
price,
|
||||
supplier: { id: supplier?.id },
|
||||
};
|
||||
return availability;
|
||||
}
|
||||
|
||||
private _mapToPickUpAvailability(availabilities: SwaggerAvailabilityDTO[]): Availability<AvailabilityDTO, SwaggerAvailabilityDTO>[] {
|
||||
if (isArray(availabilities)) {
|
||||
const preferred = availabilities.filter((f) => f.preferred === 1);
|
||||
const totalAvailable = availabilities.reduce((sum, av) => sum + (av?.qty || 0), 0);
|
||||
|
||||
return preferred.map((p) => {
|
||||
return [
|
||||
{
|
||||
orderDeadline: p?.orderDeadline,
|
||||
availabilityType: p?.status,
|
||||
ssc: p?.ssc,
|
||||
sscText: p?.sscText,
|
||||
supplier: { id: p?.supplierId },
|
||||
isPrebooked: p?.isPrebooked,
|
||||
estimatedShippingDate: p?.requestStatusCode === '32' ? p?.altAt : p?.at,
|
||||
price: p?.price,
|
||||
inStock: totalAvailable,
|
||||
supplierProductNumber: p?.supplierProductNumber,
|
||||
supplierInfo: p?.requestStatusCode,
|
||||
lastRequest: p?.requested,
|
||||
itemId: p.itemId,
|
||||
priceMaintained: p.priceMaintained,
|
||||
},
|
||||
p,
|
||||
];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private _mapToShippingAvailability(availabilities: SwaggerAvailabilityDTO[]): AvailabilityDTO[] {
|
||||
const preferred = availabilities.filter((f) => f.preferred === 1);
|
||||
return preferred.map((p) => {
|
||||
return {
|
||||
availabilityType: p?.status,
|
||||
ssc: p?.ssc,
|
||||
sscText: p?.sscText,
|
||||
isPrebooked: p?.isPrebooked,
|
||||
estimatedShippingDate: p?.requestStatusCode === '32' ? p?.altAt : p?.at,
|
||||
estimatedDelivery: p?.estimatedDelivery,
|
||||
price: p?.price,
|
||||
supplierProductNumber: p?.supplierProductNumber,
|
||||
supplierInfo: p?.requestStatusCode,
|
||||
lastRequest: p?.requested,
|
||||
itemId: p.itemId,
|
||||
priceMaintained: p.priceMaintained,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
getInStockByEan(params: { eans: string[]; branchId?: number }): Observable<Record<string, StockInfoDTO>> {
|
||||
let branchId$ = of(params.branchId);
|
||||
|
||||
if (!params.branchId) {
|
||||
branchId$ = this.getDefaultBranch().pipe(
|
||||
first(),
|
||||
map((b) => b.id)
|
||||
);
|
||||
}
|
||||
|
||||
const stock$ = branchId$.pipe(
|
||||
mergeMap((branchId) => this._stockService.StockGetStocksByBranch({ branchId }).pipe(map((response) => response.result?.[0])))
|
||||
);
|
||||
|
||||
return stock$.pipe(
|
||||
mergeMap((stock) =>
|
||||
this._stockService.StockInStockByEAN({ eans: params.eans, stockId: stock.id }).pipe(
|
||||
map((response) => {
|
||||
const result = response.result ?? [];
|
||||
|
||||
for (const stockInfo of result) {
|
||||
stockInfo.ean = stockInfo.ean;
|
||||
}
|
||||
|
||||
return result.reduce<Record<string, StockInfoDTO>>((acc, stockInfo) => {
|
||||
acc[stockInfo.ean] = stockInfo;
|
||||
return acc;
|
||||
}, {});
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
getInStock({ itemIds, branchId }: { itemIds: number[]; branchId: number }): Observable<StockInfoDTO[]> {
|
||||
return this.getStockByBranch(branchId).pipe(
|
||||
mergeMap((stock) =>
|
||||
this._stockService.StockInStock({ articleIds: itemIds, stockId: stock.id }).pipe(map((response) => response.result))
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
import { AvailabilityDTO } from '@swagger/checkout';
|
||||
|
||||
export interface AvailabilityByBranchDTO extends AvailabilityDTO {
|
||||
availableQuantity?: number;
|
||||
stockId?: number;
|
||||
branchId: number;
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
import { PriceDTO } from '@swagger/availability';
|
||||
|
||||
export interface ItemData {
|
||||
ean: string;
|
||||
itemId: number;
|
||||
price: PriceDTO;
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { StockInfoDTO } from '@swagger/remi';
|
||||
import { groupBy, isEqual, uniqWith } from 'lodash';
|
||||
import { BehaviorSubject, combineLatest, Observable, Subject } from 'rxjs';
|
||||
import { buffer, debounceTime, distinctUntilChanged } from 'rxjs/operators';
|
||||
import { DomainAvailabilityService } from './availability.service';
|
||||
|
||||
export type ItemBranch = { itemId: number; branchId: number };
|
||||
export type InStock = ItemBranch & { inStock: number; fetching: boolean };
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class DomainInStockService {
|
||||
private _inStockQueue = new Subject<ItemBranch>();
|
||||
private _inStockMap = new BehaviorSubject<Record<string, number>>({});
|
||||
private _inStockFetchingMap = new BehaviorSubject<Record<string, boolean>>({});
|
||||
|
||||
constructor(private _availability: DomainAvailabilityService) {
|
||||
const queueBufferTrigger = this._createQueueBufferTrigger();
|
||||
|
||||
this._inStockQueue.pipe(buffer(queueBufferTrigger)).subscribe(this._handleStockDataToFetch);
|
||||
}
|
||||
|
||||
private _createQueueBufferTrigger() {
|
||||
return this._inStockQueue.pipe(debounceTime(1000));
|
||||
}
|
||||
|
||||
private _handleStockDataToFetch = (itemBranchData: ItemBranch[]) => {
|
||||
const unique = uniqWith(itemBranchData, isEqual);
|
||||
if (unique?.length > 0) {
|
||||
this._fetchStockData(unique);
|
||||
}
|
||||
};
|
||||
|
||||
getKey({ itemId, branchId }: ItemBranch) {
|
||||
return `${itemId}_${branchId}`;
|
||||
}
|
||||
|
||||
getInStock$({ itemId, branchId }: ItemBranch): Observable<InStock> {
|
||||
return new Observable<InStock>((obs) => {
|
||||
if (!(itemId && branchId)) {
|
||||
obs.error(new Error(`ItemId: ${itemId} or BranchId: ${branchId} missing`));
|
||||
return;
|
||||
}
|
||||
|
||||
const key = this.getKey({ itemId, branchId });
|
||||
this._addToInStockQueue({ itemId, branchId });
|
||||
|
||||
let _previousValue: InStock;
|
||||
|
||||
const sub = combineLatest([this._inStockMap, this._inStockFetchingMap])
|
||||
.pipe(distinctUntilChanged(isEqual))
|
||||
.subscribe(([inStockMap, inStockFetchingMap]) => {
|
||||
const inStock: InStock = {
|
||||
itemId,
|
||||
branchId,
|
||||
inStock: inStockMap[key],
|
||||
fetching: inStockFetchingMap[key] ?? false,
|
||||
};
|
||||
|
||||
if (!isEqual(inStock, _previousValue)) {
|
||||
obs.next(inStock);
|
||||
}
|
||||
|
||||
_previousValue = inStock;
|
||||
});
|
||||
return () => {
|
||||
sub.unsubscribe();
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private _addToInStockQueue({ itemId, branchId }: ItemBranch): void {
|
||||
this._inStockQueue.next({ itemId, branchId });
|
||||
this._setInStockFetching({ itemId, branchId }, true);
|
||||
}
|
||||
|
||||
private _setInStockFetching({ itemId, branchId }: ItemBranch, value: boolean) {
|
||||
const key = this.getKey({ itemId, branchId });
|
||||
const current = this._inStockFetchingMap.getValue();
|
||||
this._inStockFetchingMap.next({ ...current, [key]: value });
|
||||
}
|
||||
|
||||
private _setInStock({ itemId, branchId }: ItemBranch, inStock: number) {
|
||||
const key = this.getKey({ itemId, branchId });
|
||||
const current = this._inStockMap.getValue();
|
||||
this._inStockMap.next({ ...current, [key]: inStock });
|
||||
}
|
||||
|
||||
private _fetchStockData(itemBranchData: ItemBranch[]) {
|
||||
const grouped = groupBy(itemBranchData, 'branchId');
|
||||
Object.keys(grouped).forEach((key) => {
|
||||
const branchId = Number(key);
|
||||
const itemIds = itemBranchData.filter((itemBranch) => itemBranch.branchId === branchId).map((item) => item.itemId);
|
||||
this._availability
|
||||
.getInStock({ itemIds, branchId })
|
||||
.subscribe(this._fetchStockDataResponse({ itemIds, branchId }), this._fetchStockDataError({ itemIds, branchId }));
|
||||
});
|
||||
}
|
||||
|
||||
private _fetchStockDataResponse = ({ itemIds, branchId }: { itemIds: number[]; branchId: number }) => (stockInfos: StockInfoDTO[]) => {
|
||||
itemIds.forEach((itemId) => {
|
||||
const stockInfo = stockInfos.find((stockInfo) => stockInfo.itemId === itemId && stockInfo.branchId === branchId);
|
||||
let inStock = 0;
|
||||
|
||||
if (stockInfo?.inStock) {
|
||||
inStock = stockInfo.inStock;
|
||||
}
|
||||
|
||||
this._setInStockFetching({ itemId, branchId }, false);
|
||||
this._setInStock({ itemId, branchId }, inStock);
|
||||
});
|
||||
};
|
||||
|
||||
private _fetchStockDataError = ({ itemIds, branchId }: { itemIds: number[]; branchId: number }) => (error: Error) => {
|
||||
itemIds.forEach((itemId) => {
|
||||
this._setInStockFetching({ itemId, branchId }, false);
|
||||
this._setInStock({ itemId, branchId }, 0);
|
||||
});
|
||||
console.error('DomainInStockService._fetchStockData()', error);
|
||||
};
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
/*
|
||||
* Public API Surface of availability
|
||||
*/
|
||||
|
||||
export * from './lib/availability.service';
|
||||
export * from './lib/in-stock.service';
|
||||
export * from './lib/availability.module';
|
||||
export * from './lib/defs';
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user