From f0cc76f1805dc6b9442413d058f578c22b24df19 Mon Sep 17 00:00:00 2001 From: Nino Righi Date: Mon, 28 Mar 2022 10:12:12 +0000 Subject: [PATCH] Merged PR 1134: Merge ISA 2.0 into Develop Merge ISA 2.0 into Develop Related work items: #1098, #2592, #2630, #2633, #2635, #2639, #2706, #2707, #2813, #2818, #2825, #2843, #2846, #2847, #2848, #2851, #2852, #2853, #2897, #2900 --- .gitignore | 1 + .vscode/extensions.json | 3 +- Dockerfile | 20 +- angular.json | 550 +- apps/adapter/scan/README.md | 25 + apps/adapter/scan/karma.conf.js | 41 + apps/adapter/scan/ng-package.json | 7 + apps/adapter/scan/package.json | 11 + apps/adapter/scan/src/lib/dev.scan-adapter.ts | 46 + .../scan/src/lib/native.scan-adapter.ts | 30 + apps/adapter/scan/src/lib/scan-adapter.ts | 11 + apps/adapter/scan/src/lib/scan.module.ts | 14 + apps/adapter/scan/src/lib/scan.service.ts | 26 + apps/adapter/scan/src/lib/tokens.ts | 4 + apps/adapter/scan/src/public-api.ts | 6 + apps/adapter/scan/src/test.ts | 25 + apps/adapter/scan/tsconfig.lib.json | 20 + apps/adapter/scan/tsconfig.lib.prod.json | 10 + apps/adapter/scan/tsconfig.spec.json | 17 + .../src/lib/product-image.service.ts | 5 +- apps/cdn/product-image/src/test.ts | 4 +- apps/core/application/karma.conf.js | 21 +- .../application/src/lib/application.module.ts | 9 +- .../src/lib/application.service.spec.ts | 232 +- .../src/lib/application.service.ts | 112 +- .../src/lib/defs/application-process.ts | 8 +- apps/core/application/src/lib/index.ts | 1 - .../application/src/lib/process.service.ts | 8 - .../src/lib/store/application.actions.ts | 16 +- .../src/lib/store/application.reducer.spec.ts | 200 + .../src/lib/store/application.reducer.ts | 39 +- .../lib/store/application.selectors.spec.ts | 32 + .../src/lib/store/application.selectors.ts | 11 + .../src/lib/store/application.state.ts | 4 + apps/core/application/src/test.ts | 4 +- apps/core/auth/README.md | 25 + apps/core/auth/karma.conf.js | 43 + apps/core/auth/ng-package.json | 7 + apps/core/auth/package.json | 11 + apps/core/auth/src/lib/auth.module.ts | 17 + apps/core/auth/src/lib/auth.service.spec.ts | 151 + apps/core/auth/src/lib/auth.service.ts | 84 + apps/core/auth/src/lib/index.ts | 4 + apps/core/auth/src/public-api.ts | 5 + apps/core/auth/src/test.ts | 25 + apps/core/auth/tsconfig.lib.json | 20 + apps/core/auth/tsconfig.lib.prod.json | 10 + apps/core/auth/tsconfig.spec.json | 17 + .../breadcrumb/src/lib/breadcrumb.service.ts | 25 +- .../src/lib/defs/breadcrumb.model.ts | 2 +- .../src/lib/store/breadcrumb.effect.ts | 4 +- apps/core/breadcrumb/src/test.ts | 4 +- apps/core/cache/src/test.ts | 4 +- apps/core/command/src/test.ts | 4 +- apps/core/config/README.md | 25 + apps/core/config/karma.conf.js | 43 + apps/core/config/ng-package.json | 7 + apps/core/config/package.json | 11 + .../src/lib/config-loaders/config-loader.ts | 8 + .../config/src/lib/config-loaders/index.ts | 4 + .../config-loaders/json.config-loader.spec.ts | 36 + .../lib/config-loaders/json.config-loader.ts | 13 + .../config/src/lib/config-module-options.ts | 7 + apps/core/config/src/lib/config.module.ts | 28 + apps/core/config/src/lib/config.spec.ts | 45 + apps/core/config/src/lib/config.ts | 27 + apps/core/config/src/lib/index.ts | 8 + apps/core/config/src/lib/tokens.ts | 6 + apps/core/config/src/lib/utils/index.ts | 3 + apps/core/config/src/lib/utils/pick.spec.ts | 41 + apps/core/config/src/lib/utils/pick.ts | 33 + apps/core/config/src/public-api.ts | 5 + apps/core/config/src/test.ts | 25 + apps/core/config/tsconfig.lib.json | 20 + apps/core/config/tsconfig.lib.prod.json | 10 + apps/core/config/tsconfig.spec.json | 17 + apps/core/environment/src/test.ts | 4 +- apps/core/logger/README.md | 25 + apps/core/logger/karma.conf.js | 43 + apps/core/logger/ng-package.json | 7 + apps/core/logger/package.json | 11 + .../src/lib/console-log.provider.spec.ts | 51 + .../logger/src/lib/console-log.provider.ts | 25 + apps/core/logger/src/lib/index.ts | 8 + apps/core/logger/src/lib/log-level.ts | 14 + apps/core/logger/src/lib/log.provider.ts | 6 + apps/core/logger/src/lib/logger.module.ts | 31 + .../logger/src/lib/logger.service.spec.ts | 140 + apps/core/logger/src/lib/logger.service.ts | 39 + apps/core/logger/src/lib/tokens.ts | 6 + apps/core/logger/src/public-api.ts | 5 + apps/core/logger/src/test.ts | 25 + apps/core/logger/tsconfig.lib.json | 20 + apps/core/logger/tsconfig.lib.prod.json | 10 + apps/core/logger/tsconfig.spec.json | 17 + apps/core/signalr/src/test.ts | 4 +- .../src/lib/availability.service.ts | 20 +- apps/domain/availability/src/test.ts | 4 +- apps/domain/cart/src/test.ts | 4 +- apps/domain/catalog/src/test.ts | 4 +- .../checkout/src/lib/checkout.service.ts | 16 +- apps/domain/checkout/src/test.ts | 4 +- apps/domain/checkout/tsconfig.lib.json | 1 + apps/domain/checkout/tsconfig.lib.prod.json | 3 + apps/domain/crm/src/test.ts | 4 +- apps/domain/crm/tsconfig.lib.json | 1 + apps/domain/crm/tsconfig.lib.prod.json | 3 + apps/domain/defs/src/test.ts | 4 +- apps/domain/isa/README.md | 25 + apps/domain/isa/karma.conf.js | 41 + apps/domain/isa/ng-package.json | 7 + apps/domain/isa/package.json | 11 + apps/domain/isa/src/lib/dashboard.service.ts | 11 + apps/domain/isa/src/lib/defs/index.ts | 5 + apps/domain/isa/src/lib/defs/kpi-feed-item.ts | 5 + apps/domain/isa/src/lib/defs/kpi-feed.ts | 7 + apps/domain/isa/src/lib/defs/products-feed.ts | 7 + apps/domain/isa/src/lib/domain-isa.module.ts | 12 + apps/domain/isa/src/lib/index.ts | 5 + apps/domain/isa/src/public-api.ts | 5 + apps/domain/isa/src/test.ts | 25 + apps/domain/isa/tsconfig.lib.json | 20 + apps/domain/isa/tsconfig.lib.prod.json | 10 + apps/domain/isa/tsconfig.spec.json | 17 + apps/domain/oms/src/test.ts | 4 +- apps/domain/printer/src/test.ts | 4 +- apps/domain/remission/README.md | 25 + apps/domain/remission/karma.conf.js | 43 + apps/domain/remission/ng-package.json | 7 + apps/domain/remission/package.json | 11 + apps/domain/remission/src/lib/defs/index.ts | 1 + .../src/lib/defs/remission-list-item.ts | 22 + apps/domain/remission/src/lib/index.ts | 3 + .../remission/src/lib/mappings/index.ts | 2 + .../mappings/remission-list-item.mapping.ts | 42 + .../lib/mappings/return-item-dto.mapping.ts | 36 + .../remission/src/lib/remission.module.ts | 17 + .../src/lib/remission.service.spec.ts | 23 + .../remission/src/lib/remission.service.ts | 494 + apps/domain/remission/src/public-api.ts | 5 + apps/domain/remission/src/test.ts | 25 + apps/domain/remission/tsconfig.lib.json | 20 + apps/domain/remission/tsconfig.lib.prod.json | 10 + apps/domain/remission/tsconfig.spec.json | 17 + .../src/lib/task-calendar.service.ts | 7 +- apps/domain/task-calendar/src/test.ts | 4 +- apps/hub/notifications/src/test.ts | 4 +- apps/isa-app/.browserslistrc | 17 + apps/isa-app/karma.conf.js | 44 + apps/isa-app/ngsw-config.json | 32 + apps/isa-app/src/app/app-domain.module.ts | 19 + apps/isa-app/src/app/app-routing.module.ts | 113 + apps/isa-app/src/app/app-store.module.ts | 34 + apps/isa-app/src/app/app-swagger.module.ts | 32 + apps/isa-app/src/app/app.component.html | 1 + apps/isa-app/src/app/app.component.scss | 3 + apps/isa-app/src/app/app.component.spec.ts | 89 + apps/isa-app/src/app/app.component.ts | 46 + apps/isa-app/src/app/app.module.ts | 105 + ...can-activate-cart-with-process-id.guard.ts | 35 + .../src/app/guards/can-activate-cart.guard.ts | 27 + ...activate-customer-with-process-id.guard.ts | 35 + .../app/guards/can-activate-customer.guard.ts | 27 + .../app/guards/can-activate-goods-in.guard.ts | 24 + .../guards/can-activate-goods-out.guard.ts | 24 + ...-activate-product-with-process-id.guard.ts | 35 + .../app/guards/can-activate-product.guard.ts | 27 + .../guards/can-activate-remission.guard.ts | 30 + .../can-activate-task-calendar.guard.ts | 24 + apps/isa-app/src/app/guards/index.ts | 12 + .../src/app/guards/init-store.guard.ts | 15 + .../src/app/guards/is-authenticated.guard.ts | 64 + .../http-error.interceptor.spec.ts | 60 + .../interceptors/http-error.interceptor.ts | 26 + apps/isa-app/src/app/interceptors/index.ts | 3 + apps/isa-app/src/app/providers/index.ts | 3 + .../src/app/providers/isa.error-handler.ts | 12 + .../src/app/providers/isa.log-provider.ts | 22 + apps/isa-app/src/app/resolvers/index.ts | 4 + .../src/app/resolvers/process-id.resolver.ts | 12 + .../src/app/resolvers/section.resolver.ts | 27 + apps/isa-app/src/app/shell/index.ts | 4 + .../src/app/shell/shell.component.html | 70 + .../src/app/shell/shell.component.scss | 60 + .../src/app/shell/shell.component.spec.ts | 370 + apps/isa-app/src/app/shell/shell.component.ts | 135 + apps/isa-app/src/app/shell/shell.module.ts | 19 + .../src/app/store/root-state.service.ts | 95 + apps/isa-app/src/app/store/root.reducer.ts | 4 + apps/isa-app/src/app/store/root.state.ts | 3 + apps/isa-app/src/assets/.gitkeep | 0 apps/isa-app/src/assets/icons.svg | 215 + .../isa-app/src/assets/icons/icon-144x144.png | Bin 0 -> 3807 bytes .../isa-app/src/assets/icons/icon-192x192.png | Bin 0 -> 4708 bytes .../isa-app/src/assets/icons/icon-512x512.png | Bin 0 -> 13583 bytes apps/isa-app/src/assets/icons/icon-72x72.png | Bin 0 -> 1740 bytes apps/isa-app/src/assets/icons/icon-96x96.png | Bin 0 -> 2424 bytes .../src/assets/images/Hugendubel_Logo.png | Bin 0 -> 4084 bytes apps/isa-app/src/assets/images/Icon_AU.svg | 11 + apps/isa-app/src/assets/images/Icon_DL.svg | 11 + apps/isa-app/src/assets/images/Icon_EB.svg | 11 + apps/isa-app/src/assets/images/Icon_GEB.svg | 11 + apps/isa-app/src/assets/images/Icon_HC.svg | 11 + apps/isa-app/src/assets/images/Icon_KA.svg | 11 + apps/isa-app/src/assets/images/Icon_KT.svg | 11 + apps/isa-app/src/assets/images/Icon_MA.svg | 11 + apps/isa-app/src/assets/images/Icon_NB.svg | 11 + apps/isa-app/src/assets/images/Icon_SW.svg | 11 + apps/isa-app/src/assets/images/Icon_TB.svg | 11 + apps/isa-app/src/assets/images/Icon_VI.svg | 11 + apps/isa-app/src/assets/images/Icon_ZS.svg | 11 + apps/isa-app/src/assets/images/OF_Icon_AU.svg | 11 + apps/isa-app/src/assets/images/OF_Icon_DL.svg | 11 + apps/isa-app/src/assets/images/OF_Icon_EB.svg | 11 + apps/isa-app/src/assets/images/OF_Icon_HC.svg | 11 + apps/isa-app/src/assets/images/OF_Icon_KA.svg | 11 + apps/isa-app/src/assets/images/OF_Icon_MA.svg | 11 + apps/isa-app/src/assets/images/OF_Icon_NB.svg | 11 + apps/isa-app/src/assets/images/OF_Icon_PP.svg | 11 + apps/isa-app/src/assets/images/OF_Icon_SW.svg | 11 + apps/isa-app/src/assets/images/OF_Icon_TB.svg | 11 + apps/isa-app/src/assets/images/OF_Icon_VI.svg | 11 + apps/isa-app/src/assets/images/OF_Icon_ZS.svg | 11 + apps/isa-app/src/assets/images/Star_empty.png | Bin 0 -> 1001 bytes apps/isa-app/src/assets/images/Star_full.png | Bin 0 -> 1012 bytes apps/isa-app/src/assets/images/Star_half.png | Bin 0 -> 1129 bytes apps/isa-app/src/assets/images/barcode.png | Bin 0 -> 653 bytes .../bookmark_benachrichtigung_archiv.svg | 112 + .../src/assets/images/bookmark_branch.svg | 12 + .../src/assets/images/bookmark_customer.svg | 18 + .../assets/images/bookmark_subscription.svg | 21 + apps/isa-app/src/assets/images/checkbox.svg | 7 + .../src/assets/images/checkbox_checked.svg | 5 + .../src/assets/images/email_bookmark.svg | 15 + .../src/assets/images/hugendubel-logo.png | Bin 0 -> 22039 bytes apps/isa-app/src/assets/images/radio.svg | 7 + .../src/assets/images/radio_checked.svg | 5 + .../src/assets/images/recommendation_tag.png | Bin 0 -> 2841 bytes .../src/assets/images/sms_bookmark.svg | 13 + apps/isa-app/src/assets/images/spinner.svg | 11 + .../src/assets/images/tag_icon_preorder.svg | 17 + apps/isa-app/src/config/config.feature.json | 62 + .../src/config/config.integration.json | 21 + apps/isa-app/src/config/config.json | 62 + .../isa-app/src/config/config.production.json | 21 + apps/isa-app/src/config/config.staging.json | 21 + apps/isa-app/src/config/config.test.json | 62 + .../src/environments/environment.prod.ts | 3 + apps/isa-app/src/environments/environment.ts | 16 + apps/isa-app/src/favicon.ico | Bin 0 -> 7406 bytes apps/isa-app/src/index.html | 21 + apps/isa-app/src/main.ts | 13 + apps/isa-app/src/manifest.webmanifest | 41 + apps/isa-app/src/polyfills.ts | 64 + apps/isa-app/src/scss/_branch.scss | 26 + apps/isa-app/src/scss/_customer.scss | 29 + apps/isa-app/src/scss/_root.scss | 18 + apps/isa-app/src/silent-refresh.html | 7 + apps/isa-app/src/styles.scss | 23 + apps/isa-app/src/test.ts | 24 + apps/isa-app/tsconfig.app.json | 15 + apps/isa-app/tsconfig.spec.json | 18 + .../features-to-assortment.mapping.ts | 10 + .../rest/rest-remission-products.service.ts | 2 +- .../lib/services/rest-remission.service.ts | 4 +- apps/isa/remission/src/test.ts | 4 +- apps/isa/remission/tsconfig.lib.json | 1 + apps/isa/remission/tsconfig.lib.prod.json | 3 + apps/modal/availabilities/src/test.ts | 4 +- .../history/src/lib/history.component.html | 9 +- .../history/src/lib/history.component.ts | 16 +- apps/modal/history/src/test.ts | 4 +- apps/modal/images/src/test.ts | 4 +- apps/modal/notifications/karma.conf.js | 21 +- .../notifications-list-item.component.html | 2 +- .../notifications-list-item.component.spec.ts | 48 + ...tifications-remission-group.component.html | 2 +- ...ications-remission-group.component.spec.ts | 101 + ...notifications-remission-group.component.ts | 3 +- ...fications-reservation-group.component.html | 2 +- ...ations-reservation-group.component.spec.ts | 101 + ...tifications-reservation-group.component.ts | 2 +- ...cations-task-calendar-group.component.html | 2 +- ...ions-task-calendar-group.component.spec.ts | 101 + ...fications-task-calendar-group.component.ts | 2 +- .../src/lib/notifications.component.html | 2 +- .../src/lib/notifications.component.spec.ts | 105 + .../src/lib/notifications.component.ts | 22 +- apps/modal/notifications/src/test.ts | 4 +- apps/modal/printer/src/test.ts | 4 +- apps/modal/reorder/src/test.ts | 4 +- apps/modal/reviews/src/test.ts | 4 +- apps/native-container/src/test.ts | 4 +- apps/native-container/tsconfig.lib.json | 1 + apps/native-container/tsconfig.lib.prod.json | 3 + .../article-details.component.html | 473 +- .../article-details.component.scss | 30 +- .../article-details.component.ts | 16 +- .../article-recommendations.component.html | 4 +- .../article-recommendations.component.scss | 13 +- .../article-recommendations.component.ts | 3 +- .../article-search-new.store.ts | 343 - .../article-search.component.html | 9 +- .../article-search.component.scss | 10 +- .../article-search.component.ts | 112 +- .../article-search/article-search.module.ts | 14 +- .../article-searchbox.component.html | 63 - .../article-searchbox.component.scss | 87 - .../article-searchbox.component.ts | 187 - .../article-searchbox.module.ts | 18 - .../filter-chips/filter-chips.component.html | 12 - .../filter-chips/filter-chips.component.scss | 30 - .../filter-chips/filter-chips.component.ts | 25 - .../filter-chips/filter-chips.module.ts | 14 - .../search-filter.component.html | 2 +- .../search-main/search-main.component.scss | 9 +- .../search-main/search-main.component.ts | 6 +- .../added-to-cart-modal.component.ts | 11 +- .../search-result-item.component.html | 4 +- .../search-result-item.component.ts | 9 +- .../search-results.component.html | 2 +- .../search-results.component.scss | 4 +- .../search-results.component.ts | 4 +- .../src/lib/page-catalog.component.html | 5 +- .../src/lib/page-catalog.component.scss | 10 +- apps/page/catalog/src/test.ts | 4 +- .../checkout-dummy.component.html | 4 +- .../checkout-dummy.component.scss | 14 +- .../checkout-dummy.component.ts | 17 +- .../checkout-review.component.html | 14 +- .../checkout-review.component.scss | 6 +- .../checkout-review.component.ts | 92 +- .../checkout-review/checkout-review.module.ts | 14 +- .../notification-channels.component.html | 14 - .../notification-channels.component.scss | 40 - .../notification-channels.component.ts | 33 - .../notification-channels.service.ts | 47 - .../notification-checkbox.component.html | 9 - .../notification-checkbox.component.scss | 36 - .../notification-checkbox.component.ts | 94 - .../notification-edit.component.html | 21 - .../notification-edit.component.scss | 46 - .../notification-edit.component.ts | 96 - .../shopping-cart-item.component.html | 8 +- .../shopping-cart-item.component.ts | 2 +- .../checkout-summary.component.html | 12 +- .../checkout-summary.component.scss | 4 +- .../checkout-summary.component.ts | 6 +- .../list-options.scss | 4 + ...urchasing-options-list-item.component.scss | 2 +- .../b2b-delivery-option.component.scss | 1 + .../delivery-option.component.scss | 6 +- .../dig-delivery-option.component.scss | 4 + .../pick-up-option.component.scss | 3 + .../purchasing-options-modal.component.scss | 4 +- .../purchasing-options-modal.component.ts | 12 +- .../take-away-option.component.scss | 3 + .../src/lib/page-checkout.component.scss | 3 +- apps/page/checkout/src/test.ts | 4 +- apps/page/customer/karma.conf.js | 14 +- .../customer-create-b2b.component.ts | 2 +- .../customer-create-branch.component.ts | 2 +- .../customer-create-guest.component.ts | 2 +- .../customer-create-online.component.ts | 2 +- .../customer-create.component.scss | 2 +- .../customer-create.component.ts | 2 +- .../customer-create/customer-create.module.ts | 2 +- .../address/address.component.scss | 2 +- .../payer-create/payer-create.component.ts | 2 +- .../payer-edit/payer-edit.component.ts | 2 +- .../shipping-create.component.ts | 2 +- .../shipping-edit/shipping-edit.component.ts | 2 +- .../customer-card.component.html | 4 +- .../customer-card/customer-card.component.ts | 7 +- .../customer-data-edit.component.scss | 4 +- .../customer-data-edit.component.ts | 2 +- .../customer-details.component.html | 67 +- .../customer-details.component.scss | 2 +- .../customer-details.component.ts | 34 +- .../customer-details.module.ts | 3 +- .../customer-order-details.component.scss | 4 + .../customer-order-details.component.ts | 2 +- .../customer-orders.component.html | 14 +- .../customer-orders.component.scss | 4 + .../customer-orders.component.ts | 4 +- .../guards/customer-card.guard.ts | 9 +- .../customer-search.component.html | 23 +- .../customer-search.component.scss | 37 +- .../customer-search.component.ts | 213 +- .../customer-search/customer-search.module.ts | 18 +- .../customer-search.service.spec.ts | 233 - .../customer-search.service.ts | 529 - ...-search-main-autocomplete.provider.spec.ts | 45 + ...tomer-search-main-autocomplete.provider.ts | 24 + .../search-filter.component.html | 37 +- .../search-filter.component.scss | 11 +- .../search-filter/search-filter.component.ts | 57 +- .../search-main/search-main.component.html | 19 +- .../search-main/search-main.component.scss | 9 +- .../search-main/search-main.component.spec.ts | 125 - .../search-main/search-main.component.ts | 86 +- .../customer-result-card.component.html | 17 +- .../customer-result-card.component.scss | 2 +- .../search-result.component.spec.ts | 102 - .../search-results.component.html | 25 +- .../search-results.component.scss | 39 +- .../search-results.component.ts | 198 +- ...stomer.search-state-search-service.spec.ts | 85 + .../customer.search-state-search-service.ts | 41 + ...tomer.search-state-settings-loader.spec.ts | 43 + .../customer.search-state-settings-loader.ts | 26 + .../customer-searchbox.component.html | 53 - .../customer-searchbox.component.scss | 39 - .../customer-searchbox.component.spec.ts | 168 - .../customer-searchbox.component.ts | 99 - .../shared/customer-searchbox/index.ts | 3 - .../src/lib/guards/customer-create.guard.ts | 4 +- .../cant-add-customer-to-cart.component.html | 10 +- .../cant-add-customer-to-cart.component.ts | 3 +- .../cant-select-guest-modal.component.html | 6 +- .../cant-select-guest-modal.component.ts | 3 +- .../src/lib/page-customer-routing.module.ts | 7 +- .../src/lib/page-customer.component.scss | 8 +- apps/page/customer/src/test.ts | 4 +- apps/page/dashboard/README.md | 25 + apps/page/dashboard/karma.conf.js | 43 + apps/page/dashboard/ng-package.json | 7 + apps/page/dashboard/package.json | 11 + .../src/lib/dashboard-routing-module.ts | 16 + .../src/lib/dashboard.component.html | 4 + .../src/lib/dashboard.component.scss | 7 + .../src/lib/dashboard.component.spec.ts | 117 + .../dashboard/src/lib/dashboard.component.ts | 20 + .../dashboard/src/lib/dashboard.module.ts | 16 + .../src/lib/kpi-card/kpi-card.component.html | 10 + .../src/lib/kpi-card/kpi-card.component.scss | 29 + .../lib/kpi-card/kpi-card.component.spec.ts | 71 + .../src/lib/kpi-card/kpi-card.component.ts | 19 + .../products-card/product-card.component.html | 8 + .../products-card/product-card.component.scss | 30 + .../product-card.component.spec.ts | 72 + .../products-card/product-card.component.ts | 15 + apps/page/dashboard/src/public-api.ts | 6 + apps/page/dashboard/src/test.ts | 25 + apps/page/dashboard/tsconfig.lib.json | 20 + apps/page/dashboard/tsconfig.lib.prod.json | 10 + apps/page/dashboard/tsconfig.spec.json | 17 + .../goods-in-cleanup-list.component.html | 5 +- .../goods-in-cleanup-list.component.scss | 4 + .../goods-in-cleanup-list.component.ts | 27 +- .../goods-in-details.component.ts | 34 +- .../goods-in-edit.component.html | 2 +- .../goods-in-edit.component.scss | 2 +- .../goods-in-edit/goods-in-edit.component.ts | 10 +- .../goods-in-list-item.component.scss | 4 + .../goods-in-list.component.html | 1 + .../goods-in-list.component.scss | 8 +- .../goods-in-list/goods-in-list.component.ts | 25 +- .../lib/goods-in-list/goods-in-list.store.ts | 2 +- .../goods-in-remission-preview.component.html | 1 + .../goods-in-remission-preview.component.scss | 4 + .../goods-in-remission-preview.component.ts | 46 +- .../goods-in-reservation.component.html | 1 + .../goods-in-reservation.component.scss | 4 + .../goods-in-reservation.component.ts | 25 +- .../goods-in-search-filter.component.scss | 6 +- .../goods-in-search-filter.component.ts | 14 +- .../goods-in-search.component.html | 9 +- .../goods-in-search.component.scss | 10 +- .../goods-in-search.component.ts | 21 +- .../goods-in-search/goods-in-search.module.ts | 3 +- .../goods-in-search-main.component.html | 8 +- .../goods-in-search-main.component.scss | 4 +- .../goods-in-search-main.component.ts | 46 +- .../goods-in-search-results.component.html | 1 + .../goods-in-search-results.component.scss | 4 + .../goods-in-search-results.component.ts | 24 +- .../goods-in/src/lib/goods-in.component.html | 2 +- .../goods-in/src/lib/goods-in.component.scss | 6 +- .../goods-in/src/lib/goods-in.component.ts | 5 +- apps/page/goods-in/src/test.ts | 4 +- .../goods-out-details.component.ts | 17 +- .../goods-out-edit.component.html | 2 +- .../goods-out-edit.component.scss | 2 +- .../goods-out-edit.component.ts | 14 +- .../goods-out-search-filter.component.scss | 6 +- .../goods-out-search-filter.component.ts | 16 +- .../goods-out-search.component.html | 9 +- .../goods-out-search.component.scss | 14 +- .../goods-out-search.component.ts | 21 +- .../goods-out-search.module.ts | 3 +- .../goods-out-search-main.component.scss | 2 +- .../goods-out-search-main.component.ts | 25 +- .../goods-out-search-results.component.ts | 23 +- .../src/lib/goods-out.component.html | 2 +- .../src/lib/goods-out.component.scss | 5 +- .../goods-out/src/lib/goods-out.component.ts | 5 +- apps/page/goods-out/src/test.ts | 4 +- apps/page/remission/README.md | 25 + apps/page/remission/karma.conf.js | 43 + apps/page/remission/ng-package.json | 7 + apps/page/remission/package.json | 11 + .../add-product/add-product.component.html | 18 + .../add-product/add-product.component.scss | 0 .../lib/add-product/add-product.component.ts | 116 + .../src/lib/add-product/add-product.module.ts | 13 + .../remission/src/lib/add-product/index.ts | 2 + .../create-remission.component.html | 28 + .../create-remission.component.ts | 88 + .../create-remission.module.ts | 13 + .../finish-remission.component.html | 18 + .../finish-remission.component.scss | 0 .../finish-remission.component.ts | 91 + .../finish-remission.module.ts | 12 + .../finish-shipping-document.component.html | 15 + .../finish-shipping-document.component.scss | 0 .../finish-shipping-document.component.ts | 153 + .../finish-shipping-document.module.ts | 12 + apps/page/remission/src/lib/index.ts | 7 + .../add-product-modal.component.html | 53 + .../add-product-modal.component.scss | 17 + .../add-product-modal.component.ts | 73 + .../add-product-modal.data.ts | 5 + .../add-product-modal.module.ts | 30 + .../src/lib/modals/add-product-modal/index.ts | 3 + ...-to-shipping-document-modal.component.html | 27 + ...-to-shipping-document-modal.component.scss | 3 + ...ct-to-shipping-document-modal.component.ts | 36 + ...oduct-to-shipping-document-modal.module.ts | 16 + .../src/lib/pipes/assortment.pipe.ts | 16 + apps/page/remission/src/lib/pipes/index.ts | 2 + .../src/lib/pipes/product-group.pipe.ts | 40 + .../src/lib/pipes/remission-pipe.module.ts | 11 + .../lib/pipes/short-receipt-number.pipe.ts | 10 + .../remission/src/lib/pipes/supplier.pipe.ts | 31 + .../providers/add-product-scan.provider.ts | 24 + .../create-remission-scan.provider.ts | 24 + .../finish-shipping-document-scan.provider.ts | 24 + .../page/remission/src/lib/providers/index.ts | 5 + .../remission/src/lib/remission-list/index.ts | 3 + .../remission-filter.component.html | 25 + .../remission-filter.component.scss | 43 + .../remission-filter.component.ts | 37 + .../remission-filter.module.ts | 13 + .../remission-list-item/index.ts | 2 + .../remission-list-item.component.html | 98 + .../remission-list-item.component.scss | 7 + .../remission-list-item.component.ts | 153 + .../remission-list-item.module.ts | 23 + .../remission-list.component-store.ts | 307 + .../remission-list.component.html | 132 + .../remission-list.component.scss | 32 + .../remission-list.component.ts | 256 + .../remission-list/remission-list.module.ts | 31 + .../remission.component-store.ts | 62 + .../src/lib/remission-routing.module.ts | 78 + .../src/lib/remission.component.html | 2 + .../src/lib/remission.component.scss | 7 + .../src/lib/remission.component.spec.ts | 23 + .../remission/src/lib/remission.component.ts | 98 + .../remission/src/lib/remission.module.ts | 11 + .../required-capacities.component.html | 13 + .../required-capacities.component.scss | 22 + .../required-capacities.component.ts | 51 + .../required-capacities.module.ts | 12 + ...pping-document-details-item.component.html | 32 + ...pping-document-details-item.component.scss | 3 + ...hipping-document-details-item.component.ts | 28 + .../shipping-document-details.component.html | 72 + .../shipping-document-details.component.scss | 3 + .../shipping-document-details.component.ts | 87 + .../shipping-document-details.module.ts | 14 + .../shipping-document-details.component.html | 22 + .../shipping-document-details.component.scss | 3 + .../shipping-document-details.component.ts | 80 + ...shipping-document-list-item.component.html | 45 + ...shipping-document-list-item.component.scss | 3 + .../shipping-document-list-item.component.ts | 33 + .../shipping-document-list.component.html | 7 + .../shipping-document-list.component.scss | 0 .../shipping-document-list.component.ts | 42 + .../shipping-document-list.module.ts | 16 + apps/page/remission/src/public-api.ts | 5 + apps/page/remission/src/test.ts | 25 + apps/page/remission/tsconfig.lib.json | 20 + apps/page/remission/tsconfig.lib.prod.json | 10 + apps/page/remission/tsconfig.spec.json | 17 + .../task-info/task-info.component.ts | 1 + .../task-list/task-list.component.scss | 2 +- .../task-list/task-list.component.ts | 1 + .../task-calendar-filter.component.html | 46 +- .../task-calendar-filter.component.scss | 18 +- .../task-calendar-filter.component.ts | 39 +- .../article-list-modal.component.ts | 2 +- .../lib/modals/task/task-modal.component.ts | 2 +- .../src/lib/page-task-calendar.component.html | 23 +- .../src/lib/page-task-calendar.component.scss | 40 +- .../src/lib/page-task-calendar.component.ts | 47 +- .../src/lib/page-task-calendar.module.ts | 13 +- .../lib/pages/calendar/calendar.component.ts | 8 +- .../src/lib/pages/tasks/tasks.component.ts | 8 +- .../src/lib/task-calendar.store.ts | 52 +- apps/page/task-calendar/src/test.ts | 4 +- apps/sales/src/app/app-routing.module.ts | 1 + apps/sales/src/app/app-store.module.ts | 6 +- apps/sales/src/app/app.component.spec.ts | 16 +- .../breadcrumbs/breadcrumbs.component.spec.ts | 14 +- .../header/header.component.spec.ts | 14 +- .../log-out/log-out.component.spec.ts | 14 +- .../components/menu/menu.component.spec.ts | 14 +- .../printer-selection.component.spec.ts | 14 +- .../process-header.component.spec.ts | 14 +- .../process-tab/process-tab.component.spec.ts | 14 +- .../error/component/error.component.spec.ts | 14 +- .../core/error/component/error.component.ts | 2 +- .../http-error-handler.interceptor.ts | 3 +- .../src/app/core/mappings/customer.mapping.ts | 2 +- .../src/app/core/mappings/product.mapping.ts | 5 +- .../core/mappings/recommendation.mapping.ts | 2 +- .../src/app/core/mappings/shelf.mapping.ts | 2 +- .../core/models/customer-features.model.ts | 2 +- .../strategies/container-position-strategy.ts | 2 +- .../src/app/core/services/app.service.ts | 28 +- .../src/app/core/services/branch.service.ts | 2 +- .../core/services/collecting-shelf.service.ts | 2 +- .../core/services/content-header.service.ts | 2 +- .../src/app/core/services/location-service.ts | 2 +- .../core/store/selectors/forms.selectors.ts | 3 +- .../store/selectors/remission.selectors.ts | 2 +- .../src/app/core/store/state/app.state.ts | 2 +- .../src/app/core/store/state/process.state.ts | 2 +- .../app/core/store/state/remission.state.ts | 2 +- apps/sales/src/app/core/utils/app.utils.ts | 2 +- .../branch-main/branch-main.component.spec.ts | 14 +- .../book-card/book-card.component.spec.ts | 14 +- .../event-card/event-card.component.spec.ts | 14 +- .../kpi-card/kpi-card.component.spec.ts | 14 +- .../news-card/news-card.component.spec.ts | 14 +- .../recommandation-card.component.spec.ts | 14 +- .../pages/dashboard.component.spec.ts | 14 +- ...goods-in-article-details.component.spec.ts | 14 +- .../goods-in-article-details.component.ts | 2 +- .../goods-in-order-card.component.spec.ts | 14 +- .../goods-in-order-card.component.ts | 2 +- .../goods-in-order-tag.component.spec.ts | 14 +- .../goods-in-order-loading.component.spec.ts | 14 +- .../goods-in-order-details.component.spec.ts | 14 +- .../goods-in-order-details.component.ts | 4 +- .../goods-in-search-results.component.spec.ts | 14 +- .../goods-in-search-results.component.ts | 2 +- .../goods-in-search.component.spec.ts | 14 +- .../process-delete-dialog.component.spec.ts | 14 +- .../product-details.component.ts | 2 +- ...duct-to-remission-dialog.component.spec.ts | 14 +- ...d-product-to-remission-dialog.component.ts | 2 +- ...shipping-document-dialog.component.spec.ts | 14 +- ...ocument-partially-dialog.component.spec.ts | 14 +- ...ing-document-partially-dialog.component.ts | 2 +- .../remission-filter-item.component.spec.ts | 14 +- .../remission-filter-item.component.ts | 2 +- .../remission-filters.component.spec.ts | 14 +- ...nerate-shipping-document.component.spec.ts | 14 +- ...on-generate-shipping-document.component.ts | 2 +- .../remission-leave-dialog.component.spec.ts | 14 +- ...ission-list-card-loading.component.spec.ts | 14 +- ...ission-list-card-started.component.spec.ts | 20 +- .../card-header/card-header.component.spec.ts | 24 +- .../remission-list-card.component.spec.ts | 16 +- .../remission-list.component.spec.ts | 16 +- .../remission-list.component.ts | 3 +- .../remission-list.datasource.ts | 2 +- ...open-shipping-document-widget.component.ts | 2 +- ...emission-reminder-dialog.component.spec.ts | 14 +- ...-product-invalid-barcode.component.spec.ts | 14 +- ...shipping-document-closed.component.spec.ts | 14 +- ...on-selected-filter-items.component.spec.ts | 14 +- ...mission-selected-filter-items.component.ts | 2 +- ...n-shipping-document-card.component.spec.ts | 14 +- ...ission-shipping-document.component.spec.ts | 14 +- .../remission-shipping-document.component.ts | 2 +- .../remission-start-dialog.component.spec.ts | 14 +- ...to-top-to-bottom-actions.component.spec.ts | 14 +- .../remission-list-filter.component.ts | 2 +- ...roduct-to-remission-list.component.spec.ts | 14 +- ...add-product-to-remission-list.component.ts | 2 +- .../remission-details.component.ts | 2 +- .../remission-finish.component.spec.ts | 50 +- .../remission-finish.component.ts | 2 +- .../product-list/product-list.component.ts | 2 +- .../remission-list-create.component.ts | 2 +- .../ueberlauf-capacities.component.ts | 2 +- .../remission-list-started.component.spec.ts | 14 +- .../remission-list-started.component.ts | 2 +- .../shipping-document-container.component.ts | 5 +- .../shipping-document-creation.component.ts | 2 +- .../remissions-overview.component.ts | 52 +- .../services/remission-filter.service.ts | 2 +- .../article-details.component.ts | 2 +- .../order-item-edit.component.spec.ts | 14 +- .../order-loading.component.spec.ts | 14 +- .../order-overview-edit.component.spec.ts | 14 +- .../searchbar/searchbar.component.ts | 2 +- .../shelf-filter/shelf-filter.component.ts | 2 +- .../shelf-edit-compartment.component.ts | 2 +- ...helf-edit-order-from-customer.component.ts | 2 +- .../shelf-edit-order.component.spec.ts | 14 +- .../shelf-edit-order.component.ts | 2 +- .../shelf-history/shelf-history.component.ts | 2 +- .../customer-features.component.ts | 2 +- .../shelf-order-details.component.ts | 2 +- .../search/search-input.component.ts | 2 +- .../shelf/pipes/show-compartment-code.pipe.ts | 2 +- .../shelf/services/shelf-edit-form.service.ts | 2 +- .../shelf/services/shelf-filter.service.ts | 2 +- .../pages/content/content.component.spec.ts | 14 +- .../back-arrow/back-arrow.component.spec.ts | 14 +- .../customer/shelf/search/search.effects.ts | 2 +- .../customer/shelf/search/search.facade.ts | 2 +- apps/sales/src/main.ts | 4 +- apps/sales/src/polyfills.ts | 2 +- apps/sales/src/test.ts | 2 +- ...in-out-order-details-covers.component.scss | 11 +- ...in-out-order-details-header.component.scss | 2 +- .../goods-in-out-order-details.component.scss | 2 +- .../goods-in-out-order-edit.component.scss | 8 + .../goods-in-out-order-edit.component.ts | 2 +- .../goods-in-out-order-group.component.scss | 2 +- apps/shared/goods-in-out/src/test.ts | 4 +- ...otification-channel-control.component.html | 15 +- ...otification-channel-control.component.scss | 15 +- .../notification-channel-control.component.ts | 20 +- .../notification-channel-control/src/test.ts | 4 +- apps/shell/breadcrumb/karma.conf.js | 15 +- .../src/lib/shell-breadcrumb.component.scss | 3 +- .../src/lib/shell-breadcrumb.component.ts | 5 +- apps/shell/breadcrumb/src/test.ts | 4 +- apps/shell/filter-overlay/README.md | 25 + apps/shell/filter-overlay/karma.conf.js | 41 + apps/shell/filter-overlay/ng-package.json | 7 + apps/shell/filter-overlay/package.json | 11 + .../src/lib/filter-overlay.component.html | 5 + .../src/lib/filter-overlay.component.scss | 6 + .../src/lib/filter-overlay.component.spec.ts | 24 + .../src/lib/filter-overlay.component.ts | 84 + .../src/lib/filter-overlay.module.ts | 9 + apps/shell/filter-overlay/src/public-api.ts | 6 + apps/shell/filter-overlay/src/test.ts | 25 + apps/shell/filter-overlay/tsconfig.lib.json | 20 + .../filter-overlay/tsconfig.lib.prod.json | 10 + apps/shell/filter-overlay/tsconfig.spec.json | 17 + apps/shell/footer/README.md | 25 + apps/shell/footer/karma.conf.js | 43 + apps/shell/footer/ng-package.json | 7 + apps/shell/footer/package.json | 11 + .../footer/src/lib/footer.component.html | 5 + .../footer/src/lib/footer.component.scss | 26 + .../footer/src/lib/footer.component.spec.ts | 20 + apps/shell/footer/src/lib/footer.component.ts | 13 + apps/shell/footer/src/lib/footer.module.ts | 10 + apps/shell/footer/src/public-api.ts | 6 + apps/shell/footer/src/test.ts | 25 + apps/shell/footer/tsconfig.lib.json | 20 + apps/shell/footer/tsconfig.lib.prod.json | 10 + apps/shell/footer/tsconfig.spec.json | 17 + apps/shell/header/karma.conf.js | 14 +- .../header/src/lib/header.component.html | 37 +- .../header/src/lib/header.component.scss | 80 +- .../header/src/lib/header.component.spec.ts | 36 + apps/shell/header/src/lib/header.component.ts | 24 +- apps/shell/header/src/lib/header.module.ts | 3 +- .../section-toggle.component.html | 9 + .../section-toggle.component.scss | 18 + .../section-toggle.component.spec.ts | 116 + .../section-toggle.component.ts | 65 +- apps/shell/header/src/public-api.ts | 1 - apps/shell/header/src/test.ts | 4 +- apps/shell/process/README.md | 25 + apps/shell/process/karma.conf.js | 43 + apps/shell/process/ng-package.json | 7 + apps/shell/process/package.json | 11 + .../process-tab/process-tab.component.html | 19 + .../process-tab/process-tab.component.scss | 39 + .../process-tab/process-tab.component.spec.ts | 160 + .../lib/process-tab/process-tab.component.ts | 96 + .../process/src/lib/process.component.html | 12 + .../process/src/lib/process.component.scss | 35 + .../process/src/lib/process.component.spec.ts | 46 + .../process/src/lib/process.component.ts | 22 + apps/shell/process/src/lib/process.module.ts | 13 + apps/shell/process/src/public-api.ts | 7 + apps/shell/process/src/test.ts | 25 + apps/shell/process/tsconfig.lib.json | 20 + apps/shell/process/tsconfig.lib.prod.json | 10 + apps/shell/process/tsconfig.spec.json | 17 + apps/store/search-component-store/README.md | 25 + .../search-component-store/karma.conf.js | 43 + .../search-component-store/ng-package.json | 7 + .../store/search-component-store/package.json | 11 + .../src/lib/defs/index.ts | 6 + .../src/lib/defs/search-settings-loader.ts | 6 + .../lib/defs/search-state-search-result.ts | 6 + .../lib/defs/search-state-search-service.ts | 10 + .../src/lib/defs/search-state.ts | 9 + .../search-component-store/src/lib/index.ts | 6 + .../src/lib/search-component-store.module.ts | 8 + .../search-component-store.service.spec.ts | 560 + .../src/lib/search-component-store.service.ts | 252 + .../search-component-store/src/lib/tokens.ts | 7 + .../search-component-store/src/public-api.ts | 5 + apps/store/search-component-store/src/test.ts | 25 + .../search-component-store/tsconfig.lib.json | 20 + .../tsconfig.lib.prod.json | 10 + .../search-component-store/tsconfig.spec.json | 17 + apps/swagger/availability/src/test.ts | 4 +- apps/swagger/availability/tsconfig.lib.json | 1 + .../availability/tsconfig.lib.prod.json | 3 + apps/swagger/cat/src/test.ts | 4 +- apps/swagger/cat/tsconfig.lib.json | 1 + apps/swagger/cat/tsconfig.lib.prod.json | 3 + apps/swagger/checkout/src/test.ts | 4 +- apps/swagger/checkout/tsconfig.lib.json | 1 + apps/swagger/checkout/tsconfig.lib.prod.json | 3 + apps/swagger/crm/src/lib/models.ts | 5 +- .../crm/src/lib/models/input-group-dto.ts | 8 + .../crm/src/lib/models/problem-details.ts | 2 +- .../crm/src/lib/models/query-settings-dto.ts | 8 + .../response-args-of-query-settings-dto.ts | 6 + .../crm/src/lib/services/customer.service.ts | 71 +- .../crm/src/lib/services/opt-in.service.ts | 6 +- .../crm/src/lib/services/payer.service.ts | 10 +- apps/swagger/crm/src/test.ts | 4 +- apps/swagger/crm/tsconfig.lib.json | 1 + apps/swagger/crm/tsconfig.lib.prod.json | 3 + apps/swagger/eis/src/lib/models.ts | 6 +- apps/swagger/eis/src/lib/models/entity-dto.ts | 3 +- .../models/entity-dtoreference-container.ts | 3 +- .../src/lib/models/external-reference-dto.ts | 3 +- apps/swagger/eis/src/lib/models/input-dto.ts | 4 + .../eis/src/lib/models/input-group-dto.ts | 8 + apps/swagger/eis/src/lib/models/input-type.ts | 2 +- apps/swagger/eis/src/lib/models/option-dto.ts | 4 + .../eis/src/lib/models/problem-details.ts | 2 +- .../eis/src/lib/models/query-settings-dto.ts | 8 + .../response-args-of-query-settings-dto.ts | 6 + .../eis/src/lib/models/touched-base.ts | 3 + .../src/lib/services/eisbackend.service.ts | 14 +- .../eis/src/lib/services/eispublic.service.ts | 61 +- apps/swagger/eis/src/test.ts | 4 +- apps/swagger/eis/tsconfig.lib.json | 1 + apps/swagger/eis/tsconfig.lib.prod.json | 3 + apps/swagger/isa/src/test.ts | 4 +- apps/swagger/isa/tsconfig.lib.json | 1 + apps/swagger/isa/tsconfig.lib.prod.json | 3 + apps/swagger/oms/src/test.ts | 4 +- apps/swagger/oms/tsconfig.lib.json | 1 + apps/swagger/oms/tsconfig.lib.prod.json | 3 + apps/swagger/print/src/test.ts | 4 +- apps/swagger/print/tsconfig.lib.json | 1 + apps/swagger/print/tsconfig.lib.prod.json | 3 + apps/swagger/remi/src/lib/models.ts | 10 +- ...s-of-return-item-dtoand-return-item-dto.ts | 6 +- ...suggestion-dtoand-return-suggestion-dto.ts | 6 +- .../swagger/remi/src/lib/models/branch-dto.ts | 2 +- .../remi/src/lib/models/input-group-dto.ts | 8 + .../lib/models/iresult-of-return-item-dto.ts | 5 - .../remi/src/lib/models/problem-details.ts | 2 +- .../remi/src/lib/models/query-settings-dto.ts | 8 + .../response-args-of-query-settings-dto.ts | 6 + .../remi/src/lib/models/return-info-dto.ts | 5 + .../remi/src/lib/models/return-item-dto.ts | 1 + .../models/return-value-of-return-item-dto.ts | 6 + ... return-value-of-return-suggestion-dto.ts} | 3 +- .../remi/src/lib/models/return-value.ts | 6 + .../remi/src/lib/models/stock-info-dto.ts | 15 + .../remi/src/lib/services/package.service.ts | 6 +- .../remi/src/lib/services/remi.service.ts | 95 +- .../remi/src/lib/services/return.service.ts | 60 +- .../remi/src/lib/services/stock.service.ts | 14 +- .../remi/src/lib/services/supplier.service.ts | 2 +- apps/swagger/remi/src/test.ts | 4 +- apps/ui/autocomplete/src/test.ts | 4 +- apps/ui/calendar/src/test.ts | 4 +- apps/ui/checkbox/src/test.ts | 4 +- apps/ui/common/src/lib/common.module.ts | 15 +- apps/ui/common/src/lib/date/date.adapter.ts | 2 +- apps/ui/common/src/test.ts | 4 +- .../src/lib/body/body.component.scss | 2 + .../src/lib/datepicker.component.html | 6 +- .../src/lib/datepicker.component.scss | 4 +- .../datepicker/src/lib/datepicker.module.ts | 4 +- .../src/lib/header/header.component.html | 14 +- apps/ui/datepicker/src/test.ts | 4 +- apps/ui/dropdown/src/test.ts | 4 +- .../filter-input-group-main.component.scss | 2 +- .../filter-input-group-main.component.ts | 2 +- apps/ui/filter/src/lib/next/tree/ui-filter.ts | 2 +- apps/ui/filter/src/test.ts | 4 +- .../src/lib/ui-form-control.component.scss | 8 +- .../src/lib/ui-form-first-error.pipe.ts | 1 - apps/ui/form-control/src/test.ts | 4 +- .../lib/icon-badge/icon-badge.component.html | 2 +- apps/ui/icon/src/test.ts | 4 +- apps/ui/input/src/lib/ui-input.directive.ts | 2 +- apps/ui/input/src/test.ts | 4 +- apps/ui/loader/src/test.ts | 4 +- .../confirm-modal.component.scss | 23 + .../confirm-modal/confirm-modal.component.ts | 22 + .../lib/confirm-modal/confirm-modal.data.ts | 5 + apps/ui/modal/src/lib/confirm-modal/index.ts | 2 + .../lib/dialog/dialog-modal.component.scss | 6 +- .../src/lib/dialog/dialog-modal.component.ts | 4 +- apps/ui/modal/src/lib/dialog/index.ts | 4 +- .../src/lib/dialog/open-dialog.interceptor.ts | 5 +- apps/ui/modal/src/lib/index.ts | 11 +- .../src/lib/message-modal.component.scss | 6 +- .../modal/src/lib/message-modal.component.ts | 3 +- apps/ui/modal/src/lib/modal.component.scss | 2 +- apps/ui/modal/src/lib/modal.module.ts | 5 +- apps/ui/modal/src/lib/prompt-modal/index.ts | 2 + .../prompt-modal/prompt-modal.component.scss | 27 + .../prompt-modal/prompt-modal.component.ts | 23 + .../src/lib/prompt-modal/prompt-modal.data.ts | 7 + apps/ui/modal/src/test.ts | 4 +- apps/ui/notes/src/lib/ui-notes.component.scss | 2 +- apps/ui/notes/src/test.ts | 4 +- apps/ui/progress/src/test.ts | 4 +- apps/ui/quantity-dropdown/src/test.ts | 4 +- apps/ui/radio/src/test.ts | 4 +- .../src/lib/scroll-container.component.html | 3 +- .../src/lib/scroll-container.component.scss | 21 +- .../src/lib/scroll-container.component.ts | 8 + .../skeleton-loader.component.scss | 4 +- apps/ui/scroll-container/src/test.ts | 4 +- apps/ui/searchbox/src/test.ts | 4 +- apps/ui/select-bullet/src/test.ts | 4 +- apps/ui/select/src/test.ts | 4 +- apps/ui/slider/src/lib/slider.component.scss | 64 +- apps/ui/slider/src/lib/slider.component.ts | 10 +- apps/ui/slider/src/test.ts | 4 +- apps/ui/spinner/src/lib/index.ts | 4 + apps/ui/spinner/src/test.ts | 4 +- apps/ui/stars/src/test.ts | 4 +- apps/ui/switch/src/test.ts | 4 +- apps/ui/toggle/src/test.ts | 4 +- apps/ui/tooltip/src/test.ts | 4 +- apps/ui/tshirt/src/test.ts | 4 +- apps/ui/validators/src/test.ts | 4 +- apps/utils/collection/src/test.ts | 4 +- apps/utils/common/src/test.ts | 4 +- apps/utils/id/src/test.ts | 4 +- apps/utils/object/src/test.ts | 4 +- assets/icons.afdesign | Bin 86975 -> 94889 bytes assets/icons.svg | 421 +- azure-pipelines.yml | 81 +- bitbucket-pipelines.yml | 32 - debug.log | 2 - helmvalues/client.Feature.yaml | 2 +- helmvalues/client.Integration.yaml | 2 +- helmvalues/client.Production.yaml | 2 +- helmvalues/client.Staging.yaml | 2 +- helmvalues/client.Test.yaml | 2 +- karma/coverage-reporter.js | 7 + karma/custom-launchers.js | 6 + karma/junit-reporter.js | 10 + libs/shared/src/test.ts | 4 +- libs/sso/src/lib/sso.service.ts | 2 +- libs/sso/src/test.ts | 4 +- libs/sso/tsconfig.lib.json | 1 + libs/sso/tsconfig.lib.prod.json | 3 + .../src/lib/button/button.component.spec.ts | 14 +- libs/ui/src/lib/card/card.component.spec.ts | 14 +- .../lib/checkbox/checkbox.component.spec.ts | 14 +- .../date-picker/date-picker.component.spec.ts | 14 +- .../delete-dropdown-fixed.component.spec.ts | 14 +- .../delete-dropdown.component.spec.ts | 14 +- .../double-choice-switch.component.spec.ts | 14 +- .../lib/dropdown/dropdown.component.spec.ts | 14 +- libs/ui/src/lib/icon/icon.component.spec.ts | 14 +- libs/ui/src/lib/input/input.component.spec.ts | 14 +- .../src/lib/loading/loading.component.spec.ts | 14 +- libs/ui/src/lib/modal/modal.component.spec.ts | 14 +- .../photo-gallery.component.spec.ts | 14 +- .../progress-bar.component.spec.ts | 14 +- .../radio-button-group.component.spec.ts | 14 +- .../radio-button.component.spec.ts | 14 +- .../search-dropdown.component.spec.ts | 14 +- .../search-input.component.spec.ts | 14 +- .../src/lib/select/select.component.spec.ts | 14 +- ...all-double-choice-switch.component.spec.ts | 14 +- libs/ui/src/lib/tag/tag.component.spec.ts | 14 +- libs/ui/src/lib/tag/tag.component.ts | 2 +- libs/ui/src/test.ts | 4 +- libs/ui/tsconfig.lib.json | 1 + libs/ui/tsconfig.lib.prod.json | 3 + package-lock.json | 14881 ++++++++-------- package.json | 96 +- tailwind.config.js | 13 +- tsconfig.json | 41 + 998 files changed, 25284 insertions(+), 12719 deletions(-) create mode 100644 apps/adapter/scan/README.md create mode 100644 apps/adapter/scan/karma.conf.js create mode 100644 apps/adapter/scan/ng-package.json create mode 100644 apps/adapter/scan/package.json create mode 100644 apps/adapter/scan/src/lib/dev.scan-adapter.ts create mode 100644 apps/adapter/scan/src/lib/native.scan-adapter.ts create mode 100644 apps/adapter/scan/src/lib/scan-adapter.ts create mode 100644 apps/adapter/scan/src/lib/scan.module.ts create mode 100644 apps/adapter/scan/src/lib/scan.service.ts create mode 100644 apps/adapter/scan/src/lib/tokens.ts create mode 100644 apps/adapter/scan/src/public-api.ts create mode 100644 apps/adapter/scan/src/test.ts create mode 100644 apps/adapter/scan/tsconfig.lib.json create mode 100644 apps/adapter/scan/tsconfig.lib.prod.json create mode 100644 apps/adapter/scan/tsconfig.spec.json delete mode 100644 apps/core/application/src/lib/process.service.ts create mode 100644 apps/core/application/src/lib/store/application.reducer.spec.ts create mode 100644 apps/core/application/src/lib/store/application.selectors.spec.ts create mode 100644 apps/core/auth/README.md create mode 100644 apps/core/auth/karma.conf.js create mode 100644 apps/core/auth/ng-package.json create mode 100644 apps/core/auth/package.json create mode 100644 apps/core/auth/src/lib/auth.module.ts create mode 100644 apps/core/auth/src/lib/auth.service.spec.ts create mode 100644 apps/core/auth/src/lib/auth.service.ts create mode 100644 apps/core/auth/src/lib/index.ts create mode 100644 apps/core/auth/src/public-api.ts create mode 100644 apps/core/auth/src/test.ts create mode 100644 apps/core/auth/tsconfig.lib.json create mode 100644 apps/core/auth/tsconfig.lib.prod.json create mode 100644 apps/core/auth/tsconfig.spec.json create mode 100644 apps/core/config/README.md create mode 100644 apps/core/config/karma.conf.js create mode 100644 apps/core/config/ng-package.json create mode 100644 apps/core/config/package.json create mode 100644 apps/core/config/src/lib/config-loaders/config-loader.ts create mode 100644 apps/core/config/src/lib/config-loaders/index.ts create mode 100644 apps/core/config/src/lib/config-loaders/json.config-loader.spec.ts create mode 100644 apps/core/config/src/lib/config-loaders/json.config-loader.ts create mode 100644 apps/core/config/src/lib/config-module-options.ts create mode 100644 apps/core/config/src/lib/config.module.ts create mode 100644 apps/core/config/src/lib/config.spec.ts create mode 100644 apps/core/config/src/lib/config.ts create mode 100644 apps/core/config/src/lib/index.ts create mode 100644 apps/core/config/src/lib/tokens.ts create mode 100644 apps/core/config/src/lib/utils/index.ts create mode 100644 apps/core/config/src/lib/utils/pick.spec.ts create mode 100644 apps/core/config/src/lib/utils/pick.ts create mode 100644 apps/core/config/src/public-api.ts create mode 100644 apps/core/config/src/test.ts create mode 100644 apps/core/config/tsconfig.lib.json create mode 100644 apps/core/config/tsconfig.lib.prod.json create mode 100644 apps/core/config/tsconfig.spec.json create mode 100644 apps/core/logger/README.md create mode 100644 apps/core/logger/karma.conf.js create mode 100644 apps/core/logger/ng-package.json create mode 100644 apps/core/logger/package.json create mode 100644 apps/core/logger/src/lib/console-log.provider.spec.ts create mode 100644 apps/core/logger/src/lib/console-log.provider.ts create mode 100644 apps/core/logger/src/lib/index.ts create mode 100644 apps/core/logger/src/lib/log-level.ts create mode 100644 apps/core/logger/src/lib/log.provider.ts create mode 100644 apps/core/logger/src/lib/logger.module.ts create mode 100644 apps/core/logger/src/lib/logger.service.spec.ts create mode 100644 apps/core/logger/src/lib/logger.service.ts create mode 100644 apps/core/logger/src/lib/tokens.ts create mode 100644 apps/core/logger/src/public-api.ts create mode 100644 apps/core/logger/src/test.ts create mode 100644 apps/core/logger/tsconfig.lib.json create mode 100644 apps/core/logger/tsconfig.lib.prod.json create mode 100644 apps/core/logger/tsconfig.spec.json create mode 100644 apps/domain/isa/README.md create mode 100644 apps/domain/isa/karma.conf.js create mode 100644 apps/domain/isa/ng-package.json create mode 100644 apps/domain/isa/package.json create mode 100644 apps/domain/isa/src/lib/dashboard.service.ts create mode 100644 apps/domain/isa/src/lib/defs/index.ts create mode 100644 apps/domain/isa/src/lib/defs/kpi-feed-item.ts create mode 100644 apps/domain/isa/src/lib/defs/kpi-feed.ts create mode 100644 apps/domain/isa/src/lib/defs/products-feed.ts create mode 100644 apps/domain/isa/src/lib/domain-isa.module.ts create mode 100644 apps/domain/isa/src/lib/index.ts create mode 100644 apps/domain/isa/src/public-api.ts create mode 100644 apps/domain/isa/src/test.ts create mode 100644 apps/domain/isa/tsconfig.lib.json create mode 100644 apps/domain/isa/tsconfig.lib.prod.json create mode 100644 apps/domain/isa/tsconfig.spec.json create mode 100644 apps/domain/remission/README.md create mode 100644 apps/domain/remission/karma.conf.js create mode 100644 apps/domain/remission/ng-package.json create mode 100644 apps/domain/remission/package.json create mode 100644 apps/domain/remission/src/lib/defs/index.ts create mode 100644 apps/domain/remission/src/lib/defs/remission-list-item.ts create mode 100644 apps/domain/remission/src/lib/index.ts create mode 100644 apps/domain/remission/src/lib/mappings/index.ts create mode 100644 apps/domain/remission/src/lib/mappings/remission-list-item.mapping.ts create mode 100644 apps/domain/remission/src/lib/mappings/return-item-dto.mapping.ts create mode 100644 apps/domain/remission/src/lib/remission.module.ts create mode 100644 apps/domain/remission/src/lib/remission.service.spec.ts create mode 100644 apps/domain/remission/src/lib/remission.service.ts create mode 100644 apps/domain/remission/src/public-api.ts create mode 100644 apps/domain/remission/src/test.ts create mode 100644 apps/domain/remission/tsconfig.lib.json create mode 100644 apps/domain/remission/tsconfig.lib.prod.json create mode 100644 apps/domain/remission/tsconfig.spec.json create mode 100644 apps/isa-app/.browserslistrc create mode 100644 apps/isa-app/karma.conf.js create mode 100644 apps/isa-app/ngsw-config.json create mode 100644 apps/isa-app/src/app/app-domain.module.ts create mode 100644 apps/isa-app/src/app/app-routing.module.ts create mode 100644 apps/isa-app/src/app/app-store.module.ts create mode 100644 apps/isa-app/src/app/app-swagger.module.ts create mode 100644 apps/isa-app/src/app/app.component.html create mode 100644 apps/isa-app/src/app/app.component.scss create mode 100644 apps/isa-app/src/app/app.component.spec.ts create mode 100644 apps/isa-app/src/app/app.component.ts create mode 100644 apps/isa-app/src/app/app.module.ts create mode 100644 apps/isa-app/src/app/guards/can-activate-cart-with-process-id.guard.ts create mode 100644 apps/isa-app/src/app/guards/can-activate-cart.guard.ts create mode 100644 apps/isa-app/src/app/guards/can-activate-customer-with-process-id.guard.ts create mode 100644 apps/isa-app/src/app/guards/can-activate-customer.guard.ts create mode 100644 apps/isa-app/src/app/guards/can-activate-goods-in.guard.ts create mode 100644 apps/isa-app/src/app/guards/can-activate-goods-out.guard.ts create mode 100644 apps/isa-app/src/app/guards/can-activate-product-with-process-id.guard.ts create mode 100644 apps/isa-app/src/app/guards/can-activate-product.guard.ts create mode 100644 apps/isa-app/src/app/guards/can-activate-remission.guard.ts create mode 100644 apps/isa-app/src/app/guards/can-activate-task-calendar.guard.ts create mode 100644 apps/isa-app/src/app/guards/index.ts create mode 100644 apps/isa-app/src/app/guards/init-store.guard.ts create mode 100644 apps/isa-app/src/app/guards/is-authenticated.guard.ts create mode 100644 apps/isa-app/src/app/interceptors/http-error.interceptor.spec.ts create mode 100644 apps/isa-app/src/app/interceptors/http-error.interceptor.ts create mode 100644 apps/isa-app/src/app/interceptors/index.ts create mode 100644 apps/isa-app/src/app/providers/index.ts create mode 100644 apps/isa-app/src/app/providers/isa.error-handler.ts create mode 100644 apps/isa-app/src/app/providers/isa.log-provider.ts create mode 100644 apps/isa-app/src/app/resolvers/index.ts create mode 100644 apps/isa-app/src/app/resolvers/process-id.resolver.ts create mode 100644 apps/isa-app/src/app/resolvers/section.resolver.ts create mode 100644 apps/isa-app/src/app/shell/index.ts create mode 100644 apps/isa-app/src/app/shell/shell.component.html create mode 100644 apps/isa-app/src/app/shell/shell.component.scss create mode 100644 apps/isa-app/src/app/shell/shell.component.spec.ts create mode 100644 apps/isa-app/src/app/shell/shell.component.ts create mode 100644 apps/isa-app/src/app/shell/shell.module.ts create mode 100644 apps/isa-app/src/app/store/root-state.service.ts create mode 100644 apps/isa-app/src/app/store/root.reducer.ts create mode 100644 apps/isa-app/src/app/store/root.state.ts create mode 100644 apps/isa-app/src/assets/.gitkeep create mode 100644 apps/isa-app/src/assets/icons.svg create mode 100644 apps/isa-app/src/assets/icons/icon-144x144.png create mode 100644 apps/isa-app/src/assets/icons/icon-192x192.png create mode 100644 apps/isa-app/src/assets/icons/icon-512x512.png create mode 100644 apps/isa-app/src/assets/icons/icon-72x72.png create mode 100644 apps/isa-app/src/assets/icons/icon-96x96.png create mode 100644 apps/isa-app/src/assets/images/Hugendubel_Logo.png create mode 100644 apps/isa-app/src/assets/images/Icon_AU.svg create mode 100644 apps/isa-app/src/assets/images/Icon_DL.svg create mode 100644 apps/isa-app/src/assets/images/Icon_EB.svg create mode 100644 apps/isa-app/src/assets/images/Icon_GEB.svg create mode 100644 apps/isa-app/src/assets/images/Icon_HC.svg create mode 100644 apps/isa-app/src/assets/images/Icon_KA.svg create mode 100644 apps/isa-app/src/assets/images/Icon_KT.svg create mode 100644 apps/isa-app/src/assets/images/Icon_MA.svg create mode 100644 apps/isa-app/src/assets/images/Icon_NB.svg create mode 100644 apps/isa-app/src/assets/images/Icon_SW.svg create mode 100644 apps/isa-app/src/assets/images/Icon_TB.svg create mode 100644 apps/isa-app/src/assets/images/Icon_VI.svg create mode 100644 apps/isa-app/src/assets/images/Icon_ZS.svg create mode 100644 apps/isa-app/src/assets/images/OF_Icon_AU.svg create mode 100644 apps/isa-app/src/assets/images/OF_Icon_DL.svg create mode 100644 apps/isa-app/src/assets/images/OF_Icon_EB.svg create mode 100644 apps/isa-app/src/assets/images/OF_Icon_HC.svg create mode 100644 apps/isa-app/src/assets/images/OF_Icon_KA.svg create mode 100644 apps/isa-app/src/assets/images/OF_Icon_MA.svg create mode 100644 apps/isa-app/src/assets/images/OF_Icon_NB.svg create mode 100644 apps/isa-app/src/assets/images/OF_Icon_PP.svg create mode 100644 apps/isa-app/src/assets/images/OF_Icon_SW.svg create mode 100644 apps/isa-app/src/assets/images/OF_Icon_TB.svg create mode 100644 apps/isa-app/src/assets/images/OF_Icon_VI.svg create mode 100644 apps/isa-app/src/assets/images/OF_Icon_ZS.svg create mode 100644 apps/isa-app/src/assets/images/Star_empty.png create mode 100644 apps/isa-app/src/assets/images/Star_full.png create mode 100644 apps/isa-app/src/assets/images/Star_half.png create mode 100644 apps/isa-app/src/assets/images/barcode.png create mode 100644 apps/isa-app/src/assets/images/bookmark_benachrichtigung_archiv.svg create mode 100644 apps/isa-app/src/assets/images/bookmark_branch.svg create mode 100644 apps/isa-app/src/assets/images/bookmark_customer.svg create mode 100644 apps/isa-app/src/assets/images/bookmark_subscription.svg create mode 100644 apps/isa-app/src/assets/images/checkbox.svg create mode 100644 apps/isa-app/src/assets/images/checkbox_checked.svg create mode 100644 apps/isa-app/src/assets/images/email_bookmark.svg create mode 100644 apps/isa-app/src/assets/images/hugendubel-logo.png create mode 100644 apps/isa-app/src/assets/images/radio.svg create mode 100644 apps/isa-app/src/assets/images/radio_checked.svg create mode 100644 apps/isa-app/src/assets/images/recommendation_tag.png create mode 100644 apps/isa-app/src/assets/images/sms_bookmark.svg create mode 100644 apps/isa-app/src/assets/images/spinner.svg create mode 100644 apps/isa-app/src/assets/images/tag_icon_preorder.svg create mode 100644 apps/isa-app/src/config/config.feature.json create mode 100644 apps/isa-app/src/config/config.integration.json create mode 100644 apps/isa-app/src/config/config.json create mode 100644 apps/isa-app/src/config/config.production.json create mode 100644 apps/isa-app/src/config/config.staging.json create mode 100644 apps/isa-app/src/config/config.test.json create mode 100644 apps/isa-app/src/environments/environment.prod.ts create mode 100644 apps/isa-app/src/environments/environment.ts create mode 100644 apps/isa-app/src/favicon.ico create mode 100644 apps/isa-app/src/index.html create mode 100644 apps/isa-app/src/main.ts create mode 100644 apps/isa-app/src/manifest.webmanifest create mode 100644 apps/isa-app/src/polyfills.ts create mode 100644 apps/isa-app/src/scss/_branch.scss create mode 100644 apps/isa-app/src/scss/_customer.scss create mode 100644 apps/isa-app/src/scss/_root.scss create mode 100644 apps/isa-app/src/silent-refresh.html create mode 100644 apps/isa-app/src/styles.scss create mode 100644 apps/isa-app/src/test.ts create mode 100644 apps/isa-app/tsconfig.app.json create mode 100644 apps/isa-app/tsconfig.spec.json create mode 100644 apps/modal/notifications/src/lib/notifications-list-item/notifications-list-item.component.spec.ts create mode 100644 apps/modal/notifications/src/lib/notifications-remission-group/notifications-remission-group.component.spec.ts create mode 100644 apps/modal/notifications/src/lib/notifications-reservation-group/notifications-reservation-group.component.spec.ts create mode 100644 apps/modal/notifications/src/lib/notifications-task-calendar-group/notifications-task-calendar-group.component.spec.ts create mode 100644 apps/modal/notifications/src/lib/notifications.component.spec.ts delete mode 100644 apps/page/catalog/src/lib/article-search/article-search-new.store.ts delete mode 100644 apps/page/catalog/src/lib/article-search/containers/article-searchbox/article-searchbox.component.html delete mode 100644 apps/page/catalog/src/lib/article-search/containers/article-searchbox/article-searchbox.component.scss delete mode 100644 apps/page/catalog/src/lib/article-search/containers/article-searchbox/article-searchbox.component.ts delete mode 100644 apps/page/catalog/src/lib/article-search/containers/article-searchbox/article-searchbox.module.ts delete mode 100644 apps/page/catalog/src/lib/article-search/containers/filter-chips/filter-chips.component.html delete mode 100644 apps/page/catalog/src/lib/article-search/containers/filter-chips/filter-chips.component.scss delete mode 100644 apps/page/catalog/src/lib/article-search/containers/filter-chips/filter-chips.component.ts delete mode 100644 apps/page/catalog/src/lib/article-search/containers/filter-chips/filter-chips.module.ts delete mode 100644 apps/page/checkout/src/lib/checkout-review/notification-channels/notification-channels.component.html delete mode 100644 apps/page/checkout/src/lib/checkout-review/notification-channels/notification-channels.component.scss delete mode 100644 apps/page/checkout/src/lib/checkout-review/notification-channels/notification-channels.component.ts delete mode 100644 apps/page/checkout/src/lib/checkout-review/notification-channels/notification-channels.service.ts delete mode 100644 apps/page/checkout/src/lib/checkout-review/notification-channels/notification-edit/notification-checkbox.component.html delete mode 100644 apps/page/checkout/src/lib/checkout-review/notification-channels/notification-edit/notification-checkbox.component.scss delete mode 100644 apps/page/checkout/src/lib/checkout-review/notification-channels/notification-edit/notification-checkbox.component.ts delete mode 100644 apps/page/checkout/src/lib/checkout-review/notification-channels/notification-edit/notification-edit.component.html delete mode 100644 apps/page/checkout/src/lib/checkout-review/notification-channels/notification-edit/notification-edit.component.scss delete mode 100644 apps/page/checkout/src/lib/checkout-review/notification-channels/notification-edit/notification-edit.component.ts delete mode 100644 apps/page/customer/src/lib/customer-search/customer-search.service.spec.ts delete mode 100644 apps/page/customer/src/lib/customer-search/customer-search.service.ts create mode 100644 apps/page/customer/src/lib/customer-search/providers/customer-search-main-autocomplete.provider.spec.ts create mode 100644 apps/page/customer/src/lib/customer-search/providers/customer-search-main-autocomplete.provider.ts delete mode 100644 apps/page/customer/src/lib/customer-search/search-main/search-main.component.spec.ts delete mode 100644 apps/page/customer/src/lib/customer-search/search-results/search-result.component.spec.ts create mode 100644 apps/page/customer/src/lib/customer-search/services/customer.search-state-search-service.spec.ts create mode 100644 apps/page/customer/src/lib/customer-search/services/customer.search-state-search-service.ts create mode 100644 apps/page/customer/src/lib/customer-search/services/customer.search-state-settings-loader.spec.ts create mode 100644 apps/page/customer/src/lib/customer-search/services/customer.search-state-settings-loader.ts delete mode 100644 apps/page/customer/src/lib/customer-search/shared/customer-searchbox/customer-searchbox.component.html delete mode 100644 apps/page/customer/src/lib/customer-search/shared/customer-searchbox/customer-searchbox.component.scss delete mode 100644 apps/page/customer/src/lib/customer-search/shared/customer-searchbox/customer-searchbox.component.spec.ts delete mode 100644 apps/page/customer/src/lib/customer-search/shared/customer-searchbox/customer-searchbox.component.ts delete mode 100644 apps/page/customer/src/lib/customer-search/shared/customer-searchbox/index.ts create mode 100644 apps/page/dashboard/README.md create mode 100644 apps/page/dashboard/karma.conf.js create mode 100644 apps/page/dashboard/ng-package.json create mode 100644 apps/page/dashboard/package.json create mode 100644 apps/page/dashboard/src/lib/dashboard-routing-module.ts create mode 100644 apps/page/dashboard/src/lib/dashboard.component.html create mode 100644 apps/page/dashboard/src/lib/dashboard.component.scss create mode 100644 apps/page/dashboard/src/lib/dashboard.component.spec.ts create mode 100644 apps/page/dashboard/src/lib/dashboard.component.ts create mode 100644 apps/page/dashboard/src/lib/dashboard.module.ts create mode 100644 apps/page/dashboard/src/lib/kpi-card/kpi-card.component.html create mode 100644 apps/page/dashboard/src/lib/kpi-card/kpi-card.component.scss create mode 100644 apps/page/dashboard/src/lib/kpi-card/kpi-card.component.spec.ts create mode 100644 apps/page/dashboard/src/lib/kpi-card/kpi-card.component.ts create mode 100644 apps/page/dashboard/src/lib/products-card/product-card.component.html create mode 100644 apps/page/dashboard/src/lib/products-card/product-card.component.scss create mode 100644 apps/page/dashboard/src/lib/products-card/product-card.component.spec.ts create mode 100644 apps/page/dashboard/src/lib/products-card/product-card.component.ts create mode 100644 apps/page/dashboard/src/public-api.ts create mode 100644 apps/page/dashboard/src/test.ts create mode 100644 apps/page/dashboard/tsconfig.lib.json create mode 100644 apps/page/dashboard/tsconfig.lib.prod.json create mode 100644 apps/page/dashboard/tsconfig.spec.json create mode 100644 apps/page/remission/README.md create mode 100644 apps/page/remission/karma.conf.js create mode 100644 apps/page/remission/ng-package.json create mode 100644 apps/page/remission/package.json create mode 100644 apps/page/remission/src/lib/add-product/add-product.component.html create mode 100644 apps/page/remission/src/lib/add-product/add-product.component.scss create mode 100644 apps/page/remission/src/lib/add-product/add-product.component.ts create mode 100644 apps/page/remission/src/lib/add-product/add-product.module.ts create mode 100644 apps/page/remission/src/lib/add-product/index.ts create mode 100644 apps/page/remission/src/lib/create-remission/create-remission.component.html create mode 100644 apps/page/remission/src/lib/create-remission/create-remission.component.ts create mode 100644 apps/page/remission/src/lib/create-remission/create-remission.module.ts create mode 100644 apps/page/remission/src/lib/finish-remission/finish-remission.component.html create mode 100644 apps/page/remission/src/lib/finish-remission/finish-remission.component.scss create mode 100644 apps/page/remission/src/lib/finish-remission/finish-remission.component.ts create mode 100644 apps/page/remission/src/lib/finish-remission/finish-remission.module.ts create mode 100644 apps/page/remission/src/lib/finish-shipping-document/finish-shipping-document.component.html create mode 100644 apps/page/remission/src/lib/finish-shipping-document/finish-shipping-document.component.scss create mode 100644 apps/page/remission/src/lib/finish-shipping-document/finish-shipping-document.component.ts create mode 100644 apps/page/remission/src/lib/finish-shipping-document/finish-shipping-document.module.ts create mode 100644 apps/page/remission/src/lib/index.ts create mode 100644 apps/page/remission/src/lib/modals/add-product-modal/add-product-modal.component.html create mode 100644 apps/page/remission/src/lib/modals/add-product-modal/add-product-modal.component.scss create mode 100644 apps/page/remission/src/lib/modals/add-product-modal/add-product-modal.component.ts create mode 100644 apps/page/remission/src/lib/modals/add-product-modal/add-product-modal.data.ts create mode 100644 apps/page/remission/src/lib/modals/add-product-modal/add-product-modal.module.ts create mode 100644 apps/page/remission/src/lib/modals/add-product-modal/index.ts create mode 100644 apps/page/remission/src/lib/modals/add-product-to-shipping-document-modal/add-product-to-shipping-document-modal.component.html create mode 100644 apps/page/remission/src/lib/modals/add-product-to-shipping-document-modal/add-product-to-shipping-document-modal.component.scss create mode 100644 apps/page/remission/src/lib/modals/add-product-to-shipping-document-modal/add-product-to-shipping-document-modal.component.ts create mode 100644 apps/page/remission/src/lib/modals/add-product-to-shipping-document-modal/add-product-to-shipping-document-modal.module.ts create mode 100644 apps/page/remission/src/lib/pipes/assortment.pipe.ts create mode 100644 apps/page/remission/src/lib/pipes/index.ts create mode 100644 apps/page/remission/src/lib/pipes/product-group.pipe.ts create mode 100644 apps/page/remission/src/lib/pipes/remission-pipe.module.ts create mode 100644 apps/page/remission/src/lib/pipes/short-receipt-number.pipe.ts create mode 100644 apps/page/remission/src/lib/pipes/supplier.pipe.ts create mode 100644 apps/page/remission/src/lib/providers/add-product-scan.provider.ts create mode 100644 apps/page/remission/src/lib/providers/create-remission-scan.provider.ts create mode 100644 apps/page/remission/src/lib/providers/finish-shipping-document-scan.provider.ts create mode 100644 apps/page/remission/src/lib/providers/index.ts create mode 100644 apps/page/remission/src/lib/remission-list/index.ts create mode 100644 apps/page/remission/src/lib/remission-list/remission-filter/remission-filter.component.html create mode 100644 apps/page/remission/src/lib/remission-list/remission-filter/remission-filter.component.scss create mode 100644 apps/page/remission/src/lib/remission-list/remission-filter/remission-filter.component.ts create mode 100644 apps/page/remission/src/lib/remission-list/remission-filter/remission-filter.module.ts create mode 100644 apps/page/remission/src/lib/remission-list/remission-list-item/index.ts create mode 100644 apps/page/remission/src/lib/remission-list/remission-list-item/remission-list-item.component.html create mode 100644 apps/page/remission/src/lib/remission-list/remission-list-item/remission-list-item.component.scss create mode 100644 apps/page/remission/src/lib/remission-list/remission-list-item/remission-list-item.component.ts create mode 100644 apps/page/remission/src/lib/remission-list/remission-list-item/remission-list-item.module.ts create mode 100644 apps/page/remission/src/lib/remission-list/remission-list.component-store.ts create mode 100644 apps/page/remission/src/lib/remission-list/remission-list.component.html create mode 100644 apps/page/remission/src/lib/remission-list/remission-list.component.scss create mode 100644 apps/page/remission/src/lib/remission-list/remission-list.component.ts create mode 100644 apps/page/remission/src/lib/remission-list/remission-list.module.ts create mode 100644 apps/page/remission/src/lib/remission-list/remission.component-store.ts create mode 100644 apps/page/remission/src/lib/remission-routing.module.ts create mode 100644 apps/page/remission/src/lib/remission.component.html create mode 100644 apps/page/remission/src/lib/remission.component.scss create mode 100644 apps/page/remission/src/lib/remission.component.spec.ts create mode 100644 apps/page/remission/src/lib/remission.component.ts create mode 100644 apps/page/remission/src/lib/remission.module.ts create mode 100644 apps/page/remission/src/lib/required-capacities/required-capacities.component.html create mode 100644 apps/page/remission/src/lib/required-capacities/required-capacities.component.scss create mode 100644 apps/page/remission/src/lib/required-capacities/required-capacities.component.ts create mode 100644 apps/page/remission/src/lib/required-capacities/required-capacities.module.ts create mode 100644 apps/page/remission/src/lib/shared/shipping-document-details/shipping-document-details-item/shipping-document-details-item.component.html create mode 100644 apps/page/remission/src/lib/shared/shipping-document-details/shipping-document-details-item/shipping-document-details-item.component.scss create mode 100644 apps/page/remission/src/lib/shared/shipping-document-details/shipping-document-details-item/shipping-document-details-item.component.ts create mode 100644 apps/page/remission/src/lib/shared/shipping-document-details/shipping-document-details.component.html create mode 100644 apps/page/remission/src/lib/shared/shipping-document-details/shipping-document-details.component.scss create mode 100644 apps/page/remission/src/lib/shared/shipping-document-details/shipping-document-details.component.ts create mode 100644 apps/page/remission/src/lib/shared/shipping-document-details/shipping-document-details.module.ts create mode 100644 apps/page/remission/src/lib/shipping-document-list/shipping-document-details/shipping-document-details.component.html create mode 100644 apps/page/remission/src/lib/shipping-document-list/shipping-document-details/shipping-document-details.component.scss create mode 100644 apps/page/remission/src/lib/shipping-document-list/shipping-document-details/shipping-document-details.component.ts create mode 100644 apps/page/remission/src/lib/shipping-document-list/shipping-document-list-item/shipping-document-list-item.component.html create mode 100644 apps/page/remission/src/lib/shipping-document-list/shipping-document-list-item/shipping-document-list-item.component.scss create mode 100644 apps/page/remission/src/lib/shipping-document-list/shipping-document-list-item/shipping-document-list-item.component.ts create mode 100644 apps/page/remission/src/lib/shipping-document-list/shipping-document-list.component.html create mode 100644 apps/page/remission/src/lib/shipping-document-list/shipping-document-list.component.scss create mode 100644 apps/page/remission/src/lib/shipping-document-list/shipping-document-list.component.ts create mode 100644 apps/page/remission/src/lib/shipping-document-list/shipping-document-list.module.ts create mode 100644 apps/page/remission/src/public-api.ts create mode 100644 apps/page/remission/src/test.ts create mode 100644 apps/page/remission/tsconfig.lib.json create mode 100644 apps/page/remission/tsconfig.lib.prod.json create mode 100644 apps/page/remission/tsconfig.spec.json create mode 100644 apps/shell/filter-overlay/README.md create mode 100644 apps/shell/filter-overlay/karma.conf.js create mode 100644 apps/shell/filter-overlay/ng-package.json create mode 100644 apps/shell/filter-overlay/package.json create mode 100644 apps/shell/filter-overlay/src/lib/filter-overlay.component.html create mode 100644 apps/shell/filter-overlay/src/lib/filter-overlay.component.scss create mode 100644 apps/shell/filter-overlay/src/lib/filter-overlay.component.spec.ts create mode 100644 apps/shell/filter-overlay/src/lib/filter-overlay.component.ts create mode 100644 apps/shell/filter-overlay/src/lib/filter-overlay.module.ts create mode 100644 apps/shell/filter-overlay/src/public-api.ts create mode 100644 apps/shell/filter-overlay/src/test.ts create mode 100644 apps/shell/filter-overlay/tsconfig.lib.json create mode 100644 apps/shell/filter-overlay/tsconfig.lib.prod.json create mode 100644 apps/shell/filter-overlay/tsconfig.spec.json create mode 100644 apps/shell/footer/README.md create mode 100644 apps/shell/footer/karma.conf.js create mode 100644 apps/shell/footer/ng-package.json create mode 100644 apps/shell/footer/package.json create mode 100644 apps/shell/footer/src/lib/footer.component.html create mode 100644 apps/shell/footer/src/lib/footer.component.scss create mode 100644 apps/shell/footer/src/lib/footer.component.spec.ts create mode 100644 apps/shell/footer/src/lib/footer.component.ts create mode 100644 apps/shell/footer/src/lib/footer.module.ts create mode 100644 apps/shell/footer/src/public-api.ts create mode 100644 apps/shell/footer/src/test.ts create mode 100644 apps/shell/footer/tsconfig.lib.json create mode 100644 apps/shell/footer/tsconfig.lib.prod.json create mode 100644 apps/shell/footer/tsconfig.spec.json create mode 100644 apps/shell/header/src/lib/header.component.spec.ts create mode 100644 apps/shell/header/src/lib/section-toggle/section-toggle.component.spec.ts create mode 100644 apps/shell/process/README.md create mode 100644 apps/shell/process/karma.conf.js create mode 100644 apps/shell/process/ng-package.json create mode 100644 apps/shell/process/package.json create mode 100644 apps/shell/process/src/lib/process-tab/process-tab.component.html create mode 100644 apps/shell/process/src/lib/process-tab/process-tab.component.scss create mode 100644 apps/shell/process/src/lib/process-tab/process-tab.component.spec.ts create mode 100644 apps/shell/process/src/lib/process-tab/process-tab.component.ts create mode 100644 apps/shell/process/src/lib/process.component.html create mode 100644 apps/shell/process/src/lib/process.component.scss create mode 100644 apps/shell/process/src/lib/process.component.spec.ts create mode 100644 apps/shell/process/src/lib/process.component.ts create mode 100644 apps/shell/process/src/lib/process.module.ts create mode 100644 apps/shell/process/src/public-api.ts create mode 100644 apps/shell/process/src/test.ts create mode 100644 apps/shell/process/tsconfig.lib.json create mode 100644 apps/shell/process/tsconfig.lib.prod.json create mode 100644 apps/shell/process/tsconfig.spec.json create mode 100644 apps/store/search-component-store/README.md create mode 100644 apps/store/search-component-store/karma.conf.js create mode 100644 apps/store/search-component-store/ng-package.json create mode 100644 apps/store/search-component-store/package.json create mode 100644 apps/store/search-component-store/src/lib/defs/index.ts create mode 100644 apps/store/search-component-store/src/lib/defs/search-settings-loader.ts create mode 100644 apps/store/search-component-store/src/lib/defs/search-state-search-result.ts create mode 100644 apps/store/search-component-store/src/lib/defs/search-state-search-service.ts create mode 100644 apps/store/search-component-store/src/lib/defs/search-state.ts create mode 100644 apps/store/search-component-store/src/lib/index.ts create mode 100644 apps/store/search-component-store/src/lib/search-component-store.module.ts create mode 100644 apps/store/search-component-store/src/lib/search-component-store.service.spec.ts create mode 100644 apps/store/search-component-store/src/lib/search-component-store.service.ts create mode 100644 apps/store/search-component-store/src/lib/tokens.ts create mode 100644 apps/store/search-component-store/src/public-api.ts create mode 100644 apps/store/search-component-store/src/test.ts create mode 100644 apps/store/search-component-store/tsconfig.lib.json create mode 100644 apps/store/search-component-store/tsconfig.lib.prod.json create mode 100644 apps/store/search-component-store/tsconfig.spec.json create mode 100644 apps/swagger/crm/src/lib/models/input-group-dto.ts create mode 100644 apps/swagger/crm/src/lib/models/query-settings-dto.ts create mode 100644 apps/swagger/crm/src/lib/models/response-args-of-query-settings-dto.ts create mode 100644 apps/swagger/eis/src/lib/models/input-group-dto.ts create mode 100644 apps/swagger/eis/src/lib/models/query-settings-dto.ts create mode 100644 apps/swagger/eis/src/lib/models/response-args-of-query-settings-dto.ts create mode 100644 apps/swagger/eis/src/lib/models/touched-base.ts create mode 100644 apps/swagger/remi/src/lib/models/input-group-dto.ts delete mode 100644 apps/swagger/remi/src/lib/models/iresult-of-return-item-dto.ts create mode 100644 apps/swagger/remi/src/lib/models/query-settings-dto.ts create mode 100644 apps/swagger/remi/src/lib/models/response-args-of-query-settings-dto.ts create mode 100644 apps/swagger/remi/src/lib/models/return-value-of-return-item-dto.ts rename apps/swagger/remi/src/lib/models/{iresult-of-return-suggestion-dto.ts => return-value-of-return-suggestion-dto.ts} (50%) create mode 100644 apps/swagger/remi/src/lib/models/return-value.ts create mode 100644 apps/ui/modal/src/lib/confirm-modal/confirm-modal.component.scss create mode 100644 apps/ui/modal/src/lib/confirm-modal/confirm-modal.component.ts create mode 100644 apps/ui/modal/src/lib/confirm-modal/confirm-modal.data.ts create mode 100644 apps/ui/modal/src/lib/confirm-modal/index.ts create mode 100644 apps/ui/modal/src/lib/prompt-modal/index.ts create mode 100644 apps/ui/modal/src/lib/prompt-modal/prompt-modal.component.scss create mode 100644 apps/ui/modal/src/lib/prompt-modal/prompt-modal.component.ts create mode 100644 apps/ui/modal/src/lib/prompt-modal/prompt-modal.data.ts create mode 100644 apps/ui/spinner/src/lib/index.ts delete mode 100644 bitbucket-pipelines.yml delete mode 100644 debug.log create mode 100644 karma/coverage-reporter.js create mode 100644 karma/custom-launchers.js create mode 100644 karma/junit-reporter.js diff --git a/.gitignore b/.gitignore index 717ccb66b..fedb85165 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,7 @@ speed-measure-plugin.json /.sass-cache /connect.lock /coverage +/testresults /libpeerconnection.log npm-debug.log yarn-error.log diff --git a/.vscode/extensions.json b/.vscode/extensions.json index c24553a2a..29ef4c718 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -3,7 +3,6 @@ "johnpapa.angular2", "esbenp.prettier-vscode", "angular.ng-template", - "ms-vscode.vscode-typescript-tslint-plugin", "eg2.vscode-npm-script" ] -} +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 44ba0bb27..1a1a19a6b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ #stage 1 -FROM node:14 as node +FROM node:14 as base ARG IS_PRODUCTION=false ARG SEMVERSION=1.0.0 ARG BuildUniqueID @@ -11,9 +11,19 @@ RUN npm version ${SEMVERSION} RUN npm install --always-auth=false RUN if [ "${IS_PRODUCTION}" = "true" ] ; then npm run-script build-prod ; else npm run-script build ; fi -# stage 2 -FROM nginx:alpine +# stage final +FROM nginx:alpine as publish ARG BuildUniqueID LABEL build.uniqueid="${BuildUniqueID:-1}" -COPY --from=node /app/dist/sales /usr/share/nginx/html -COPY --from=node /app/nginx.conf /etc/nginx/conf.d/default.conf +COPY --from=base /app/dist/isa-app /usr/share/nginx/html +COPY --from=base /app/nginx.conf /etc/nginx/conf.d/default.conf + +# stage npm test +FROM base as test +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 +ENTRYPOINT [ "/bin/sleep", "60000" ] + diff --git a/angular.json b/angular.json index ad4ab9718..2fecfbc06 100644 --- a/angular.json +++ b/angular.json @@ -10,7 +10,7 @@ "prefix": "lib", "architect": { "build": { - "builder": "@angular-devkit/build-ng-packagr:build", + "builder": "@angular-devkit/build-angular:ng-packagr", "options": { "tsConfig": "libs/ui/tsconfig.lib.json", "project": "libs/ui/ng-package.json" @@ -55,7 +55,6 @@ "options": { "aot": true, "outputPath": "dist/sales", - "outputHashing": "all", "index": "apps/sales/src/index.html", "main": "apps/sales/src/main.ts", "polyfills": "apps/sales/src/polyfills.ts", @@ -123,10 +122,7 @@ "serve": { "builder": "@angular-builders/custom-webpack:dev-server", "options": { - "browserTarget": "sales:build", - "customWebpackConfig": { - "path": "apps/sales/webpack.config.js" - } + "browserTarget": "sales:build" }, "configurations": { "test": { @@ -224,7 +220,7 @@ "prefix": "lib", "architect": { "build": { - "builder": "@angular-devkit/build-ng-packagr:build", + "builder": "@angular-devkit/build-angular:ng-packagr", "options": { "tsConfig": "libs/sso/tsconfig.lib.json", "project": "libs/sso/ng-package.json" @@ -264,7 +260,7 @@ "prefix": "lib", "architect": { "build": { - "builder": "@angular-devkit/build-ng-packagr:build", + "builder": "@angular-devkit/build-angular:ng-packagr", "options": { "tsConfig": "apps/swagger/availability/tsconfig.lib.json", "project": "apps/swagger/availability/ng-package.json" @@ -304,7 +300,7 @@ "prefix": "lib", "architect": { "build": { - "builder": "@angular-devkit/build-ng-packagr:build", + "builder": "@angular-devkit/build-angular:ng-packagr", "options": { "tsConfig": "apps/swagger/checkout/tsconfig.lib.json", "project": "apps/swagger/checkout/ng-package.json" @@ -344,7 +340,7 @@ "prefix": "lib", "architect": { "build": { - "builder": "@angular-devkit/build-ng-packagr:build", + "builder": "@angular-devkit/build-angular:ng-packagr", "options": { "tsConfig": "apps/swagger/crm/tsconfig.lib.json", "project": "apps/swagger/crm/ng-package.json" @@ -384,7 +380,7 @@ "prefix": "lib", "architect": { "build": { - "builder": "@angular-devkit/build-ng-packagr:build", + "builder": "@angular-devkit/build-angular:ng-packagr", "options": { "tsConfig": "apps/swagger/isa/tsconfig.lib.json", "project": "apps/swagger/isa/ng-package.json" @@ -424,7 +420,7 @@ "prefix": "lib", "architect": { "build": { - "builder": "@angular-devkit/build-ng-packagr:build", + "builder": "@angular-devkit/build-angular:ng-packagr", "options": { "tsConfig": "apps/swagger/oms/tsconfig.lib.json", "project": "apps/swagger/oms/ng-package.json" @@ -464,7 +460,7 @@ "prefix": "lib", "architect": { "build": { - "builder": "@angular-devkit/build-ng-packagr:build", + "builder": "@angular-devkit/build-angular:ng-packagr", "options": { "tsConfig": "apps/swagger/print/tsconfig.lib.json", "project": "apps/swagger/print/ng-package.json" @@ -504,7 +500,7 @@ "prefix": "lib", "architect": { "build": { - "builder": "@angular-devkit/build-ng-packagr:build", + "builder": "@angular-devkit/build-angular:ng-packagr", "options": { "tsConfig": "apps/swagger/cat/tsconfig.lib.json", "project": "apps/swagger/cat/ng-package.json" @@ -544,7 +540,7 @@ "prefix": "lib", "architect": { "build": { - "builder": "@angular-devkit/build-ng-packagr:build", + "builder": "@angular-devkit/build-angular:ng-packagr", "options": { "tsConfig": "apps/swagger/eis/tsconfig.lib.json", "project": "apps/swagger/eis/ng-package.json" @@ -584,7 +580,7 @@ "prefix": "lib", "architect": { "build": { - "builder": "@angular-devkit/build-ng-packagr:build", + "builder": "@angular-devkit/build-angular:ng-packagr", "options": { "tsConfig": "apps/native-container/tsconfig.lib.json", "project": "apps/native-container/ng-package.json" @@ -624,7 +620,7 @@ "prefix": "lib", "architect": { "build": { - "builder": "@angular-devkit/build-ng-packagr:build", + "builder": "@angular-devkit/build-angular:ng-packagr", "options": { "tsConfig": "apps/isa/remission/tsconfig.lib.json", "project": "apps/isa/remission/ng-package.json" @@ -664,7 +660,7 @@ "prefix": "crm", "architect": { "build": { - "builder": "@angular-devkit/build-ng-packagr:build", + "builder": "@angular-devkit/build-angular:ng-packagr", "options": { "tsConfig": "apps/domain/crm/tsconfig.lib.json", "project": "apps/domain/crm/ng-package.json" @@ -704,7 +700,7 @@ "prefix": "checkout", "architect": { "build": { - "builder": "@angular-devkit/build-ng-packagr:build", + "builder": "@angular-devkit/build-angular:ng-packagr", "options": { "tsConfig": "apps/domain/checkout/tsconfig.lib.json", "project": "apps/domain/checkout/ng-package.json" @@ -3297,6 +3293,487 @@ } } }, + "isa-app": { + "projectType": "application", + "schematics": { + "@schematics/angular:component": { + "style": "scss" + }, + "@schematics/angular:application": { + "strict": true + } + }, + "root": "apps/isa-app", + "sourceRoot": "apps/isa-app/src", + "prefix": "app", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:browser", + "options": { + "outputPath": "dist/isa-app", + "index": "apps/isa-app/src/index.html", + "main": "apps/isa-app/src/main.ts", + "polyfills": "apps/isa-app/src/polyfills.ts", + "tsConfig": "apps/isa-app/tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "apps/isa-app/src/favicon.ico", + "apps/isa-app/src/assets", + "apps/isa-app/src/config", + "apps/isa-app/src/silent-refresh.html", + "apps/isa-app/src/manifest.webmanifest" + ], + "styles": [ + "apps/isa-app/src/styles.scss" + ], + "scripts": [], + "serviceWorker": true, + "ngswConfigPath": "apps/isa-app/ngsw-config.json" + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "fileReplacements": [ + { + "replace": "apps/isa-app/src/environments/environment.ts", + "with": "apps/isa-app/src/environments/environment.prod.ts" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "builder": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "browserTarget": "isa-app:build:production" + }, + "development": { + "browserTarget": "isa-app:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "builder": "@angular-devkit/build-angular:extract-i18n", + "options": { + "browserTarget": "isa-app:build" + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "apps/isa-app/src/test.ts", + "polyfills": "apps/isa-app/src/polyfills.ts", + "tsConfig": "apps/isa-app/tsconfig.spec.json", + "karmaConfig": "apps/isa-app/karma.conf.js", + "inlineStyleLanguage": "scss", + "assets": [ + "apps/isa-app/src/favicon.ico", + "apps/isa-app/src/assets", + "apps/isa-app/src/manifest.webmanifest" + ], + "styles": [ + "apps/isa-app/src/styles.scss" + ], + "scripts": [] + } + } + } + }, + "@core/config": { + "projectType": "library", + "root": "apps/core/config", + "sourceRoot": "apps/core/config/src", + "prefix": "lib", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:ng-packagr", + "options": { + "project": "apps/core/config/ng-package.json" + }, + "configurations": { + "production": { + "tsConfig": "apps/core/config/tsconfig.lib.prod.json" + }, + "development": { + "tsConfig": "apps/core/config/tsconfig.lib.json" + } + }, + "defaultConfiguration": "production" + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "apps/core/config/src/test.ts", + "tsConfig": "apps/core/config/tsconfig.spec.json", + "karmaConfig": "apps/core/config/karma.conf.js" + } + } + } + }, + "@core/auth": { + "projectType": "library", + "root": "apps/core/auth", + "sourceRoot": "apps/core/auth/src", + "prefix": "lib", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:ng-packagr", + "options": { + "project": "apps/core/auth/ng-package.json" + }, + "configurations": { + "production": { + "tsConfig": "apps/core/auth/tsconfig.lib.prod.json" + }, + "development": { + "tsConfig": "apps/core/auth/tsconfig.lib.json" + } + }, + "defaultConfiguration": "production" + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "apps/core/auth/src/test.ts", + "tsConfig": "apps/core/auth/tsconfig.spec.json", + "karmaConfig": "apps/core/auth/karma.conf.js" + } + } + } + }, + "@shell/footer": { + "projectType": "library", + "root": "apps/shell/footer", + "sourceRoot": "apps/shell/footer/src", + "prefix": "lib", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:ng-packagr", + "options": { + "project": "apps/shell/footer/ng-package.json" + }, + "configurations": { + "production": { + "tsConfig": "apps/shell/footer/tsconfig.lib.prod.json" + }, + "development": { + "tsConfig": "apps/shell/footer/tsconfig.lib.json" + } + }, + "defaultConfiguration": "production" + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "apps/shell/footer/src/test.ts", + "tsConfig": "apps/shell/footer/tsconfig.spec.json", + "karmaConfig": "apps/shell/footer/karma.conf.js" + } + } + } + }, + "@shell/process": { + "projectType": "library", + "root": "apps/shell/process", + "sourceRoot": "apps/shell/process/src", + "prefix": "lib", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:ng-packagr", + "options": { + "project": "apps/shell/process/ng-package.json" + }, + "configurations": { + "production": { + "tsConfig": "apps/shell/process/tsconfig.lib.prod.json" + }, + "development": { + "tsConfig": "apps/shell/process/tsconfig.lib.json" + } + }, + "defaultConfiguration": "production" + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "apps/shell/process/src/test.ts", + "tsConfig": "apps/shell/process/tsconfig.spec.json", + "karmaConfig": "apps/shell/process/karma.conf.js" + } + } + } + }, + "@page/dashboard": { + "projectType": "library", + "root": "apps/page/dashboard", + "sourceRoot": "apps/page/dashboard/src", + "prefix": "page", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:ng-packagr", + "options": { + "project": "apps/page/dashboard/ng-package.json" + }, + "configurations": { + "production": { + "tsConfig": "apps/page/dashboard/tsconfig.lib.prod.json" + }, + "development": { + "tsConfig": "apps/page/dashboard/tsconfig.lib.json" + } + }, + "defaultConfiguration": "production" + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "apps/page/dashboard/src/test.ts", + "tsConfig": "apps/page/dashboard/tsconfig.spec.json", + "karmaConfig": "apps/page/dashboard/karma.conf.js" + } + } + } + }, + "@domain/isa": { + "projectType": "library", + "root": "apps/domain/isa", + "sourceRoot": "apps/domain/isa/src", + "prefix": "lib", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:ng-packagr", + "options": { + "project": "apps/domain/isa/ng-package.json" + }, + "configurations": { + "production": { + "tsConfig": "apps/domain/isa/tsconfig.lib.prod.json" + }, + "development": { + "tsConfig": "apps/domain/isa/tsconfig.lib.json" + } + }, + "defaultConfiguration": "production" + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "apps/domain/isa/src/test.ts", + "tsConfig": "apps/domain/isa/tsconfig.spec.json", + "karmaConfig": "apps/domain/isa/karma.conf.js" + } + } + } + }, + "@core/logger": { + "projectType": "library", + "root": "apps/core/logger", + "sourceRoot": "apps/core/logger/src", + "prefix": "lib", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:ng-packagr", + "options": { + "project": "apps/core/logger/ng-package.json" + }, + "configurations": { + "production": { + "tsConfig": "apps/core/logger/tsconfig.lib.prod.json" + }, + "development": { + "tsConfig": "apps/core/logger/tsconfig.lib.json" + } + }, + "defaultConfiguration": "production" + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "apps/core/logger/src/test.ts", + "tsConfig": "apps/core/logger/tsconfig.spec.json", + "karmaConfig": "apps/core/logger/karma.conf.js" + } + } + } + }, + "@shell/filter-overlay": { + "projectType": "library", + "root": "apps/shell/filter-overlay", + "sourceRoot": "apps/shell/filter-overlay/src", + "prefix": "lib", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:ng-packagr", + "options": { + "project": "apps/shell/filter-overlay/ng-package.json" + }, + "configurations": { + "production": { + "tsConfig": "apps/shell/filter-overlay/tsconfig.lib.prod.json" + }, + "development": { + "tsConfig": "apps/shell/filter-overlay/tsconfig.lib.json" + } + }, + "defaultConfiguration": "production" + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "apps/shell/filter-overlay/src/test.ts", + "tsConfig": "apps/shell/filter-overlay/tsconfig.spec.json", + "karmaConfig": "apps/shell/filter-overlay/karma.conf.js" + } + } + } + }, + "@store/search-component-store": { + "projectType": "library", + "root": "apps/store/search-component-store", + "sourceRoot": "apps/store/search-component-store/src", + "prefix": "lib", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:ng-packagr", + "options": { + "project": "apps/store/search-component-store/ng-package.json" + }, + "configurations": { + "production": { + "tsConfig": "apps/store/search-component-store/tsconfig.lib.prod.json" + }, + "development": { + "tsConfig": "apps/store/search-component-store/tsconfig.lib.json" + } + }, + "defaultConfiguration": "production" + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "apps/store/search-component-store/src/test.ts", + "tsConfig": "apps/store/search-component-store/tsconfig.spec.json", + "karmaConfig": "apps/store/search-component-store/karma.conf.js" + } + } + } + }, + "@domain/remission": { + "projectType": "library", + "root": "apps/domain/remission", + "sourceRoot": "apps/domain/remission/src", + "prefix": "lib", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:ng-packagr", + "options": { + "project": "apps/domain/remission/ng-package.json" + }, + "configurations": { + "production": { + "tsConfig": "apps/domain/remission/tsconfig.lib.prod.json" + }, + "development": { + "tsConfig": "apps/domain/remission/tsconfig.lib.json" + } + }, + "defaultConfiguration": "production" + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "apps/domain/remission/src/test.ts", + "tsConfig": "apps/domain/remission/tsconfig.spec.json", + "karmaConfig": "apps/domain/remission/karma.conf.js" + } + } + } + }, + "@page/remission": { + "projectType": "library", + "root": "apps/page/remission", + "sourceRoot": "apps/page/remission/src", + "prefix": "page", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:ng-packagr", + "options": { + "project": "apps/page/remission/ng-package.json" + }, + "configurations": { + "production": { + "tsConfig": "apps/page/remission/tsconfig.lib.prod.json" + }, + "development": { + "tsConfig": "apps/page/remission/tsconfig.lib.json" + } + }, + "defaultConfiguration": "production" + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "apps/page/remission/src/test.ts", + "tsConfig": "apps/page/remission/tsconfig.spec.json", + "karmaConfig": "apps/page/remission/karma.conf.js" + } + } + } + }, + "@modal/notifications": { + "projectType": "library", + "root": "apps/modal/notifications", + "sourceRoot": "apps/modal/notifications/src", + "prefix": "lib", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:ng-packagr", + "options": { + "project": "apps/modal/notifications/ng-package.json" + }, + "configurations": { + "production": { + "tsConfig": "apps/modal/notifications/tsconfig.lib.prod.json" + }, + "development": { + "tsConfig": "apps/modal/notifications/tsconfig.lib.json" + } + }, + "defaultConfiguration": "production" + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "apps/modal/notifications/src/test.ts", + "tsConfig": "apps/modal/notifications/tsconfig.spec.json", + "karmaConfig": "apps/modal/notifications/karma.conf.js" + } + } + } + }, "@ui/branch-dropdown": { "projectType": "library", "root": "apps/ui/branch-dropdown", @@ -3336,7 +3813,38 @@ } } } + }, + "@adapter/scan": { + "projectType": "library", + "root": "apps/adapter/scan", + "sourceRoot": "apps/adapter/scan/src", + "prefix": "lib", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:ng-packagr", + "options": { + "project": "apps/adapter/scan/ng-package.json" + }, + "configurations": { + "production": { + "tsConfig": "apps/adapter/scan/tsconfig.lib.prod.json" + }, + "development": { + "tsConfig": "apps/adapter/scan/tsconfig.lib.json" + } + }, + "defaultConfiguration": "production" + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "apps/adapter/scan/src/test.ts", + "tsConfig": "apps/adapter/scan/tsconfig.spec.json", + "karmaConfig": "apps/adapter/scan/karma.conf.js" + } + } + } } - }, - "defaultProject": "sales" + }, + "defaultProject": "isa-app" } \ No newline at end of file diff --git a/apps/adapter/scan/README.md b/apps/adapter/scan/README.md new file mode 100644 index 000000000..fdde5c3f3 --- /dev/null +++ b/apps/adapter/scan/README.md @@ -0,0 +1,25 @@ +# 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. diff --git a/apps/adapter/scan/karma.conf.js b/apps/adapter/scan/karma.conf.js new file mode 100644 index 000000000..a5f98cf6d --- /dev/null +++ b/apps/adapter/scan/karma.conf.js @@ -0,0 +1,41 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage'), + require('@angular-devkit/build-angular/plugins/karma'), + ], + client: { + jasmine: { + // you can add configuration options for Jasmine here + // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html + // for example, you can disable the random execution with `random: false` + // or set a specific seed with `seed: 4321` + }, + clearContext: false, // leave Jasmine Spec Runner output visible in browser + }, + jasmineHtmlReporter: { + suppressAll: true, // removes the duplicated traces + }, + coverageReporter: { + dir: require('path').join(__dirname, '../../../coverage/adapter/scan'), + subdir: '.', + reporters: [{ type: 'html' }, { type: 'text-summary' }], + }, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + singleRun: false, + restartOnFileChange: true, + }); +}; diff --git a/apps/adapter/scan/ng-package.json b/apps/adapter/scan/ng-package.json new file mode 100644 index 000000000..8148010b1 --- /dev/null +++ b/apps/adapter/scan/ng-package.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../../dist/adapter/scan", + "lib": { + "entryFile": "src/public-api.ts" + } +} \ No newline at end of file diff --git a/apps/adapter/scan/package.json b/apps/adapter/scan/package.json new file mode 100644 index 000000000..7586245ba --- /dev/null +++ b/apps/adapter/scan/package.json @@ -0,0 +1,11 @@ +{ + "name": "@adapter/scan", + "version": "0.0.1", + "peerDependencies": { + "@angular/common": "^12.2.0", + "@angular/core": "^12.2.0" + }, + "dependencies": { + "tslib": "^2.3.0" + } +} \ No newline at end of file diff --git a/apps/adapter/scan/src/lib/dev.scan-adapter.ts b/apps/adapter/scan/src/lib/dev.scan-adapter.ts new file mode 100644 index 000000000..65dc012d0 --- /dev/null +++ b/apps/adapter/scan/src/lib/dev.scan-adapter.ts @@ -0,0 +1,46 @@ +import { Injectable } from '@angular/core'; +import { PromptModalData, UiModalService, UiPromptModalComponent } from '@ui/modal'; +import { Observable } from 'rxjs'; +import { ScanAdapter } from './scan-adapter'; + +@Injectable() +export class DevScanAdapter implements ScanAdapter { + constructor(private _modal: UiModalService) {} + + getName(): string { + return 'Dev Scanner'; + } + + isPrimary(): boolean { + return true; + } + + isReady(): boolean { + return true; + } + + scan(): Observable { + return new Observable((observer) => { + const modalRef = this._modal.open({ + content: UiPromptModalComponent, + title: 'Anmeldung', + data: { + message: 'Bitte geben Sie Ihren Login Code ein', + placeholder: 'Login Code', + confirmText: 'weiter', + cancelText: 'Anmeldung mit Benutzername und Passwort', + } as PromptModalData, + }); + + const sub = modalRef.afterClosed$.subscribe((result) => { + observer.next(result.data); + observer.complete(); + }); + + return () => { + modalRef.close(); + sub.unsubscribe(); + }; + }); + } +} diff --git a/apps/adapter/scan/src/lib/native.scan-adapter.ts b/apps/adapter/scan/src/lib/native.scan-adapter.ts new file mode 100644 index 000000000..b958812c6 --- /dev/null +++ b/apps/adapter/scan/src/lib/native.scan-adapter.ts @@ -0,0 +1,30 @@ +import { Injectable } from '@angular/core'; +import { NativeContainerService } from 'native-container'; +import { Observable } from 'rxjs'; +import { filter, map, take } from 'rxjs/operators'; +import { ScanAdapter } from './scan-adapter'; + +@Injectable() +export class NativeScanAdapter implements ScanAdapter { + constructor(private readonly nativeContainerService: NativeContainerService) {} + + getName(): string { + return 'Native Scanner'; + } + + isPrimary(): boolean { + return true; + } + + isReady(): boolean { + return this.nativeContainerService.isUiWebview().isNative; + } + + scan(): Observable { + return this.nativeContainerService.openScanner('remissionContainer').pipe( + filter((result) => result.status === 'SUCCESS'), + map((result) => result.data), + take(1) + ); + } +} diff --git a/apps/adapter/scan/src/lib/scan-adapter.ts b/apps/adapter/scan/src/lib/scan-adapter.ts new file mode 100644 index 000000000..a10f74b31 --- /dev/null +++ b/apps/adapter/scan/src/lib/scan-adapter.ts @@ -0,0 +1,11 @@ +import { Observable } from 'rxjs'; + +export interface ScanAdapter { + getName(): string; + + isPrimary(): boolean; + + isReady(): boolean; + + scan(): Observable; +} diff --git a/apps/adapter/scan/src/lib/scan.module.ts b/apps/adapter/scan/src/lib/scan.module.ts new file mode 100644 index 000000000..d63f25d83 --- /dev/null +++ b/apps/adapter/scan/src/lib/scan.module.ts @@ -0,0 +1,14 @@ +import { NgModule } from '@angular/core'; +import { DevScanAdapter } from './dev.scan-adapter'; +import { NativeScanAdapter } from './native.scan-adapter'; +import { SCAN_ADAPTER } from './tokens'; + +@NgModule({}) +export class ScanAdapterModule { + static forRoot(dev?: boolean) { + return { + ngModule: ScanAdapterModule, + providers: [{ provide: SCAN_ADAPTER, useClass: dev ? DevScanAdapter : NativeScanAdapter, multi: true }], + }; + } +} diff --git a/apps/adapter/scan/src/lib/scan.service.ts b/apps/adapter/scan/src/lib/scan.service.ts new file mode 100644 index 000000000..08070a760 --- /dev/null +++ b/apps/adapter/scan/src/lib/scan.service.ts @@ -0,0 +1,26 @@ +import { Inject, Injectable } from '@angular/core'; +import { ScanAdapter } from './scan-adapter'; +import { SCAN_ADAPTER } from './tokens'; + +@Injectable({ + providedIn: 'root', +}) +export class ScanAdapterService { + constructor(@Inject(SCAN_ADAPTER) private readonly scanAdapters: ScanAdapter[]) {} + + scanners() { + return this.scanAdapters.filter((adapter) => adapter.isReady()); + } + + scanner() { + return this.scanners().find((scanner) => scanner.isPrimary()) || this.scanners().find(() => true); + } + + scan() { + const primaryScanner = this.scanner(); + if (primaryScanner) { + return primaryScanner.scan(); + } + return null; + } +} diff --git a/apps/adapter/scan/src/lib/tokens.ts b/apps/adapter/scan/src/lib/tokens.ts new file mode 100644 index 000000000..d88369165 --- /dev/null +++ b/apps/adapter/scan/src/lib/tokens.ts @@ -0,0 +1,4 @@ +import { InjectionToken } from '@angular/core'; +import { ScanAdapter } from './scan-adapter'; + +export const SCAN_ADAPTER = new InjectionToken('SCAN_ADAPTER'); diff --git a/apps/adapter/scan/src/public-api.ts b/apps/adapter/scan/src/public-api.ts new file mode 100644 index 000000000..35c014434 --- /dev/null +++ b/apps/adapter/scan/src/public-api.ts @@ -0,0 +1,6 @@ +/* + * Public API Surface of scan + */ + +export * from './lib/scan.service'; +export * from './lib/scan.module'; diff --git a/apps/adapter/scan/src/test.ts b/apps/adapter/scan/src/test.ts new file mode 100644 index 000000000..b22c5d4af --- /dev/null +++ b/apps/adapter/scan/src/test.ts @@ -0,0 +1,25 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import 'zone.js'; +import 'zone.js/testing'; +import { getTestBed } from '@angular/core/testing'; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; + +declare const require: { + context( + path: string, + deep?: boolean, + filter?: RegExp + ): { + keys(): string[]; + (id: string): T; + }; +}; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), { teardown: { destroyAfterEach: true } }); + +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().map(context); diff --git a/apps/adapter/scan/tsconfig.lib.json b/apps/adapter/scan/tsconfig.lib.json new file mode 100644 index 000000000..a7141c543 --- /dev/null +++ b/apps/adapter/scan/tsconfig.lib.json @@ -0,0 +1,20 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "outDir": "../../../out-tsc/lib", + "target": "es2015", + "declaration": true, + "declarationMap": true, + "inlineSources": true, + "types": [], + "lib": [ + "dom", + "es2018" + ] + }, + "exclude": [ + "src/test.ts", + "**/*.spec.ts" + ] +} diff --git a/apps/adapter/scan/tsconfig.lib.prod.json b/apps/adapter/scan/tsconfig.lib.prod.json new file mode 100644 index 000000000..06de549e1 --- /dev/null +++ b/apps/adapter/scan/tsconfig.lib.prod.json @@ -0,0 +1,10 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.lib.json", + "compilerOptions": { + "declarationMap": false + }, + "angularCompilerOptions": { + "compilationMode": "partial" + } +} diff --git a/apps/adapter/scan/tsconfig.spec.json b/apps/adapter/scan/tsconfig.spec.json new file mode 100644 index 000000000..85392ee8f --- /dev/null +++ b/apps/adapter/scan/tsconfig.spec.json @@ -0,0 +1,17 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "outDir": "../../../out-tsc/spec", + "types": [ + "jasmine" + ] + }, + "files": [ + "src/test.ts" + ], + "include": [ + "**/*.spec.ts", + "**/*.d.ts" + ] +} diff --git a/apps/cdn/product-image/src/lib/product-image.service.ts b/apps/cdn/product-image/src/lib/product-image.service.ts index cec421790..29671da9a 100644 --- a/apps/cdn/product-image/src/lib/product-image.service.ts +++ b/apps/cdn/product-image/src/lib/product-image.service.ts @@ -1,11 +1,12 @@ import { Inject, Injectable } from '@angular/core'; +import { Config } from '@core/config'; import { CDN_PRODUCT_IMAGE } from './tokens'; @Injectable({ providedIn: 'root', }) export class ProductImageService { - constructor(@Inject(CDN_PRODUCT_IMAGE) private imageUrl: string) {} + constructor(private readonly _config: Config) {} getImageUrl({ imageId, @@ -18,6 +19,6 @@ export class ProductImageService { height?: number; showDummy?: boolean; }): string { - return `${this.imageUrl}/${imageId}_${width}x${height}.jpg?showDummy=${showDummy}`; + return `${this._config.get('@cdn/product-image.url')}/${imageId}_${width}x${height}.jpg?showDummy=${showDummy}`; } } diff --git a/apps/cdn/product-image/src/test.ts b/apps/cdn/product-image/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/cdn/product-image/src/test.ts +++ b/apps/cdn/product-image/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/core/application/karma.conf.js b/apps/core/application/karma.conf.js index 48d0a312e..b41217804 100644 --- a/apps/core/application/karma.conf.js +++ b/apps/core/application/karma.conf.js @@ -1,5 +1,8 @@ // Karma configuration file, see link for more information // https://karma-runner.github.io/1.0/config/configuration-file.html +const customLaunchers = require('../../../karma/custom-launchers'); +const junitReporter = require('../../../karma/junit-reporter')('core-application'); +const coverageReporter = require('../../../karma/coverage-reporter')('core-application'); module.exports = function (config) { config.set({ @@ -9,23 +12,31 @@ module.exports = function (config) { require('karma-jasmine'), require('karma-chrome-launcher'), require('karma-jasmine-html-reporter'), - require('karma-coverage-istanbul-reporter'), + require('karma-coverage'), + require('karma-junit-reporter'), require('@angular-devkit/build-angular/plugins/karma'), ], client: { + jasmine: { + // you can add configuration options for Jasmine here + // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html + // for example, you can disable the random execution with `random: false` + // or set a specific seed with `seed: 4321` + }, clearContext: false, // leave Jasmine Spec Runner output visible in browser }, - coverageIstanbulReporter: { - dir: require('path').join(__dirname, '../../../coverage/core/application'), - reports: ['html', 'lcovonly', 'text-summary'], - fixWebpackSourcePaths: true, + jasmineHtmlReporter: { + suppressAll: true, // removes the duplicated traces }, + coverageReporter, + junitReporter, reporters: ['progress', 'kjhtml'], port: 9876, colors: true, logLevel: config.LOG_INFO, autoWatch: true, browsers: ['Chrome'], + customLaunchers, singleRun: false, restartOnFileChange: true, }); diff --git a/apps/core/application/src/lib/application.module.ts b/apps/core/application/src/lib/application.module.ts index fd4ced105..844f7a451 100644 --- a/apps/core/application/src/lib/application.module.ts +++ b/apps/core/application/src/lib/application.module.ts @@ -11,8 +11,13 @@ import { ApplicationService } from './application.service'; export class CoreApplicationModule { static forRoot(): ModuleWithProviders { return { - ngModule: CoreApplicationModule, - providers: [ApplicationService, StoreModule.forFeature('core-application', applicationReducer).providers], + ngModule: RootCoreApplicationModule, }; } } + +@NgModule({ + imports: [StoreModule.forFeature('core-application', applicationReducer)], + providers: [ApplicationService], +}) +export class RootCoreApplicationModule {} diff --git a/apps/core/application/src/lib/application.service.spec.ts b/apps/core/application/src/lib/application.service.spec.ts index e3746db8d..91414c4e4 100644 --- a/apps/core/application/src/lib/application.service.spec.ts +++ b/apps/core/application/src/lib/application.service.spec.ts @@ -1,32 +1,232 @@ -import { TestBed } from '@angular/core/testing'; -import { isObservable } from 'rxjs'; +import { createServiceFactory, SpectatorService, SpyObject } from '@ngneat/spectator'; +import { Store } from '@ngrx/store'; +import { Observable, of } from 'rxjs'; +import { first } from 'rxjs/operators'; +import { ApplicationProcess } from './defs'; import { ApplicationService } from './application.service'; +import * as actions from './store/application.actions'; describe('ApplicationService', () => { - let service: ApplicationService; + let spectator: SpectatorService; + let store: SpyObject; + const createService = createServiceFactory({ + service: ApplicationService, + mocks: [Store], + }); beforeEach(() => { - TestBed.configureTestingModule({ - providers: [ApplicationService], - }); - service = TestBed.inject(ApplicationService); + spectator = createService({}); + store = spectator.inject(Store); }); it('should be created', () => { - expect(service).toBeTruthy(); - }); - - describe('activatedProcessId', () => { - it('should return the processId', () => { - service.setActivatedProcessId(100); - expect(service.activatedProcessId).toEqual(100); - }); + expect(spectator.service).toBeTruthy(); }); describe('activatedProcessId$', () => { it('should return an observable', () => { - expect(isObservable(service.activatedProcessId$)).toBeTruthy(); + expect(spectator.service.activatedProcessId$).toBeInstanceOf(Observable); + }); + }); + + describe('activatedProcessId', () => { + it('should return the process id as a number', () => { + spyOnProperty(spectator.service['activatedProcessIdSubject'] as any, 'value').and.returnValue(2); + expect(spectator.service.activatedProcessId).toBe(2); + }); + }); + + describe('getProcesses$()', () => { + it('should call select on store and return all selected processes', async () => { + const processes: ApplicationProcess[] = [ + { id: 1, name: 'Vorgang', type: 'cart', section: 'customer', data: { count: 1 } }, + { id: 2, name: 'Vorgang', type: 'task-calendar', section: 'branch' }, + ]; + store.select.and.returnValue(of(processes)); + const result = await spectator.service.getProcesses$().pipe(first()).toPromise(); + expect(result).toEqual(processes); + expect(store.select).toHaveBeenCalled(); + }); + + it('should call select on store and return all section customer processes', async () => { + const processes: ApplicationProcess[] = [ + { id: 1, name: 'Vorgang', type: 'cart', section: 'customer', data: { count: 1 } }, + { id: 2, name: 'Vorgang', type: 'task-calendar', section: 'branch' }, + ]; + store.select.and.returnValue(of(processes)); + const result = await spectator.service.getProcesses$('customer').pipe(first()).toPromise(); + expect(result).toEqual([processes[0]]); + expect(store.select).toHaveBeenCalled(); + }); + + it('should call select on store and return all section branch processes', async () => { + const processes: ApplicationProcess[] = [ + { id: 1, name: 'Vorgang', type: 'cart', section: 'customer', data: { count: 1 } }, + { id: 2, name: 'Vorgang', type: 'task-calendar', section: 'branch' }, + ]; + store.select.and.returnValue(of(processes)); + const result = await spectator.service.getProcesses$('branch').pipe(first()).toPromise(); + expect(result).toEqual([processes[1]]); + expect(store.select).toHaveBeenCalled(); + }); + }); + + describe('getProcessById$()', () => { + it('should return the process by id', async () => { + const processes: ApplicationProcess[] = [ + { id: 1, name: 'Vorgang 1', section: 'customer' }, + { id: 2, name: 'Vorgang 2', section: 'customer' }, + ]; + spyOn(spectator.service, 'getProcesses$').and.returnValue(of(processes)); + + const process = await spectator.service.getProcessById$(1).toPromise(); + expect(process.id).toBe(1); + }); + }); + + describe('getSection$()', () => { + it('should return the selected section branch', async () => { + const section = 'branch'; + store.select.and.returnValue(of(section)); + const result = await spectator.service.getSection$().pipe(first()).toPromise(); + expect(result).toEqual(section); + expect(store.select).toHaveBeenCalled(); + }); + }); + + describe('getActivatedProcessId$', () => { + it('should return the current selected activated process id', async () => { + const activatedProcessId = 2; + store.select.and.returnValue(of({ id: activatedProcessId })); + const result = await spectator.service.getActivatedProcessId$().pipe(first()).toPromise(); + expect(result).toEqual(activatedProcessId); + expect(store.select).toHaveBeenCalled(); + }); + }); + + describe('activateProcess()', () => { + it('should dispatch action setActivatedProcess with argument activatedProcessId and action type', () => { + const activatedProcessId = 2; + spectator.service.activateProcess(activatedProcessId); + expect(store.dispatch).toHaveBeenCalledWith({ activatedProcessId, type: actions.setActivatedProcess.type }); + }); + }); + + describe('removeProcess()', () => { + it('should dispatch action removeProcess with argument processId and action type', () => { + const processId = 2; + spectator.service.removeProcess(processId); + expect(store.dispatch).toHaveBeenCalledWith({ processId, type: actions.removeProcess.type }); + }); + }); + + describe('createProcess()', () => { + it('should dispatch action addProcess with process', async () => { + const process: ApplicationProcess = { + id: 1, + name: 'Vorgang 1', + section: 'customer', + type: 'cart', + }; + + const timestamp = 100; + spyOn(spectator.service as any, '_createTimestamp').and.returnValue(timestamp); + spyOn(spectator.service, 'getProcessById$').and.returnValue(of(undefined)); + await spectator.service.createProcess(process); + + expect(store.dispatch).toHaveBeenCalledWith({ + type: actions.addProcess.type, + process: { + ...process, + activated: 0, + created: timestamp, + }, + }); + }); + + it('should throw an error if the process id is already existing', async () => { + const process: ApplicationProcess = { + id: 1, + name: 'Vorgang 1', + section: 'customer', + type: 'cart', + }; + spyOn(spectator.service, 'getProcessById$').and.returnValue(of(process)); + await expectAsync(spectator.service.createProcess(process)).toBeRejectedWithError('Process Id existiert bereits'); + }); + + it('should throw an error if the process id is not a number', async () => { + const process: ApplicationProcess = { + id: undefined, + name: 'Vorgang 1', + section: 'customer', + type: 'cart', + }; + spyOn(spectator.service, 'getProcessById$').and.returnValue(of({ id: 5, name: 'Vorgang 2', section: 'customer' })); + await expectAsync(spectator.service.createProcess(process)).toBeRejectedWithError('Process Id nicht gesetzt'); + }); + }); + + describe('patchProcess', () => { + it('should dispatch action patchProcess with changes', async () => { + const process: ApplicationProcess = { + id: 1, + name: 'Vorgang 1', + section: 'customer', + type: 'cart', + }; + + await spectator.service.patchProcess(process.id, process); + + expect(store.dispatch).toHaveBeenCalledWith({ + type: actions.patchProcess.type, + process: { + ...process, + }, + }); + }); + }); + + describe('setSection()', () => { + it('should dispatch action setSection with argument section and action type', () => { + const section = 'customer'; + spectator.service.setSection(section); + expect(store.dispatch).toHaveBeenCalledWith({ section, type: actions.setSection.type }); + }); + }); + + describe('getLastActivatedProcessWithSectionAndType()', () => { + it('should return the last activated process by section and type', async () => { + const processes: ApplicationProcess[] = [ + { id: 1, name: 'Vorgang 1', section: 'customer', type: 'cart', activated: 100 }, + { id: 2, name: 'Vorgang 2', section: 'customer', type: 'cart', activated: 200 }, + { id: 3, name: 'Vorgang 3', section: 'customer', type: 'goodsOut', activated: 300 }, + ]; + spyOn(spectator.service, 'getProcesses$').and.returnValue(of(processes)); + + expect(await spectator.service.getLastActivatedProcessWithSectionAndType$('customer', 'cart').pipe(first()).toPromise()).toBe( + processes[1] + ); + }); + }); + + describe('getLastActivatedProcessWithSection()', () => { + it('should return the last activated process by section', async () => { + const processes: ApplicationProcess[] = [ + { id: 1, name: 'Vorgang 1', section: 'customer', activated: 100 }, + { id: 2, name: 'Vorgang 2', section: 'customer', activated: 200 }, + { id: 3, name: 'Vorgang 3', section: 'customer', activated: 300 }, + ]; + spyOn(spectator.service, 'getProcesses$').and.returnValue(of(processes)); + + expect(await spectator.service.getLastActivatedProcessWithSection$('customer').pipe(first()).toPromise()).toBe(processes[2]); + }); + }); + + describe('_createTimestamp', () => { + it('should return the current timestamp in ms', () => { + expect(spectator.service['_createTimestamp']()).toBeCloseTo(Date.now()); }); }); }); diff --git a/apps/core/application/src/lib/application.service.ts b/apps/core/application/src/lib/application.service.ts index 6cfd4d1cf..afa7b6999 100644 --- a/apps/core/application/src/lib/application.service.ts +++ b/apps/core/application/src/lib/application.service.ts @@ -1,37 +1,129 @@ import { Injectable } from '@angular/core'; import { Store } from '@ngrx/store'; -import { BehaviorSubject } from 'rxjs'; -import { processRemoved, selectSection, setSection } from './store'; +import { isBoolean, isNumber } from '@utils/common'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { first, map, switchMap } from 'rxjs/operators'; +import { ApplicationProcess } from './defs'; +import { + removeProcess, + selectSection, + selectProcesses, + setSection, + addProcess, + setActivatedProcess, + selectActivatedProcess, + patchProcess, + patchProcessData, +} from './store'; @Injectable() export class ApplicationService { + /** @deprecated */ private activatedProcessIdSubject = new BehaviorSubject(undefined); + /** @deprecated */ get activatedProcessId() { return this.activatedProcessIdSubject.value; } + /** @deprecated */ get activatedProcessId$() { return this.activatedProcessIdSubject.asObservable(); } - readonly section$ = this.store.select(selectSection); - constructor(private store: Store) {} - setActivatedProcessId(processId: number) { - if (this.activatedProcessId !== processId) { - this.activatedProcessIdSubject.next(processId); - } + getProcesses$(section?: 'customer' | 'branch') { + const processes$ = this.store.select(selectProcesses); + return processes$.pipe(map((processes) => processes.filter((process) => (section ? process.section === section : true)))); + } + + getProcessById$(processId: number): Observable { + return this.getProcesses$().pipe(map((processes) => processes.find((process) => process.id === processId))); + } + + getSection$() { + return this.store.select(selectSection); + } + + /** @deprecated */ + getActivatedProcessId$() { + return this.store.select(selectActivatedProcess).pipe(map((process) => process?.id)); + } + + activateProcess(activatedProcessId: number) { + this.store.dispatch(setActivatedProcess({ activatedProcessId })); + this.activatedProcessIdSubject.next(activatedProcessId); } removeProcess(processId: number) { - this.store.dispatch(processRemoved({ processId })); + this.store.dispatch(removeProcess({ processId })); } - createProcess() {} + patchProcess(processId: number, changes: Partial) { + this.store.dispatch(patchProcess({ processId, changes })); + } + + patchProcessData(processId: number, data: Record) { + this.store.dispatch(patchProcessData({ processId, data })); + } + + async createProcess(process: ApplicationProcess) { + const existingProcess = await this.getProcessById$(process?.id).pipe(first()).toPromise(); + if (existingProcess?.id === process?.id) { + throw new Error('Process Id existiert bereits'); + } + + if (!isNumber(process.id)) { + throw new Error('Process Id nicht gesetzt'); + } + + if (!isBoolean(process.closeable)) { + process.closeable = true; + } + + if (!isBoolean(process.confirmClosing)) { + process.confirmClosing = true; + } + + process.created = this._createTimestamp(); + process.activated = 0; + this.store.dispatch(addProcess({ process })); + } setSection(section: 'customer' | 'branch') { this.store.dispatch(setSection({ section })); } + + getLastActivatedProcessWithSectionAndType$(section: 'customer' | 'branch', type: string): Observable { + return this.getProcesses$(section).pipe( + map((processes) => + processes + ?.filter((process) => process.type === type) + ?.reduce((latest, current) => { + if (!latest) { + return current; + } + return latest?.activated > current?.activated ? latest : current; + }, undefined) + ) + ); + } + + getLastActivatedProcessWithSection$(section: 'customer' | 'branch'): Observable { + return this.getProcesses$(section).pipe( + map((processes) => + processes?.reduce((latest, current) => { + if (!latest) { + return current; + } + return latest?.activated > current?.activated ? latest : current; + }, undefined) + ) + ); + } + + private _createTimestamp() { + return Date.now(); + } } diff --git a/apps/core/application/src/lib/defs/application-process.ts b/apps/core/application/src/lib/defs/application-process.ts index 4eb56df8d..fd33165e7 100644 --- a/apps/core/application/src/lib/defs/application-process.ts +++ b/apps/core/application/src/lib/defs/application-process.ts @@ -1,5 +1,11 @@ export interface ApplicationProcess { id: number; + created?: number; + activated?: number; name: string; - data: { [key: string]: any }; + section: 'customer' | 'branch'; + type?: string; + data?: { [key: string]: any }; + closeable?: boolean; + confirmClosing?: boolean; } diff --git a/apps/core/application/src/lib/index.ts b/apps/core/application/src/lib/index.ts index 45e818108..c21639ff3 100644 --- a/apps/core/application/src/lib/index.ts +++ b/apps/core/application/src/lib/index.ts @@ -1,7 +1,6 @@ // start:ng42.barrel export * from './application.module'; export * from './application.service'; -export * from './process.service'; export * from './defs'; export * from './store'; // end:ng42.barrel diff --git a/apps/core/application/src/lib/process.service.ts b/apps/core/application/src/lib/process.service.ts deleted file mode 100644 index 5bfa53ba4..000000000 --- a/apps/core/application/src/lib/process.service.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Injectable } from '@angular/core'; - -@Injectable({ providedIn: 'root' }) -export class ProcessService { - constructor() {} - - updateName(processId: number, name: string) {} -} diff --git a/apps/core/application/src/lib/store/application.actions.ts b/apps/core/application/src/lib/store/application.actions.ts index e1ac9c173..7acdbbbb9 100644 --- a/apps/core/application/src/lib/store/application.actions.ts +++ b/apps/core/application/src/lib/store/application.actions.ts @@ -1,10 +1,16 @@ import { createAction, props } from '@ngrx/store'; +import { ApplicationProcess } from '..'; const prefix = '[CORE-APPLICATION]'; -/** - * Action nach Entfernung eines Prozesses - */ -export const processRemoved = createAction(`${prefix} Process Removed`, props<{ processId: number }>()); - 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 }>()); + +export const patchProcessData = createAction(`${prefix} Patch Process Data`, props<{ processId: number; data: Record }>()); diff --git a/apps/core/application/src/lib/store/application.reducer.spec.ts b/apps/core/application/src/lib/store/application.reducer.spec.ts new file mode 100644 index 000000000..b147b4019 --- /dev/null +++ b/apps/core/application/src/lib/store/application.reducer.spec.ts @@ -0,0 +1,200 @@ +import { INITIAL_APPLICATION_STATE } from './application.state'; +import * as actions from './application.actions'; +import { applicationReducer } from './application.reducer'; +import { ApplicationProcess } from '../defs'; +import { ApplicationState } from './application.state'; + +describe('applicationReducer', () => { + describe('setSection()', () => { + it('should return modified state with section customer', () => { + const initialState = INITIAL_APPLICATION_STATE; + + const action = actions.setSection({ section: 'customer' }); + const state = applicationReducer(initialState, action); + + expect(state).toEqual({ + ...initialState, + section: 'customer', + }); + }); + + it('should return modified state with section branch', () => { + const initialState = INITIAL_APPLICATION_STATE; + + const action = actions.setSection({ section: 'branch' }); + const state = applicationReducer(initialState, action); + + expect(state).toEqual({ + ...initialState, + section: 'branch', + }); + }); + }); + + describe('addProcess()', () => { + it('should return modified state with new process if no processes are registered in the state', () => { + const initialState = INITIAL_APPLICATION_STATE; + + const process: ApplicationProcess = { + id: 1, + name: 'Vorgang', + section: 'customer', + type: 'cart', + data: {}, + }; + + const action = actions.addProcess({ process }); + const state = applicationReducer(initialState, action); + expect(state.processes[0]).toEqual(process); + }); + }); + + describe('patchProcess()', () => { + it('should return modified state with updated process when id is found', () => { + const initialState = INITIAL_APPLICATION_STATE; + + const process: ApplicationProcess = { + id: 1, + name: 'Vorgang', + section: 'customer', + type: 'cart', + }; + + const action = actions.patchProcess({ process: { ...process, name: 'Test' } }); + const state = applicationReducer( + { + ...initialState, + processes: [process], + }, + action + ); + expect(state.processes[0].name).toEqual('Test'); + }); + + it('should return unmodified state when id is not existing', () => { + const initialState = INITIAL_APPLICATION_STATE; + + const process: ApplicationProcess = { + id: 1, + name: 'Vorgang', + section: 'customer', + type: 'cart', + }; + + const action = actions.patchProcess({ process: { ...process, id: 2 } }); + const state = applicationReducer( + { + ...initialState, + processes: [process], + }, + action + ); + expect(state.processes).toEqual([process]); + }); + }); + + describe('removeProcess()', () => { + it('should return initial state if no processes are registered in the state', () => { + const initialState = INITIAL_APPLICATION_STATE; + + const action = actions.removeProcess({ processId: 2 }); + const state = applicationReducer(initialState, action); + expect(state).toEqual(initialState); + }); + + it('should return the unmodified state if processId not found', () => { + const initialState = INITIAL_APPLICATION_STATE; + const modifiedState: ApplicationState = { + ...initialState, + section: 'customer', + processes: [ + { + id: 1, + name: 'Vorgang', + section: 'customer', + type: 'cart', + }, + { + id: 4, + name: 'Vorgang', + section: 'customer', + type: 'goods-out', + }, + ] as ApplicationProcess[], + }; + + const action = actions.removeProcess({ processId: 2 }); + const state = applicationReducer(modifiedState, action); + expect(state).toEqual(modifiedState); + }); + + it('should return modified state, after process gets removed', () => { + const initialState = INITIAL_APPLICATION_STATE; + const modifiedState: ApplicationState = { + ...initialState, + section: 'customer', + processes: [ + { + id: 1, + name: 'Vorgang', + section: 'customer', + type: 'cart', + }, + { + id: 2, + name: 'Vorgang', + section: 'customer', + type: 'goods-out', + }, + ] as ApplicationProcess[], + }; + + const action = actions.removeProcess({ processId: 2 }); + const state = applicationReducer(modifiedState, action); + expect(state.processes).toEqual([ + { + id: 1, + name: 'Vorgang', + section: 'customer', + type: 'cart', + }, + ]); + }); + }); + + describe('setActivatedProcess()', () => { + it('should return modified state with process.activated value', () => { + const process: ApplicationProcess = { + id: 3, + name: 'Vorgang 3', + section: 'customer', + }; + const initialState: ApplicationState = { + ...INITIAL_APPLICATION_STATE, + processes: [process], + }; + + const action = actions.setActivatedProcess({ activatedProcessId: 3 }); + const state = applicationReducer(initialState, action); + + expect(state.processes[0].activated).toBeDefined(); + }); + + it('should return modified state without process.activated value when activatedProcessId doesnt exist', () => { + const process: ApplicationProcess = { + id: 1, + name: 'Vorgang 3', + section: 'customer', + }; + const initialState: ApplicationState = { + ...INITIAL_APPLICATION_STATE, + processes: [process], + }; + + const action = actions.setActivatedProcess({ activatedProcessId: 3 }); + const state = applicationReducer(initialState, action); + + expect(state.processes[0].activated).toBeUndefined(); + }); + }); +}); diff --git a/apps/core/application/src/lib/store/application.reducer.ts b/apps/core/application/src/lib/store/application.reducer.ts index 72280b24c..99337bad5 100644 --- a/apps/core/application/src/lib/store/application.reducer.ts +++ b/apps/core/application/src/lib/store/application.reducer.ts @@ -1,10 +1,45 @@ import { Action, createReducer, on } from '@ngrx/store'; -import { setSection } from './application.actions'; +import { setSection, addProcess, removeProcess, setActivatedProcess, patchProcess, patchProcessData } from './application.actions'; import { ApplicationState, INITIAL_APPLICATION_STATE } from './application.state'; const _applicationReducer = createReducer( INITIAL_APPLICATION_STATE, - on(setSection, (state, { section }) => ({ ...state, section })) + on(setSection, (state, { section }) => ({ ...state, section })), + on(addProcess, (state, { process }) => ({ ...state, processes: [...state.processes, { data: {}, ...process }] })), + on(removeProcess, (state, { processId }) => { + const processes = state?.processes?.filter((process) => process.id !== processId) || []; + return { ...state, processes }; + }), + on(setActivatedProcess, (state, { activatedProcessId }) => { + const processes = state.processes.map((process) => { + if (process.id === activatedProcessId) { + return { ...process, activated: Date.now() }; + } + return process; + }); + + return { ...state, processes }; + }), + on(patchProcess, (state, { processId, changes }) => { + const processes = state.processes.map((process) => { + if (process.id === processId) { + return { ...process, ...changes }; + } + return process; + }); + + return { ...state, processes }; + }), + on(patchProcessData, (state, { processId, data }) => { + const processes = state.processes.map((process) => { + if (process.id === processId) { + return { ...process, data: { ...(process.data || {}), ...data } }; + } + return process; + }); + + return { ...state, processes }; + }) ); export function applicationReducer(state: ApplicationState, action: Action) { diff --git a/apps/core/application/src/lib/store/application.selectors.spec.ts b/apps/core/application/src/lib/store/application.selectors.spec.ts new file mode 100644 index 000000000..6b899801e --- /dev/null +++ b/apps/core/application/src/lib/store/application.selectors.spec.ts @@ -0,0 +1,32 @@ +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: Partial = { + processes, + }; + expect(selectors.selectProcesses.projector(state)).toEqual(processes); + }); + + it('should select the section', () => { + const state: Partial = { + 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: Partial = { + processes, + }; + expect(selectors.selectActivatedProcess.projector(state)).toEqual(processes[1]); + }); +}); diff --git a/apps/core/application/src/lib/store/application.selectors.ts b/apps/core/application/src/lib/store/application.selectors.ts index f766ffbc3..8be024809 100644 --- a/apps/core/application/src/lib/store/application.selectors.ts +++ b/apps/core/application/src/lib/store/application.selectors.ts @@ -3,3 +3,14 @@ import { ApplicationState } from './application.state'; export const selectApplicationState = createFeatureSelector('core-application'); export const selectSection = createSelector(selectApplicationState, (s) => s.section); + +export const selectProcesses = createSelector(selectApplicationState, (s) => s.processes); + +export const selectActivatedProcess = createSelector(selectApplicationState, (s) => + s?.processes?.reduce((process, current) => { + if (!process) { + return current; + } + return process.activated > current.activated ? process : current; + }, undefined) +); diff --git a/apps/core/application/src/lib/store/application.state.ts b/apps/core/application/src/lib/store/application.state.ts index 19007b266..5315bb1d6 100644 --- a/apps/core/application/src/lib/store/application.state.ts +++ b/apps/core/application/src/lib/store/application.state.ts @@ -1,7 +1,11 @@ +import { ApplicationProcess } from '../defs'; + export interface ApplicationState { + processes: ApplicationProcess[]; section: 'customer' | 'branch'; } export const INITIAL_APPLICATION_STATE: ApplicationState = { + processes: [], section: 'customer', }; diff --git a/apps/core/application/src/test.ts b/apps/core/application/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/core/application/src/test.ts +++ b/apps/core/application/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/core/auth/README.md b/apps/core/auth/README.md new file mode 100644 index 000000000..d8b22783f --- /dev/null +++ b/apps/core/auth/README.md @@ -0,0 +1,25 @@ +# Auth + +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 auth` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project auth`. + +> Note: Don't forget to add `--project auth` or else it will be added to the default project in your `angular.json` file. + +## Build + +Run `ng build auth` to build the project. The build artifacts will be stored in the `dist/` directory. + +## Publishing + +After building your library with `ng build auth`, go to the dist folder `cd dist/auth` and run `npm publish`. + +## Running unit tests + +Run `ng test auth` 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. diff --git a/apps/core/auth/karma.conf.js b/apps/core/auth/karma.conf.js new file mode 100644 index 000000000..30d867fd4 --- /dev/null +++ b/apps/core/auth/karma.conf.js @@ -0,0 +1,43 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html +const customLaunchers = require('../../../karma/custom-launchers'); +const junitReporter = require('../../../karma/junit-reporter')('core-auth'); +const coverageReporter = require('../../../karma/coverage-reporter')('core-auth'); + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage'), + require('karma-junit-reporter'), + require('@angular-devkit/build-angular/plugins/karma'), + ], + client: { + jasmine: { + // you can add configuration options for Jasmine here + // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html + // for example, you can disable the random execution with `random: false` + // or set a specific seed with `seed: 4321` + }, + clearContext: false, // leave Jasmine Spec Runner output visible in browser + }, + jasmineHtmlReporter: { + suppressAll: true, // removes the duplicated traces + }, + coverageReporter, + junitReporter, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + customLaunchers, + singleRun: false, + restartOnFileChange: true, + }); +}; diff --git a/apps/core/auth/ng-package.json b/apps/core/auth/ng-package.json new file mode 100644 index 000000000..2c1047868 --- /dev/null +++ b/apps/core/auth/ng-package.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../../dist/core/auth", + "lib": { + "entryFile": "src/public-api.ts" + } +} \ No newline at end of file diff --git a/apps/core/auth/package.json b/apps/core/auth/package.json new file mode 100644 index 000000000..4560bd474 --- /dev/null +++ b/apps/core/auth/package.json @@ -0,0 +1,11 @@ +{ + "name": "@core/auth", + "version": "0.0.1", + "peerDependencies": { + "@angular/common": "^12.2.0", + "@angular/core": "^12.2.0" + }, + "dependencies": { + "tslib": "^2.3.0" + } +} \ No newline at end of file diff --git a/apps/core/auth/src/lib/auth.module.ts b/apps/core/auth/src/lib/auth.module.ts new file mode 100644 index 000000000..07d567f15 --- /dev/null +++ b/apps/core/auth/src/lib/auth.module.ts @@ -0,0 +1,17 @@ +import { ModuleWithProviders, NgModule } from '@angular/core'; +import { AuthService } from './auth.service'; +import { OAuthModule } from 'angular-oauth2-oidc'; +@NgModule({}) +export class AuthModule { + static forRoot(): ModuleWithProviders { + return { + ngModule: AuthModule, + providers: [ + AuthService, + OAuthModule.forRoot({ + resourceServer: { sendAccessToken: true }, + }).providers, + ], + }; + } +} diff --git a/apps/core/auth/src/lib/auth.service.spec.ts b/apps/core/auth/src/lib/auth.service.spec.ts new file mode 100644 index 000000000..e5e823d12 --- /dev/null +++ b/apps/core/auth/src/lib/auth.service.spec.ts @@ -0,0 +1,151 @@ +import { Config } from '@core/config'; +import { SpectatorService, createServiceFactory, SpyObject } from '@ngneat/spectator'; +import { OAuthService } from 'angular-oauth2-oidc'; +import { JwksValidationHandler } from 'angular-oauth2-oidc-jwks'; +import { Observable } from 'rxjs'; +import { AuthService } from './auth.service'; + +describe('AuthService', () => { + let spectator: SpectatorService; + const createService = createServiceFactory({ + service: AuthService, + mocks: [Config, OAuthService], + }); + let config: SpyObject; + let oAuthService: SpyObject; + + beforeEach(() => { + spectator = createService(); + config = spectator.inject(Config); + oAuthService = spectator.inject(OAuthService); + }); + + it('should be created', () => { + expect(spectator.service).toBeTruthy(); + }); + + describe('init()', () => { + it('should configure the oAuthService', () => { + config.get.and.returnValue({}); + spectator.service.init(); + expect(oAuthService.configure).toHaveBeenCalledWith({ + redirectUri: window.location.origin, + silentRefreshRedirectUri: window.location.origin + '/silent-refresh.html', + useSilentRefresh: true, + }); + expect(oAuthService.tokenValidationHandler).toBeInstanceOf(JwksValidationHandler); + expect(oAuthService.setupAutomaticSilentRefresh).toHaveBeenCalled(); + }); + + it('should load the discovery document', async () => { + config.get.and.returnValue({}); + oAuthService.loadDiscoveryDocumentAndTryLogin.and.returnValue(Promise.resolve(true)); + + spyOn(spectator.service['_initialized'], 'next'); + await spectator.service.init(); + + expect(oAuthService.loadDiscoveryDocumentAndTryLogin).toHaveBeenCalled(); + expect(spectator.service['_initialized'].next).toHaveBeenCalledWith(true); + }); + + it('should throw an error if its already initialized', async () => { + spyOn(spectator.service['_initialized'], 'getValue').and.returnValue(true); + await expectAsync(spectator.service.init()).toBeRejectedWithError('AuthService is already initialized'); + }); + }); + + describe('isAuthenticated()', () => { + it('should call hasValidIdToken() and return its value', () => { + oAuthService.hasValidIdToken.and.returnValue(true); + expect(spectator.service.isAuthenticated()).toBeTrue(); + expect(oAuthService.hasValidIdToken).toHaveBeenCalled(); + }); + }); + + describe('getToken()', () => { + it('should call getAccessToken() and return its value', () => { + oAuthService.getAccessToken.and.returnValue('token'); + expect(spectator.service.getToken()).toEqual('token'); + expect(oAuthService.getAccessToken).toHaveBeenCalled(); + }); + }); + + describe('getClaims()', () => { + it('should call getAccessToken() and return its value', () => { + oAuthService.getAccessToken.and.returnValue('token'); + const claims = { + claim1: 'value', + claim2: 'value2', + }; + spyOn(spectator.service, 'parseJwt').and.returnValue(claims); + expect(spectator.service.getClaims()).toEqual(claims); + expect(spectator.service.parseJwt).toHaveBeenCalledWith('token'); + expect(oAuthService.getAccessToken).toHaveBeenCalled(); + }); + }); + + describe('getClaimByKey()', () => { + it('should call getClaims() and return its key value', () => { + spyOn(spectator.service, 'getClaims').and.returnValue({ + claim1: 'value', + claim2: 'value2', + }); + + expect(spectator.service.getClaimByKey('claim1')).toEqual('value'); + expect(spectator.service.getClaims).toHaveBeenCalled(); + }); + + it('should return null if getClaims() returns null or undefined', () => { + spyOn(spectator.service, 'getClaims').and.returnValue(null); + + expect(spectator.service.getClaimByKey('claim1')).toBeNull(); + expect(spectator.service.getClaims).toHaveBeenCalled(); + }); + }); + + describe('parseJwt()', () => { + it('should return null if the token is null or undefined', () => { + expect(spectator.service.parseJwt(null)).toBeNull(); + expect(spectator.service.parseJwt(undefined)).toBeNull(); + }); + + it('should return the value of the key', () => { + const token = + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ'; + expect(spectator.service.parseJwt(token)).toEqual({ + sub: '1234567890', + name: 'John Doe', + admin: true, + }); + }); + }); + + describe('login()', () => { + it('should call initLoginFlow()', () => { + spectator.service.login(); + expect(oAuthService.initLoginFlow).toHaveBeenCalled(); + }); + }); + + describe('logout()', () => { + it('should call revokeTokenAndLogout()', async () => { + await spectator.service.logout(); + expect(oAuthService.revokeTokenAndLogout).toHaveBeenCalled(); + }); + }); + + describe('getToken()', () => { + it('should return getAccessToken()', () => { + spectator.service.getToken(); + expect(oAuthService.getAccessToken).toHaveBeenCalled(); + }); + }); + + describe('initialized', () => { + it('should return _initialized as Observable', () => { + spyOn(spectator.service['_initialized'], 'asObservable').and.callThrough(); + expect(spectator.service.initialized$).toBeInstanceOf(Observable); + expect(spectator.service['_initialized'].asObservable).toHaveBeenCalled(); + }); + }); +}); diff --git a/apps/core/auth/src/lib/auth.service.ts b/apps/core/auth/src/lib/auth.service.ts new file mode 100644 index 000000000..9a55102e6 --- /dev/null +++ b/apps/core/auth/src/lib/auth.service.ts @@ -0,0 +1,84 @@ +import { Injectable } from '@angular/core'; +import { Config } from '@core/config'; +import { isNullOrUndefined } from '@utils/common'; +import { AuthConfig, OAuthService } from 'angular-oauth2-oidc'; +import { JwksValidationHandler } from 'angular-oauth2-oidc-jwks'; +import { BehaviorSubject } from 'rxjs'; + +@Injectable({ + providedIn: 'root', +}) +export class AuthService { + private readonly _initialized = new BehaviorSubject(false); + get initialized$() { + return this._initialized.asObservable(); + } + + constructor(private _config: Config, private readonly _oAuthService: OAuthService) {} + + async init() { + if (this._initialized.getValue()) { + throw new Error('AuthService is already initialized'); + } + + const authConfig: AuthConfig = this._config.get('@core/auth'); + + authConfig.redirectUri = window.location.origin; + authConfig.silentRefreshRedirectUri = window.location.origin + '/silent-refresh.html'; + authConfig.useSilentRefresh = true; + + this._oAuthService.configure(authConfig); + this._oAuthService.tokenValidationHandler = new JwksValidationHandler(); + + this._oAuthService.setupAutomaticSilentRefresh(); + await this._oAuthService.loadDiscoveryDocumentAndTryLogin(); + + 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(); + } +} diff --git a/apps/core/auth/src/lib/index.ts b/apps/core/auth/src/lib/index.ts new file mode 100644 index 000000000..25b136fe6 --- /dev/null +++ b/apps/core/auth/src/lib/index.ts @@ -0,0 +1,4 @@ +// start:ng42.barrel +export * from './auth.module'; +export * from './auth.service'; +// end:ng42.barrel diff --git a/apps/core/auth/src/public-api.ts b/apps/core/auth/src/public-api.ts new file mode 100644 index 000000000..b7e76251b --- /dev/null +++ b/apps/core/auth/src/public-api.ts @@ -0,0 +1,5 @@ +/* + * Public API Surface of auth + */ + +export * from './lib'; diff --git a/apps/core/auth/src/test.ts b/apps/core/auth/src/test.ts new file mode 100644 index 000000000..b22c5d4af --- /dev/null +++ b/apps/core/auth/src/test.ts @@ -0,0 +1,25 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import 'zone.js'; +import 'zone.js/testing'; +import { getTestBed } from '@angular/core/testing'; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; + +declare const require: { + context( + path: string, + deep?: boolean, + filter?: RegExp + ): { + keys(): string[]; + (id: string): T; + }; +}; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), { teardown: { destroyAfterEach: true } }); + +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().map(context); diff --git a/apps/core/auth/tsconfig.lib.json b/apps/core/auth/tsconfig.lib.json new file mode 100644 index 000000000..a7141c543 --- /dev/null +++ b/apps/core/auth/tsconfig.lib.json @@ -0,0 +1,20 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "outDir": "../../../out-tsc/lib", + "target": "es2015", + "declaration": true, + "declarationMap": true, + "inlineSources": true, + "types": [], + "lib": [ + "dom", + "es2018" + ] + }, + "exclude": [ + "src/test.ts", + "**/*.spec.ts" + ] +} diff --git a/apps/core/auth/tsconfig.lib.prod.json b/apps/core/auth/tsconfig.lib.prod.json new file mode 100644 index 000000000..06de549e1 --- /dev/null +++ b/apps/core/auth/tsconfig.lib.prod.json @@ -0,0 +1,10 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.lib.json", + "compilerOptions": { + "declarationMap": false + }, + "angularCompilerOptions": { + "compilationMode": "partial" + } +} diff --git a/apps/core/auth/tsconfig.spec.json b/apps/core/auth/tsconfig.spec.json new file mode 100644 index 000000000..85392ee8f --- /dev/null +++ b/apps/core/auth/tsconfig.spec.json @@ -0,0 +1,17 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "outDir": "../../../out-tsc/spec", + "types": [ + "jasmine" + ] + }, + "files": [ + "src/test.ts" + ], + "include": [ + "**/*.spec.ts", + "**/*.d.ts" + ] +} diff --git a/apps/core/breadcrumb/src/lib/breadcrumb.service.ts b/apps/core/breadcrumb/src/lib/breadcrumb.service.ts index 7e3556bea..a32142e6d 100644 --- a/apps/core/breadcrumb/src/lib/breadcrumb.service.ts +++ b/apps/core/breadcrumb/src/lib/breadcrumb.service.ts @@ -3,7 +3,7 @@ import { Store } from '@ngrx/store'; import { getNumberId } from '@utils/id'; import { Observable } from 'rxjs'; -import { map, take } from 'rxjs/operators'; +import { first, map, take } from 'rxjs/operators'; import { Breadcrumb } from './defs'; import * as actions from './store/breadcrumb.actions'; @@ -29,6 +29,19 @@ export class BreadcrumbService { return this.store.select(selectors.selectBreadcrumbsByKey, key); } + getLastActivatedBreadcrumbByKey$(key: string | number): Observable { + return this.getBreadcrumbByKey$(key).pipe( + map((crumbs) => + crumbs.reduce((latest, current) => { + if (!latest) { + return current; + } + return latest.timestamp > current.timestamp ? latest : current; + }, undefined) + ) + ); + } + addBreadcrumb(breadcrumb: Breadcrumb): Breadcrumb { const newBreadcrumb: Breadcrumb = { ...breadcrumb, id: getNumberId(), timestamp: Date.now(), changed: Date.now() }; this.store.dispatch(actions.addBreadcrumb({ breadcrumb: newBreadcrumb })); @@ -39,6 +52,11 @@ export class BreadcrumbService { this.store.dispatch(actions.updateBreadcrumb({ id: breadcrumbId, changes: { ...changes, changed: Date.now() } })); } + async patchBreadcrumbByKeyAndTags(key: string | number, tags: string[], changes: Partial) { + const crumbs = await this.getBreadcrumbsByKeyAndTags$(key, tags).pipe(first()).toPromise(); + crumbs.forEach((crumb) => this.patchBreadcrumb(crumb.id, changes)); + } + async addBreadcrumbIfNotExists(breadcrumb: Breadcrumb) { const crumbs = await this.getBreadcrumbsByKeyAndTags$(breadcrumb.key, breadcrumb.tags).pipe(take(1)).toPromise(); if (crumbs.length === 0) { @@ -112,6 +130,11 @@ export class BreadcrumbService { this.store.dispatch(actions.removeManyBreadcrumb({ ids: breadcrumbsToRemove.map((crumb) => crumb.id) })); } + async removeBreadcrumbsByKeyAndTags(key: number | string, tags: string[]) { + const crumbs = await this.getBreadcrumbsByKeyAndTags$(key, tags).pipe(first()).toPromise(); + crumbs.forEach((crumb) => this.removeBreadcrumb(crumb.id)); + } + getLatestBreadcrumbForSection(section: 'customer' | 'branch') { return this.store .select(selectors.selectBreadcrumbsBySection, { section }) diff --git a/apps/core/breadcrumb/src/lib/defs/breadcrumb.model.ts b/apps/core/breadcrumb/src/lib/defs/breadcrumb.model.ts index e2ad33533..dcafc90f0 100644 --- a/apps/core/breadcrumb/src/lib/defs/breadcrumb.model.ts +++ b/apps/core/breadcrumb/src/lib/defs/breadcrumb.model.ts @@ -42,5 +42,5 @@ export interface Breadcrumb { /** * Applicatiuon Section */ - section: 'branch' | 'customer'; + section: string; } diff --git a/apps/core/breadcrumb/src/lib/store/breadcrumb.effect.ts b/apps/core/breadcrumb/src/lib/store/breadcrumb.effect.ts index fc1f6d7e3..bf0e87195 100644 --- a/apps/core/breadcrumb/src/lib/store/breadcrumb.effect.ts +++ b/apps/core/breadcrumb/src/lib/store/breadcrumb.effect.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { processRemoved } from '@core/application'; +import { removeProcess } from '@core/application'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { NEVER } from 'rxjs'; import { mergeMap, tap, first, map } from 'rxjs/operators'; @@ -10,7 +10,7 @@ export class BreadcrumbEffects { removeProcess$ = createEffect( () => this.actions$.pipe( - ofType(processRemoved), + ofType(removeProcess), mergeMap((action) => this.breadcrumb.getBreadcrumbByKey$(action.processId).pipe( first(), diff --git a/apps/core/breadcrumb/src/test.ts b/apps/core/breadcrumb/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/core/breadcrumb/src/test.ts +++ b/apps/core/breadcrumb/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/core/cache/src/test.ts b/apps/core/cache/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/core/cache/src/test.ts +++ b/apps/core/cache/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/core/command/src/test.ts b/apps/core/command/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/core/command/src/test.ts +++ b/apps/core/command/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/core/config/README.md b/apps/core/config/README.md new file mode 100644 index 000000000..6d05d99c5 --- /dev/null +++ b/apps/core/config/README.md @@ -0,0 +1,25 @@ +# Config + +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 config` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project config`. + +> Note: Don't forget to add `--project config` or else it will be added to the default project in your `angular.json` file. + +## Build + +Run `ng build config` to build the project. The build artifacts will be stored in the `dist/` directory. + +## Publishing + +After building your library with `ng build config`, go to the dist folder `cd dist/config` and run `npm publish`. + +## Running unit tests + +Run `ng test config` 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. diff --git a/apps/core/config/karma.conf.js b/apps/core/config/karma.conf.js new file mode 100644 index 000000000..e0ce960d2 --- /dev/null +++ b/apps/core/config/karma.conf.js @@ -0,0 +1,43 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html +const customLaunchers = require('../../../karma/custom-launchers'); +const junitReporter = require('../../../karma/junit-reporter')('core-config'); +const coverageReporter = require('../../../karma/coverage-reporter')('core-config'); + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage'), + require('karma-junit-reporter'), + require('@angular-devkit/build-angular/plugins/karma'), + ], + client: { + jasmine: { + // you can add configuration options for Jasmine here + // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html + // for example, you can disable the random execution with `random: false` + // or set a specific seed with `seed: 4321` + }, + clearContext: false, // leave Jasmine Spec Runner output visible in browser + }, + jasmineHtmlReporter: { + suppressAll: true, // removes the duplicated traces + }, + coverageReporter, + junitReporter, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + customLaunchers, + singleRun: false, + restartOnFileChange: true, + }); +}; diff --git a/apps/core/config/ng-package.json b/apps/core/config/ng-package.json new file mode 100644 index 000000000..33547b640 --- /dev/null +++ b/apps/core/config/ng-package.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../../dist/core/config", + "lib": { + "entryFile": "src/public-api.ts" + } +} \ No newline at end of file diff --git a/apps/core/config/package.json b/apps/core/config/package.json new file mode 100644 index 000000000..e7cdf5be7 --- /dev/null +++ b/apps/core/config/package.json @@ -0,0 +1,11 @@ +{ + "name": "@core/config", + "version": "0.0.1", + "peerDependencies": { + "@angular/common": "^12.2.0", + "@angular/core": "^12.2.0" + }, + "dependencies": { + "tslib": "^2.3.0" + } +} \ No newline at end of file diff --git a/apps/core/config/src/lib/config-loaders/config-loader.ts b/apps/core/config/src/lib/config-loaders/config-loader.ts new file mode 100644 index 000000000..f8222460d --- /dev/null +++ b/apps/core/config/src/lib/config-loaders/config-loader.ts @@ -0,0 +1,8 @@ +import { Observable } from 'rxjs'; + +/** + * Config loader interface for loading configurations + */ +export interface ConfigLoader { + load(): Promise; +} diff --git a/apps/core/config/src/lib/config-loaders/index.ts b/apps/core/config/src/lib/config-loaders/index.ts new file mode 100644 index 000000000..be3543595 --- /dev/null +++ b/apps/core/config/src/lib/config-loaders/index.ts @@ -0,0 +1,4 @@ +// start:ng42.barrel +export * from './config-loader'; +export * from './json.config-loader'; +// end:ng42.barrel diff --git a/apps/core/config/src/lib/config-loaders/json.config-loader.spec.ts b/apps/core/config/src/lib/config-loaders/json.config-loader.spec.ts new file mode 100644 index 000000000..e37aa778f --- /dev/null +++ b/apps/core/config/src/lib/config-loaders/json.config-loader.spec.ts @@ -0,0 +1,36 @@ +// 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; + 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' }); + }); + }); +}); diff --git a/apps/core/config/src/lib/config-loaders/json.config-loader.ts b/apps/core/config/src/lib/config-loaders/json.config-loader.ts new file mode 100644 index 000000000..62e0c7fab --- /dev/null +++ b/apps/core/config/src/lib/config-loaders/json.config-loader.ts @@ -0,0 +1,13 @@ +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 { + return this.http.get(this.url).toPromise(); + } +} diff --git a/apps/core/config/src/lib/config-module-options.ts b/apps/core/config/src/lib/config-module-options.ts new file mode 100644 index 000000000..21a488736 --- /dev/null +++ b/apps/core/config/src/lib/config-module-options.ts @@ -0,0 +1,7 @@ +import { Type } from '@angular/core'; +import { ConfigLoader } from './config-loaders'; + +export interface ConfigModuleOptions { + useConfigLoader: Type; + jsonConfigLoaderUrl?: string; +} diff --git a/apps/core/config/src/lib/config.module.ts b/apps/core/config/src/lib/config.module.ts new file mode 100644 index 000000000..6bde205a0 --- /dev/null +++ b/apps/core/config/src/lib/config.module.ts @@ -0,0 +1,28 @@ +import { APP_INITIALIZER, ModuleWithProviders, NgModule } from '@angular/core'; +import { CORE_CONFIG_LOADER } from '@core/config'; +import { Config } from './config'; +import { ConfigModuleOptions } from './config-module-options'; +import { CORE_JSON_CONFIG_LOADER_URL } from './tokens'; + +export function _initializeConfigFactory(config: Config) { + return () => config.init(); +} + +@NgModule({}) +export class ConfigModule { + static forRoot(options: ConfigModuleOptions): ModuleWithProviders { + 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, + ], + }; + } +} diff --git a/apps/core/config/src/lib/config.spec.ts b/apps/core/config/src/lib/config.spec.ts new file mode 100644 index 000000000..293bfeb14 --- /dev/null +++ b/apps/core/config/src/lib/config.spec.ts @@ -0,0 +1,45 @@ +import { createServiceFactory, SpectatorService } from '@ngneat/spectator'; +import { Config } from './config'; +import { ConfigLoader } from './config-loaders'; +import { CORE_CONFIG_LOADER } from './tokens'; + +class TestConfigLoader implements ConfigLoader { + load() { + return Promise.resolve({}); + } +} + +// Unit test Config +describe('Config', () => { + let spectator: SpectatorService; + 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'); + }); + }); +}); diff --git a/apps/core/config/src/lib/config.ts b/apps/core/config/src/lib/config.ts new file mode 100644 index 000000000..e21d298e6 --- /dev/null +++ b/apps/core/config/src/lib/config.ts @@ -0,0 +1,27 @@ +import { Inject, Injectable } from '@angular/core'; +import { ReplaySubject } from 'rxjs'; +import { ConfigLoader } from './config-loaders'; +import { CORE_CONFIG_LOADER } from './tokens'; +import { pick } from './utils'; + +@Injectable() +export class Config { + private _config: any; + + private readonly _initilized = new ReplaySubject(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); + } +} diff --git a/apps/core/config/src/lib/index.ts b/apps/core/config/src/lib/index.ts new file mode 100644 index 000000000..253c7419e --- /dev/null +++ b/apps/core/config/src/lib/index.ts @@ -0,0 +1,8 @@ +// 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 diff --git a/apps/core/config/src/lib/tokens.ts b/apps/core/config/src/lib/tokens.ts new file mode 100644 index 000000000..b8b72f761 --- /dev/null +++ b/apps/core/config/src/lib/tokens.ts @@ -0,0 +1,6 @@ +import { InjectionToken } from '@angular/core'; +import { ConfigLoader } from './config-loaders'; + +export const CORE_CONFIG_LOADER = new InjectionToken('core.config.loader'); + +export const CORE_JSON_CONFIG_LOADER_URL = new InjectionToken('core.json.config.loader.url'); diff --git a/apps/core/config/src/lib/utils/index.ts b/apps/core/config/src/lib/utils/index.ts new file mode 100644 index 000000000..ba6a59327 --- /dev/null +++ b/apps/core/config/src/lib/utils/index.ts @@ -0,0 +1,3 @@ +// start:ng42.barrel +export * from './pick'; +// end:ng42.barrel diff --git a/apps/core/config/src/lib/utils/pick.spec.ts b/apps/core/config/src/lib/utils/pick.spec.ts new file mode 100644 index 000000000..0097582b7 --- /dev/null +++ b/apps/core/config/src/lib/utils/pick.spec.ts @@ -0,0 +1,41 @@ +import { pick } from './pick'; + +describe('pick', () => { + it('should pick properties from the 1st level from the object', () => { + const obj = { + foo: 'bar', + }; + expect(pick('foo', obj)).toEqual('bar'); + }); + + it('should pick properties from the 2nd level from the object', () => { + const obj = { + foo: { + bar: 'baz', + }, + }; + expect(pick('foo.bar', obj)).toEqual('baz'); + }); + + it('should pick properties from the 3rd level from the object', () => { + const obj = { + foo: { + bar: { + baz: 'qux', + }, + }, + }; + expect(pick('foo.bar.baz', obj)).toEqual('qux'); + }); + + it('should throw an error of obj is not an object', () => { + expect(() => pick('foo', 'bar')).toThrowError(`bar is not an object`); + }); + + it('should return undefined if the property is not found', () => { + const obj = { + foo: 'bar', + }; + expect(pick('bar', obj)).toEqual(undefined); + }); +}); diff --git a/apps/core/config/src/lib/utils/pick.ts b/apps/core/config/src/lib/utils/pick.ts new file mode 100644 index 000000000..b11bc7e54 --- /dev/null +++ b/apps/core/config/src/lib/utils/pick.ts @@ -0,0 +1,33 @@ +/** + * Pick a value from an object at a given path. + * @param path path of the value to pick + * @param obj object to pick from + * @returns the value at the path or undefined + * @throws if obj is not an object + */ +export function pick(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; +} diff --git a/apps/core/config/src/public-api.ts b/apps/core/config/src/public-api.ts new file mode 100644 index 000000000..ecc72d696 --- /dev/null +++ b/apps/core/config/src/public-api.ts @@ -0,0 +1,5 @@ +/* + * Public API Surface of config + */ + +export * from './lib'; diff --git a/apps/core/config/src/test.ts b/apps/core/config/src/test.ts new file mode 100644 index 000000000..b22c5d4af --- /dev/null +++ b/apps/core/config/src/test.ts @@ -0,0 +1,25 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import 'zone.js'; +import 'zone.js/testing'; +import { getTestBed } from '@angular/core/testing'; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; + +declare const require: { + context( + path: string, + deep?: boolean, + filter?: RegExp + ): { + keys(): string[]; + (id: string): T; + }; +}; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), { teardown: { destroyAfterEach: true } }); + +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().map(context); diff --git a/apps/core/config/tsconfig.lib.json b/apps/core/config/tsconfig.lib.json new file mode 100644 index 000000000..a7141c543 --- /dev/null +++ b/apps/core/config/tsconfig.lib.json @@ -0,0 +1,20 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "outDir": "../../../out-tsc/lib", + "target": "es2015", + "declaration": true, + "declarationMap": true, + "inlineSources": true, + "types": [], + "lib": [ + "dom", + "es2018" + ] + }, + "exclude": [ + "src/test.ts", + "**/*.spec.ts" + ] +} diff --git a/apps/core/config/tsconfig.lib.prod.json b/apps/core/config/tsconfig.lib.prod.json new file mode 100644 index 000000000..06de549e1 --- /dev/null +++ b/apps/core/config/tsconfig.lib.prod.json @@ -0,0 +1,10 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.lib.json", + "compilerOptions": { + "declarationMap": false + }, + "angularCompilerOptions": { + "compilationMode": "partial" + } +} diff --git a/apps/core/config/tsconfig.spec.json b/apps/core/config/tsconfig.spec.json new file mode 100644 index 000000000..85392ee8f --- /dev/null +++ b/apps/core/config/tsconfig.spec.json @@ -0,0 +1,17 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "outDir": "../../../out-tsc/spec", + "types": [ + "jasmine" + ] + }, + "files": [ + "src/test.ts" + ], + "include": [ + "**/*.spec.ts", + "**/*.d.ts" + ] +} diff --git a/apps/core/environment/src/test.ts b/apps/core/environment/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/core/environment/src/test.ts +++ b/apps/core/environment/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/core/logger/README.md b/apps/core/logger/README.md new file mode 100644 index 000000000..da658a08a --- /dev/null +++ b/apps/core/logger/README.md @@ -0,0 +1,25 @@ +# Logger + +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 logger` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project logger`. + +> Note: Don't forget to add `--project logger` or else it will be added to the default project in your `angular.json` file. + +## Build + +Run `ng build logger` to build the project. The build artifacts will be stored in the `dist/` directory. + +## Publishing + +After building your library with `ng build logger`, go to the dist folder `cd dist/logger` and run `npm publish`. + +## Running unit tests + +Run `ng test logger` 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. diff --git a/apps/core/logger/karma.conf.js b/apps/core/logger/karma.conf.js new file mode 100644 index 000000000..6f5619a69 --- /dev/null +++ b/apps/core/logger/karma.conf.js @@ -0,0 +1,43 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html +const customLaunchers = require('../../../karma/custom-launchers'); +const junitReporter = require('../../../karma/junit-reporter')('core-logger'); +const coverageReporter = require('../../../karma/coverage-reporter')('core-logger'); + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage'), + require('karma-junit-reporter'), + require('@angular-devkit/build-angular/plugins/karma'), + ], + client: { + jasmine: { + // you can add configuration options for Jasmine here + // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html + // for example, you can disable the random execution with `random: false` + // or set a specific seed with `seed: 4321` + }, + clearContext: false, // leave Jasmine Spec Runner output visible in browser + }, + jasmineHtmlReporter: { + suppressAll: true, // removes the duplicated traces + }, + junitReporter, + coverageReporter, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + customLaunchers, + singleRun: false, + restartOnFileChange: true, + }); +}; diff --git a/apps/core/logger/ng-package.json b/apps/core/logger/ng-package.json new file mode 100644 index 000000000..4c81d52e6 --- /dev/null +++ b/apps/core/logger/ng-package.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../../dist/core/logger", + "lib": { + "entryFile": "src/public-api.ts" + } +} \ No newline at end of file diff --git a/apps/core/logger/package.json b/apps/core/logger/package.json new file mode 100644 index 000000000..97e1f9a1b --- /dev/null +++ b/apps/core/logger/package.json @@ -0,0 +1,11 @@ +{ + "name": "@core/logger", + "version": "0.0.1", + "peerDependencies": { + "@angular/common": "^12.2.0", + "@angular/core": "^12.2.0" + }, + "dependencies": { + "tslib": "^2.3.0" + } +} \ No newline at end of file diff --git a/apps/core/logger/src/lib/console-log.provider.spec.ts b/apps/core/logger/src/lib/console-log.provider.spec.ts new file mode 100644 index 000000000..89e353126 --- /dev/null +++ b/apps/core/logger/src/lib/console-log.provider.spec.ts @@ -0,0 +1,51 @@ +import { createServiceFactory, SpectatorService } from '@ngneat/spectator'; +import { ConsoleLogProvider } from './console-log.provider'; +import { LogLevel } from './log-level'; + +describe('ConsoleLogProvider', () => { + let spectator: SpectatorService; + + const createService = createServiceFactory({ + service: ConsoleLogProvider, + }); + + beforeEach(() => { + spectator = createService(); + }); + + it('should create', () => { + expect(spectator.service).toBeTruthy(); + }); + + describe('log', () => { + it('should call console.debug', () => { + spyOn(console, 'debug'); + spectator.service.log(LogLevel.DEBUG, 'test'); + expect(console.debug).toHaveBeenCalledWith('test'); + }); + + it('should call console.info', () => { + spyOn(console, 'info'); + spectator.service.log(LogLevel.INFO, 'test'); + expect(console.info).toHaveBeenCalledWith('test'); + }); + + it('should call console.warn', () => { + spyOn(console, 'warn'); + spectator.service.log(LogLevel.WARN, 'test'); + expect(console.warn).toHaveBeenCalledWith('test'); + }); + + it('should call console.error', () => { + spyOn(console, 'error'); + spectator.service.log(LogLevel.ERROR, 'test'); + expect(console.error).toHaveBeenCalledWith('test'); + }); + + it('should not call console.log', () => { + spyOn(console, 'log'); + spectator.service.log(LogLevel.OFF, 'test'); + expect(console.log).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/apps/core/logger/src/lib/console-log.provider.ts b/apps/core/logger/src/lib/console-log.provider.ts new file mode 100644 index 000000000..a9c1ee539 --- /dev/null +++ b/apps/core/logger/src/lib/console-log.provider.ts @@ -0,0 +1,25 @@ +import { Injectable } from '@angular/core'; +import { LogLevel } from './log-level'; +import { LogProvider } from './log.provider'; + +@Injectable() +export class ConsoleLogProvider implements LogProvider { + log(logLevel: LogLevel, message: string, ...optionalParams: any[]): void { + switch (logLevel) { + case LogLevel.DEBUG: + console.debug(message, ...optionalParams); + break; + case LogLevel.INFO: + console.info(message, ...optionalParams); + break; + case LogLevel.WARN: + console.warn(message, ...optionalParams); + break; + case LogLevel.ERROR: + console.error(message, ...optionalParams); + break; + case LogLevel.OFF: + break; + } + } +} diff --git a/apps/core/logger/src/lib/index.ts b/apps/core/logger/src/lib/index.ts new file mode 100644 index 000000000..b662b7221 --- /dev/null +++ b/apps/core/logger/src/lib/index.ts @@ -0,0 +1,8 @@ +// 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 diff --git a/apps/core/logger/src/lib/log-level.ts b/apps/core/logger/src/lib/log-level.ts new file mode 100644 index 000000000..6030bc132 --- /dev/null +++ b/apps/core/logger/src/lib/log-level.ts @@ -0,0 +1,14 @@ +export enum LogLevel { + DEBUG = 0, + INFO = 1, + WARN = 2, + ERROR = 3, + OFF = 4, + + // aliases + debug = 0, + info = 1, + warn = 2, + error = 3, + off = 4, +} diff --git a/apps/core/logger/src/lib/log.provider.ts b/apps/core/logger/src/lib/log.provider.ts new file mode 100644 index 000000000..e66f74464 --- /dev/null +++ b/apps/core/logger/src/lib/log.provider.ts @@ -0,0 +1,6 @@ +import { Injectable } from '@angular/core'; +import { LogLevel } from './log-level'; + +export interface LogProvider { + log(logLevel: LogLevel, message: string, ...optionalParams: any[]): void; +} diff --git a/apps/core/logger/src/lib/logger.module.ts b/apps/core/logger/src/lib/logger.module.ts new file mode 100644 index 000000000..9b8018c6f --- /dev/null +++ b/apps/core/logger/src/lib/logger.module.ts @@ -0,0 +1,31 @@ +import { ModuleWithProviders, NgModule } from '@angular/core'; +import { Config } from '@core/config'; +import { ConsoleLogProvider } from './console-log.provider'; +import { Logger } from './logger.service'; +import { LOG_PROVIDER, LOG_LEVEL } from './tokens'; + +export function _logLevelProviderFactory(config: Config) { + return config.get('@core/logger.logLevel'); +} + +@NgModule({}) +export class CoreLoggerModule { + static forRoot(): ModuleWithProviders { + return { + ngModule: CoreLoggerModule, + providers: [ + Logger, + { + provide: LOG_PROVIDER, + useClass: ConsoleLogProvider, + multi: true, + }, + { + provide: LOG_LEVEL, + useFactory: _logLevelProviderFactory, + deps: [Config], + }, + ], + }; + } +} diff --git a/apps/core/logger/src/lib/logger.service.spec.ts b/apps/core/logger/src/lib/logger.service.spec.ts new file mode 100644 index 000000000..247649931 --- /dev/null +++ b/apps/core/logger/src/lib/logger.service.spec.ts @@ -0,0 +1,140 @@ +import { SpectatorService, createServiceFactory } from '@ngneat/spectator'; +import { LogLevel } from './log-level'; +import { Logger } from './logger.service'; +import { LOG_PROVIDER } from './tokens'; +import { LOG_LEVEL } from '.'; + +describe('LoggerService', () => { + let spectator: SpectatorService; + let logProviderMock1 = jasmine.createSpyObj('LogProvider', ['log']); + let logProviderMock2 = jasmine.createSpyObj('LogProvider', ['log']); + + const createService = createServiceFactory({ + service: Logger, + mocks: [Logger], + providers: [ + { + provide: LOG_PROVIDER, + useValue: logProviderMock1, + multi: true, + }, + { + provide: LOG_PROVIDER, + useValue: logProviderMock2, + multi: true, + }, + { + provide: LOG_LEVEL, + useValue: LogLevel.DEBUG, + }, + ], + }); + + beforeEach(() => { + spectator = createService(); + }); + + it('should create', () => { + expect(spectator.service).toBeTruthy(); + }); + + const testData: { + serviceLogLevel: LogLevel; + callLogLevel: LogLevel; + shoudCall: boolean; + }[] = [ + { serviceLogLevel: LogLevel.DEBUG, callLogLevel: LogLevel.DEBUG, shoudCall: true }, + { serviceLogLevel: LogLevel.DEBUG, callLogLevel: LogLevel.INFO, shoudCall: true }, + { serviceLogLevel: LogLevel.DEBUG, callLogLevel: LogLevel.WARN, shoudCall: true }, + { serviceLogLevel: LogLevel.DEBUG, callLogLevel: LogLevel.ERROR, shoudCall: true }, + + { serviceLogLevel: LogLevel.INFO, callLogLevel: LogLevel.DEBUG, shoudCall: false }, + { serviceLogLevel: LogLevel.INFO, callLogLevel: LogLevel.INFO, shoudCall: true }, + { serviceLogLevel: LogLevel.INFO, callLogLevel: LogLevel.WARN, shoudCall: true }, + { serviceLogLevel: LogLevel.INFO, callLogLevel: LogLevel.ERROR, shoudCall: true }, + + { serviceLogLevel: LogLevel.WARN, callLogLevel: LogLevel.DEBUG, shoudCall: false }, + { serviceLogLevel: LogLevel.WARN, callLogLevel: LogLevel.INFO, shoudCall: false }, + { serviceLogLevel: LogLevel.WARN, callLogLevel: LogLevel.WARN, shoudCall: true }, + { serviceLogLevel: LogLevel.WARN, callLogLevel: LogLevel.ERROR, shoudCall: true }, + + { serviceLogLevel: LogLevel.ERROR, callLogLevel: LogLevel.DEBUG, shoudCall: false }, + { serviceLogLevel: LogLevel.ERROR, callLogLevel: LogLevel.INFO, shoudCall: false }, + { serviceLogLevel: LogLevel.ERROR, callLogLevel: LogLevel.WARN, shoudCall: false }, + { serviceLogLevel: LogLevel.ERROR, callLogLevel: LogLevel.ERROR, shoudCall: true }, + + { serviceLogLevel: LogLevel.OFF, callLogLevel: LogLevel.DEBUG, shoudCall: false }, + { serviceLogLevel: LogLevel.OFF, callLogLevel: LogLevel.INFO, shoudCall: false }, + { serviceLogLevel: LogLevel.OFF, callLogLevel: LogLevel.WARN, shoudCall: false }, + { serviceLogLevel: LogLevel.OFF, callLogLevel: LogLevel.ERROR, shoudCall: false }, + ]; + + describe('log()', () => { + beforeEach(() => { + logProviderMock1.log.calls.reset(); + logProviderMock2.log.calls.reset(); + }); + + for (const test of testData) { + if (test.shoudCall) { + it(`should call logProvider if logLevel is ${LogLevel[test.callLogLevel]} and serviceLogLevel is ${ + LogLevel[test.serviceLogLevel] + }`, () => { + spectator.service['_logLevel'] = test.serviceLogLevel; + spectator.service.log(test.callLogLevel, 'test'); + expect(logProviderMock1.log).toHaveBeenCalledTimes(1); + expect(logProviderMock2.log).toHaveBeenCalledTimes(1); + }); + } else { + it(`should not call logProvider if logLevel is ${LogLevel[test.callLogLevel]} and serviceLogLevel is ${ + LogLevel[test.serviceLogLevel] + }`, () => { + spectator.service['_logLevel'] = test.serviceLogLevel; + spectator.service.log(test.callLogLevel, 'test'); + expect(logProviderMock1.log).not.toHaveBeenCalled(); + expect(logProviderMock2.log).not.toHaveBeenCalled(); + }); + } + } + }); + + describe('warn()', () => { + it('should call log and logLevel should be WARN', () => { + spyOn(spectator.service, 'log'); + + spectator.service.warn('test', 'data'); + + expect(spectator.service.log).toHaveBeenCalledWith(LogLevel.WARN, 'test', 'data'); + }); + }); + + describe('info()', () => { + it('should call log and logLevel should be INFO', () => { + spyOn(spectator.service, 'log'); + + spectator.service.info('test', 'data'); + + expect(spectator.service.log).toHaveBeenCalledWith(LogLevel.INFO, 'test', 'data'); + }); + }); + + describe('debug()', () => { + it('should call log and logLevel should be DEBUG', () => { + spyOn(spectator.service, 'log'); + + spectator.service.debug('test', 'data'); + + expect(spectator.service.log).toHaveBeenCalledWith(LogLevel.DEBUG, 'test', 'data'); + }); + }); + + describe('error()', () => { + it('should call log and logLevel should be ERROR', () => { + spyOn(spectator.service, 'log'); + + spectator.service.error('test', 'data'); + + expect(spectator.service.log).toHaveBeenCalledWith(LogLevel.ERROR, 'test', 'data'); + }); + }); +}); diff --git a/apps/core/logger/src/lib/logger.service.ts b/apps/core/logger/src/lib/logger.service.ts new file mode 100644 index 000000000..7ad4b53e0 --- /dev/null +++ b/apps/core/logger/src/lib/logger.service.ts @@ -0,0 +1,39 @@ +import { Inject, Injectable } from '@angular/core'; + +import { LogLevel } from './log-level'; +import { LogProvider } from './log.provider'; +import { LOG_LEVEL, LOG_PROVIDER } from './tokens'; + +@Injectable() +export class Logger { + constructor(@Inject(LOG_PROVIDER) private readonly _loggerProviders: LogProvider[], @Inject(LOG_LEVEL) private _logLevel: LogLevel) {} + + log(logLevel: LogLevel, message: string, ...optionalParams: any[]): void { + if (this._logLevel === LogLevel.OFF) { + return; + } + + if (this._logLevel <= logLevel) { + console.log(this._logLevel, logLevel, this._logLevel <= logLevel); + this._loggerProviders.forEach((provider) => { + provider.log(logLevel, message, ...optionalParams); + }); + } + } + + warn(message: string, ...optionalParams: any[]): void { + this.log(LogLevel.WARN, message, ...optionalParams); + } + + info(message: string, ...optionalParams: any[]): void { + this.log(LogLevel.INFO, message, ...optionalParams); + } + + debug(message: string, ...optionalParams: any[]): void { + this.log(LogLevel.DEBUG, message, ...optionalParams); + } + + error(message: string, ...optionalParams: any[]): void { + this.log(LogLevel.ERROR, message, ...optionalParams); + } +} diff --git a/apps/core/logger/src/lib/tokens.ts b/apps/core/logger/src/lib/tokens.ts new file mode 100644 index 000000000..459879ff5 --- /dev/null +++ b/apps/core/logger/src/lib/tokens.ts @@ -0,0 +1,6 @@ +import { LogProvider } from './log.provider'; +import { InjectionToken } from '@angular/core'; +import { LogLevel } from './log-level'; + +export const LOG_PROVIDER = new InjectionToken('LOG_PROVIDER'); +export const LOG_LEVEL = new InjectionToken('LOG_LEVEL'); diff --git a/apps/core/logger/src/public-api.ts b/apps/core/logger/src/public-api.ts new file mode 100644 index 000000000..4cc7ec56a --- /dev/null +++ b/apps/core/logger/src/public-api.ts @@ -0,0 +1,5 @@ +/* + * Public API Surface of logger + */ + +export * from './lib'; diff --git a/apps/core/logger/src/test.ts b/apps/core/logger/src/test.ts new file mode 100644 index 000000000..b22c5d4af --- /dev/null +++ b/apps/core/logger/src/test.ts @@ -0,0 +1,25 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import 'zone.js'; +import 'zone.js/testing'; +import { getTestBed } from '@angular/core/testing'; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; + +declare const require: { + context( + path: string, + deep?: boolean, + filter?: RegExp + ): { + keys(): string[]; + (id: string): T; + }; +}; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), { teardown: { destroyAfterEach: true } }); + +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().map(context); diff --git a/apps/core/logger/tsconfig.lib.json b/apps/core/logger/tsconfig.lib.json new file mode 100644 index 000000000..a7141c543 --- /dev/null +++ b/apps/core/logger/tsconfig.lib.json @@ -0,0 +1,20 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "outDir": "../../../out-tsc/lib", + "target": "es2015", + "declaration": true, + "declarationMap": true, + "inlineSources": true, + "types": [], + "lib": [ + "dom", + "es2018" + ] + }, + "exclude": [ + "src/test.ts", + "**/*.spec.ts" + ] +} diff --git a/apps/core/logger/tsconfig.lib.prod.json b/apps/core/logger/tsconfig.lib.prod.json new file mode 100644 index 000000000..06de549e1 --- /dev/null +++ b/apps/core/logger/tsconfig.lib.prod.json @@ -0,0 +1,10 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.lib.json", + "compilerOptions": { + "declarationMap": false + }, + "angularCompilerOptions": { + "compilationMode": "partial" + } +} diff --git a/apps/core/logger/tsconfig.spec.json b/apps/core/logger/tsconfig.spec.json new file mode 100644 index 000000000..85392ee8f --- /dev/null +++ b/apps/core/logger/tsconfig.spec.json @@ -0,0 +1,17 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "outDir": "../../../out-tsc/spec", + "types": [ + "jasmine" + ] + }, + "files": [ + "src/test.ts" + ], + "include": [ + "**/*.spec.ts", + "**/*.d.ts" + ] +} diff --git a/apps/core/signalr/src/test.ts b/apps/core/signalr/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/core/signalr/src/test.ts +++ b/apps/core/signalr/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/domain/availability/src/lib/availability.service.ts b/apps/domain/availability/src/lib/availability.service.ts index 032c8d253..9bdedbc3b 100644 --- a/apps/domain/availability/src/lib/availability.service.ts +++ b/apps/domain/availability/src/lib/availability.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { ItemDTO } from '@swagger/cat'; +import { ItemDTO, SearchService } from '@swagger/cat'; import { AvailabilityDTO, BranchDTO, OLAAvailabilityDTO, StoreCheckoutService, SupplierDTO } from '@swagger/checkout'; import { combineLatest, Observable, of } from 'rxjs'; import { @@ -61,7 +61,23 @@ export class DomainAvailabilityService { @memorize() getCurrentBranch(): Observable { return this._stock.StockCurrentBranch().pipe( - map((response) => response.result), + 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() ); } diff --git a/apps/domain/availability/src/test.ts b/apps/domain/availability/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/domain/availability/src/test.ts +++ b/apps/domain/availability/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/domain/cart/src/test.ts b/apps/domain/cart/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/domain/cart/src/test.ts +++ b/apps/domain/cart/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/domain/catalog/src/test.ts b/apps/domain/catalog/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/domain/catalog/src/test.ts +++ b/apps/domain/catalog/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/domain/checkout/src/lib/checkout.service.ts b/apps/domain/checkout/src/lib/checkout.service.ts index edc8f9359..0dda828d2 100644 --- a/apps/domain/checkout/src/lib/checkout.service.ts +++ b/apps/domain/checkout/src/lib/checkout.service.ts @@ -30,11 +30,13 @@ import * as DomainCheckoutSelectors from './store/domain-checkout.selectors'; import * as DomainCheckoutActions from './store/domain-checkout.actions'; import { DomainAvailabilityService } from '@domain/availability'; import { HttpErrorResponse } from '@angular/common/http'; +import { ApplicationService } from '@core/application'; @Injectable() export class DomainCheckoutService { constructor( private store: Store, + private applicationService: ApplicationService, private storeCheckoutService: StoreCheckoutService, private orderCheckoutService: OrderCheckoutService, private availabilityService: DomainAvailabilityService @@ -105,7 +107,8 @@ export class DomainCheckoutService { shoppingCart, }) ) - ) + ), + tap((shoppingCart) => this.updateProcessCount(processId, shoppingCart?.items?.length)) ) ) ); @@ -253,7 +256,8 @@ export class DomainCheckoutService { }) .pipe( map((response) => response.result), - tap((shoppingCart) => this.store.dispatch(DomainCheckoutActions.setShoppingCart({ processId, shoppingCart }))) + tap((shoppingCart) => this.store.dispatch(DomainCheckoutActions.setShoppingCart({ processId, shoppingCart }))), + tap((shoppingCart) => this.updateProcessCount(processId, shoppingCart?.items?.length)) ) ) ); @@ -888,4 +892,12 @@ export class DomainCheckoutService { return this.store.select(DomainCheckoutSelectors.selectSpecialComment, { processId }); } //#endregion + + //#region Common + + private updateProcessCount(processId: number, count: number) { + this.applicationService.patchProcess(processId, { data: { count } }); + } + + //#endregion } diff --git a/apps/domain/checkout/src/test.ts b/apps/domain/checkout/src/test.ts index dec5191a9..4d5f0245f 100644 --- a/apps/domain/checkout/src/test.ts +++ b/apps/domain/checkout/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/domain/checkout/tsconfig.lib.json b/apps/domain/checkout/tsconfig.lib.json index 1c34c36b8..02b80298e 100644 --- a/apps/domain/checkout/tsconfig.lib.json +++ b/apps/domain/checkout/tsconfig.lib.json @@ -2,6 +2,7 @@ "extends": "../../../tsconfig.json", "compilerOptions": { "outDir": "../../../out-tsc/lib", + "declarationMap": true, "target": "es2015", "declaration": true, "inlineSources": true, diff --git a/apps/domain/checkout/tsconfig.lib.prod.json b/apps/domain/checkout/tsconfig.lib.prod.json index b1d501abb..2617a83e3 100644 --- a/apps/domain/checkout/tsconfig.lib.prod.json +++ b/apps/domain/checkout/tsconfig.lib.prod.json @@ -1,5 +1,8 @@ { "extends": "./tsconfig.lib.json", + "compilerOptions": { + "declarationMap": false + }, "angularCompilerOptions": { "enableIvy": false } diff --git a/apps/domain/crm/src/test.ts b/apps/domain/crm/src/test.ts index dec5191a9..4d5f0245f 100644 --- a/apps/domain/crm/src/test.ts +++ b/apps/domain/crm/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/domain/crm/tsconfig.lib.json b/apps/domain/crm/tsconfig.lib.json index 1c34c36b8..02b80298e 100644 --- a/apps/domain/crm/tsconfig.lib.json +++ b/apps/domain/crm/tsconfig.lib.json @@ -2,6 +2,7 @@ "extends": "../../../tsconfig.json", "compilerOptions": { "outDir": "../../../out-tsc/lib", + "declarationMap": true, "target": "es2015", "declaration": true, "inlineSources": true, diff --git a/apps/domain/crm/tsconfig.lib.prod.json b/apps/domain/crm/tsconfig.lib.prod.json index b1d501abb..2617a83e3 100644 --- a/apps/domain/crm/tsconfig.lib.prod.json +++ b/apps/domain/crm/tsconfig.lib.prod.json @@ -1,5 +1,8 @@ { "extends": "./tsconfig.lib.json", + "compilerOptions": { + "declarationMap": false + }, "angularCompilerOptions": { "enableIvy": false } diff --git a/apps/domain/defs/src/test.ts b/apps/domain/defs/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/domain/defs/src/test.ts +++ b/apps/domain/defs/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/domain/isa/README.md b/apps/domain/isa/README.md new file mode 100644 index 000000000..5dcd432db --- /dev/null +++ b/apps/domain/isa/README.md @@ -0,0 +1,25 @@ +# Isa + +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 isa` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project isa`. + +> Note: Don't forget to add `--project isa` or else it will be added to the default project in your `angular.json` file. + +## Build + +Run `ng build isa` to build the project. The build artifacts will be stored in the `dist/` directory. + +## Publishing + +After building your library with `ng build isa`, go to the dist folder `cd dist/isa` and run `npm publish`. + +## Running unit tests + +Run `ng test isa` 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. diff --git a/apps/domain/isa/karma.conf.js b/apps/domain/isa/karma.conf.js new file mode 100644 index 000000000..5b754f365 --- /dev/null +++ b/apps/domain/isa/karma.conf.js @@ -0,0 +1,41 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage'), + require('@angular-devkit/build-angular/plugins/karma'), + ], + client: { + jasmine: { + // you can add configuration options for Jasmine here + // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html + // for example, you can disable the random execution with `random: false` + // or set a specific seed with `seed: 4321` + }, + clearContext: false, // leave Jasmine Spec Runner output visible in browser + }, + jasmineHtmlReporter: { + suppressAll: true, // removes the duplicated traces + }, + coverageReporter: { + dir: require('path').join(__dirname, '../../../coverage/domain/isa'), + subdir: '.', + reporters: [{ type: 'html' }, { type: 'text-summary' }], + }, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + singleRun: false, + restartOnFileChange: true, + }); +}; diff --git a/apps/domain/isa/ng-package.json b/apps/domain/isa/ng-package.json new file mode 100644 index 000000000..3c14d6023 --- /dev/null +++ b/apps/domain/isa/ng-package.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../../dist/domain/isa", + "lib": { + "entryFile": "src/public-api.ts" + } +} \ No newline at end of file diff --git a/apps/domain/isa/package.json b/apps/domain/isa/package.json new file mode 100644 index 000000000..b61e569c2 --- /dev/null +++ b/apps/domain/isa/package.json @@ -0,0 +1,11 @@ +{ + "name": "@domain/isa", + "version": "0.0.1", + "peerDependencies": { + "@angular/common": "^12.2.0", + "@angular/core": "^12.2.0" + }, + "dependencies": { + "tslib": "^2.3.0" + } +} \ No newline at end of file diff --git a/apps/domain/isa/src/lib/dashboard.service.ts b/apps/domain/isa/src/lib/dashboard.service.ts new file mode 100644 index 000000000..aa138330a --- /dev/null +++ b/apps/domain/isa/src/lib/dashboard.service.ts @@ -0,0 +1,11 @@ +import { Injectable } from '@angular/core'; +import { InfoService } from '@swagger/isa'; + +@Injectable() +export class DomainDashboardService { + constructor(private readonly _infoService: InfoService) {} + + feed() { + return this._infoService.InfoInfo({}); + } +} diff --git a/apps/domain/isa/src/lib/defs/index.ts b/apps/domain/isa/src/lib/defs/index.ts new file mode 100644 index 000000000..7adfbd50a --- /dev/null +++ b/apps/domain/isa/src/lib/defs/index.ts @@ -0,0 +1,5 @@ +// start:ng42.barrel +export * from './kpi-feed-item'; +export * from './kpi-feed'; +export * from './products-feed'; +// end:ng42.barrel diff --git a/apps/domain/isa/src/lib/defs/kpi-feed-item.ts b/apps/domain/isa/src/lib/defs/kpi-feed-item.ts new file mode 100644 index 000000000..1a631e35d --- /dev/null +++ b/apps/domain/isa/src/lib/defs/kpi-feed-item.ts @@ -0,0 +1,5 @@ +export interface KpiFeedItem { + label?: string; + target?: number; + actual?: number; +} diff --git a/apps/domain/isa/src/lib/defs/kpi-feed.ts b/apps/domain/isa/src/lib/defs/kpi-feed.ts new file mode 100644 index 000000000..eea943a34 --- /dev/null +++ b/apps/domain/isa/src/lib/defs/kpi-feed.ts @@ -0,0 +1,7 @@ +import { FeedDTO } from '@swagger/isa'; +import { KpiFeedItem } from './kpi-feed-item'; + +export interface KpiFeed extends FeedDTO { + type: 'kpi'; + items: KpiFeedItem[]; +} diff --git a/apps/domain/isa/src/lib/defs/products-feed.ts b/apps/domain/isa/src/lib/defs/products-feed.ts new file mode 100644 index 000000000..59badf861 --- /dev/null +++ b/apps/domain/isa/src/lib/defs/products-feed.ts @@ -0,0 +1,7 @@ +import { ItemDTO } from '@swagger/cat'; +import { FeedDTO } from '@swagger/isa'; + +export interface ProductsFeed extends FeedDTO { + type: 'products'; + items: ItemDTO[]; +} diff --git a/apps/domain/isa/src/lib/domain-isa.module.ts b/apps/domain/isa/src/lib/domain-isa.module.ts new file mode 100644 index 000000000..981f5f961 --- /dev/null +++ b/apps/domain/isa/src/lib/domain-isa.module.ts @@ -0,0 +1,12 @@ +import { ModuleWithProviders, NgModule } from '@angular/core'; +import { DomainDashboardService } from './dashboard.service'; + +@NgModule({}) +export class DomainIsaModule { + static forRoot(): ModuleWithProviders { + return { + ngModule: DomainIsaModule, + providers: [DomainDashboardService], + }; + } +} diff --git a/apps/domain/isa/src/lib/index.ts b/apps/domain/isa/src/lib/index.ts new file mode 100644 index 000000000..436b7a1ba --- /dev/null +++ b/apps/domain/isa/src/lib/index.ts @@ -0,0 +1,5 @@ +// start:ng42.barrel +export * from './defs'; +export * from './dashboard.service'; +export * from './domain-isa.module'; +// end:ng42.barrel diff --git a/apps/domain/isa/src/public-api.ts b/apps/domain/isa/src/public-api.ts new file mode 100644 index 000000000..6561e44fc --- /dev/null +++ b/apps/domain/isa/src/public-api.ts @@ -0,0 +1,5 @@ +/* + * Public API Surface of isa + */ + +export * from './lib'; diff --git a/apps/domain/isa/src/test.ts b/apps/domain/isa/src/test.ts new file mode 100644 index 000000000..b22c5d4af --- /dev/null +++ b/apps/domain/isa/src/test.ts @@ -0,0 +1,25 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import 'zone.js'; +import 'zone.js/testing'; +import { getTestBed } from '@angular/core/testing'; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; + +declare const require: { + context( + path: string, + deep?: boolean, + filter?: RegExp + ): { + keys(): string[]; + (id: string): T; + }; +}; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), { teardown: { destroyAfterEach: true } }); + +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().map(context); diff --git a/apps/domain/isa/tsconfig.lib.json b/apps/domain/isa/tsconfig.lib.json new file mode 100644 index 000000000..a7141c543 --- /dev/null +++ b/apps/domain/isa/tsconfig.lib.json @@ -0,0 +1,20 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "outDir": "../../../out-tsc/lib", + "target": "es2015", + "declaration": true, + "declarationMap": true, + "inlineSources": true, + "types": [], + "lib": [ + "dom", + "es2018" + ] + }, + "exclude": [ + "src/test.ts", + "**/*.spec.ts" + ] +} diff --git a/apps/domain/isa/tsconfig.lib.prod.json b/apps/domain/isa/tsconfig.lib.prod.json new file mode 100644 index 000000000..06de549e1 --- /dev/null +++ b/apps/domain/isa/tsconfig.lib.prod.json @@ -0,0 +1,10 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.lib.json", + "compilerOptions": { + "declarationMap": false + }, + "angularCompilerOptions": { + "compilationMode": "partial" + } +} diff --git a/apps/domain/isa/tsconfig.spec.json b/apps/domain/isa/tsconfig.spec.json new file mode 100644 index 000000000..85392ee8f --- /dev/null +++ b/apps/domain/isa/tsconfig.spec.json @@ -0,0 +1,17 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "outDir": "../../../out-tsc/spec", + "types": [ + "jasmine" + ] + }, + "files": [ + "src/test.ts" + ], + "include": [ + "**/*.spec.ts", + "**/*.d.ts" + ] +} diff --git a/apps/domain/oms/src/test.ts b/apps/domain/oms/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/domain/oms/src/test.ts +++ b/apps/domain/oms/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/domain/printer/src/test.ts b/apps/domain/printer/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/domain/printer/src/test.ts +++ b/apps/domain/printer/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/domain/remission/README.md b/apps/domain/remission/README.md new file mode 100644 index 000000000..84cec88e6 --- /dev/null +++ b/apps/domain/remission/README.md @@ -0,0 +1,25 @@ +# Remission + +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 remission` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project remission`. + +> Note: Don't forget to add `--project remission` or else it will be added to the default project in your `angular.json` file. + +## Build + +Run `ng build remission` to build the project. The build artifacts will be stored in the `dist/` directory. + +## Publishing + +After building your library with `ng build remission`, go to the dist folder `cd dist/remission` and run `npm publish`. + +## Running unit tests + +Run `ng test remission` 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. diff --git a/apps/domain/remission/karma.conf.js b/apps/domain/remission/karma.conf.js new file mode 100644 index 000000000..ed41a09c1 --- /dev/null +++ b/apps/domain/remission/karma.conf.js @@ -0,0 +1,43 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html +const customLaunchers = require('../../../karma/custom-launchers'); +const junitReporter = require('../../../karma/junit-reporter')('domain-remission'); +const coverageReporter = require('../../../karma/coverage-reporter')('domain-remission'); + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage'), + require('karma-junit-reporter'), + require('@angular-devkit/build-angular/plugins/karma'), + ], + client: { + jasmine: { + // you can add configuration options for Jasmine here + // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html + // for example, you can disable the random execution with `random: false` + // or set a specific seed with `seed: 4321` + }, + clearContext: false, // leave Jasmine Spec Runner output visible in browser + }, + jasmineHtmlReporter: { + suppressAll: true, // removes the duplicated traces + }, + coverageReporter, + junitReporter, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + customLaunchers, + singleRun: false, + restartOnFileChange: true, + }); +}; diff --git a/apps/domain/remission/ng-package.json b/apps/domain/remission/ng-package.json new file mode 100644 index 000000000..914e829b9 --- /dev/null +++ b/apps/domain/remission/ng-package.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../../dist/domain/remission", + "lib": { + "entryFile": "src/public-api.ts" + } +} \ No newline at end of file diff --git a/apps/domain/remission/package.json b/apps/domain/remission/package.json new file mode 100644 index 000000000..7404e45a1 --- /dev/null +++ b/apps/domain/remission/package.json @@ -0,0 +1,11 @@ +{ + "name": "@domain/remission", + "version": "0.0.1", + "peerDependencies": { + "@angular/common": "^12.2.0", + "@angular/core": "^12.2.0" + }, + "dependencies": { + "tslib": "^2.3.0" + } +} \ No newline at end of file diff --git a/apps/domain/remission/src/lib/defs/index.ts b/apps/domain/remission/src/lib/defs/index.ts new file mode 100644 index 000000000..c3f44b1a1 --- /dev/null +++ b/apps/domain/remission/src/lib/defs/index.ts @@ -0,0 +1 @@ +export * from './remission-list-item'; diff --git a/apps/domain/remission/src/lib/defs/remission-list-item.ts b/apps/domain/remission/src/lib/defs/remission-list-item.ts new file mode 100644 index 000000000..a4c1670b6 --- /dev/null +++ b/apps/domain/remission/src/lib/defs/remission-list-item.ts @@ -0,0 +1,22 @@ +import { PriceDTO, ReturnItemDTO, ReturnSuggestionDTO } from '@swagger/remi'; + +export interface RemissionListItem { + dtoType: 'return' | 'suggestion'; + dto: ReturnItemDTO | ReturnSuggestionDTO; + + title: string; + ean: string; + productGroup: string; + format: string; + formatDetail: string; + + price: PriceDTO; + placementType: string; + department: string; + + inStock: number; + remainingQuantity: number; + remissionQuantity: number; + remissionReason?: string; + assortment?: string; +} diff --git a/apps/domain/remission/src/lib/index.ts b/apps/domain/remission/src/lib/index.ts new file mode 100644 index 000000000..32534d4af --- /dev/null +++ b/apps/domain/remission/src/lib/index.ts @@ -0,0 +1,3 @@ +export * from './defs'; +export * from './remission.module'; +export * from './remission.service'; diff --git a/apps/domain/remission/src/lib/mappings/index.ts b/apps/domain/remission/src/lib/mappings/index.ts new file mode 100644 index 000000000..b37911cda --- /dev/null +++ b/apps/domain/remission/src/lib/mappings/index.ts @@ -0,0 +1,2 @@ +export * from './remission-list-item.mapping'; +export * from './return-item-dto.mapping'; diff --git a/apps/domain/remission/src/lib/mappings/remission-list-item.mapping.ts b/apps/domain/remission/src/lib/mappings/remission-list-item.mapping.ts new file mode 100644 index 000000000..80ecfa6c0 --- /dev/null +++ b/apps/domain/remission/src/lib/mappings/remission-list-item.mapping.ts @@ -0,0 +1,42 @@ +import { ReturnItemDTO, ReturnSuggestionDTO } from '@swagger/remi'; +import { RemissionListItem } from '../defs'; + +export function mapFromReturnSuggestionDTO(dto: ReturnSuggestionDTO): RemissionListItem { + return { + dtoType: 'suggestion', + dto: dto, + inStock: 0, + remainingQuantity: dto.remainingQuantityInStock, + remissionQuantity: dto.returnItem?.data?.predefinedReturnQuantity, + remissionReason: dto.returnReason, + title: [dto.product.contributors, dto.product.name].filter((f) => !!f).join(' - '), + department: dto.department, + ean: dto.product.ean, + productGroup: dto.product.productGroup, + format: dto.product.format, + formatDetail: dto.product.formatDetail, + price: dto.retailPrice, + placementType: dto.placementType, + assortment: dto.assortment, + }; +} + +export function mapFromReturnItemDTO(dto: ReturnItemDTO): RemissionListItem { + return { + dtoType: 'return', + dto: dto, + inStock: 0, + remissionQuantity: dto.predefinedReturnQuantity, + remainingQuantity: dto.remainingQuantityInStock, + remissionReason: dto.returnReason, + title: [dto.product.contributors, dto.product.name].filter((f) => !!f).join(' - '), + department: dto.department, + ean: dto.product.ean, + productGroup: dto.product.productGroup, + format: dto.product.format, + formatDetail: dto.product.formatDetail, + price: dto.retailPrice, + placementType: dto.placementType, + assortment: dto.assortment, + }; +} diff --git a/apps/domain/remission/src/lib/mappings/return-item-dto.mapping.ts b/apps/domain/remission/src/lib/mappings/return-item-dto.mapping.ts new file mode 100644 index 000000000..fb66a31ed --- /dev/null +++ b/apps/domain/remission/src/lib/mappings/return-item-dto.mapping.ts @@ -0,0 +1,36 @@ +import { ItemDTO } from '@swagger/cat'; +import { PriceDTO, ReturnItemDTO, StockDTO } from '@swagger/remi'; + +function mapAssortment(itemDto: ItemDTO): string { + // input: + // { + // "enabled": true, + // "key": "SOB", + // "value": "Basissortiment" + // } + // output: + // Basissortiment|B + return itemDto.features?.reduce((acc, curr) => acc + `${curr.value || ''}|${curr.key[curr.key.length - 1]}`, ''); +} + +function mapRetailPrice(itemDto: ItemDTO): PriceDTO { + let availability = itemDto?.storeAvailabilities?.find((f) => !!f); + + if (!availability) { + availability = itemDto?.catalogAvailability; + } + + return availability.price; +} + +export function fromItemDto(itemDto: ItemDTO, stock: StockDTO): ReturnItemDTO { + return { + id: itemDto.ids['dig'], + product: itemDto.product, + assortment: mapAssortment(itemDto), + retailPrice: mapRetailPrice(itemDto), + stock: { id: stock.id }, + source: 'manually-added', + predefinedReturnQuantity: 1, + }; +} diff --git a/apps/domain/remission/src/lib/remission.module.ts b/apps/domain/remission/src/lib/remission.module.ts new file mode 100644 index 000000000..338cb7eb9 --- /dev/null +++ b/apps/domain/remission/src/lib/remission.module.ts @@ -0,0 +1,17 @@ +import { ModuleWithProviders, NgModule } from '@angular/core'; +import { DomainRemissionService } from './remission.service'; + +@NgModule({}) +export class DomainRemissionModule { + static forRoot(): ModuleWithProviders { + return { + ngModule: RootDomainRemissionModule, + }; + } +} + +@NgModule({ + imports: [], + providers: [DomainRemissionService], +}) +export class RootDomainRemissionModule {} diff --git a/apps/domain/remission/src/lib/remission.service.spec.ts b/apps/domain/remission/src/lib/remission.service.spec.ts new file mode 100644 index 000000000..1ab873880 --- /dev/null +++ b/apps/domain/remission/src/lib/remission.service.spec.ts @@ -0,0 +1,23 @@ +import { Logger } from '@core/logger'; +import { createServiceFactory, SpectatorService } from '@ngneat/spectator'; +import { SearchService } from '@swagger/cat'; +import { PackageService, RemiService, ReturnService, StockService, SupplierService } from '@swagger/remi'; +import { RemissionStore } from './store'; +import { DomainRemissionService } from './remission.service'; +import { DateAdapter } from '@ui/common'; + +describe('DomainRemissionService', () => { + let service: SpectatorService; + const createService = createServiceFactory({ + service: DomainRemissionService, + mocks: [RemiService, StockService, SupplierService, PackageService, ReturnService, SearchService, RemissionStore, Logger, DateAdapter], + }); + + beforeEach(() => { + service = createService(); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/apps/domain/remission/src/lib/remission.service.ts b/apps/domain/remission/src/lib/remission.service.ts new file mode 100644 index 000000000..e6bdec320 --- /dev/null +++ b/apps/domain/remission/src/lib/remission.service.ts @@ -0,0 +1,494 @@ +import { Injectable } from '@angular/core'; +import { ItemDTO, ListResponseArgsOfItemDTO, SearchService } from '@swagger/cat'; +import { + RemiService, + StockService, + SupplierService, + ReturnService, + RemiQueryTokenDTO, + QueryTokenDTO, + ReturnItemDTO, + StockDTO, + ReceiptDTO, + ReturnDTO, + ReturnQueryTokenDTO, +} from '@swagger/remi'; +import { memorize } from '@utils/common'; +import { Observable, of, throwError } from 'rxjs'; +import { catchError, map, shareReplay, switchMap } from 'rxjs/operators'; +import { RemissionListItem } from './defs'; +import { fromItemDto, mapFromReturnItemDTO, mapFromReturnSuggestionDTO } from './mappings'; +import { Logger } from '@core/logger'; +import { DateAdapter } from '@ui/common'; +import { RemissionPlacementType } from '@isa/remission'; + +@Injectable() +export class DomainRemissionService { + constructor( + private readonly _logger: Logger, + private readonly _remiService: RemiService, + private readonly _stockService: StockService, + private readonly _supplierService: SupplierService, + private readonly _returnService: ReturnService, + private readonly _search: SearchService, + private readonly _dateAdapter: DateAdapter + ) {} + + @memorize() + getCurrentStock() { + return this._stockService.StockCurrentStock().pipe( + map((res) => res.result), + catchError((err: Error) => { + this._logger.error('Fehler beim Laden des aktuellen Lagers', err); + return throwError(err); + }), + shareReplay() + ); + } + + @memorize() + getSuppliers() { + return this.getCurrentStock().pipe( + switchMap((stock) => + this._supplierService.SupplierGetSuppliers({ + stockId: stock.id, + }) + ), + map((res) => res.result), + catchError((err: Error) => { + this._logger.error('Fehler beim Laden der Lieferanten', err); + return throwError(err); + }), + shareReplay() + ); + } + + getSources() { + return of(['Pflichtremission', 'Abteilungsremission']); + } + + getQuerySettings(arg: { source: string; supplierId: number }) { + return this.getCurrentStock().pipe( + switchMap((stock) => + this._remiService + .RemiRemissionQuerySettings({ + remiType: arg.source, + supplierId: arg.supplierId, + stockId: stock.id, + }) + .pipe(map((res) => res.result)) + ), + catchError((err: Error) => { + this._logger.error('Fehler beim Laden des Filters', err); + return throwError(err); + }) + ); + } + + @memorize() + getProductGroups() { + return this.getCurrentStock().pipe( + switchMap((stock) => + this._remiService.RemiProductgroups({ stockId: stock.id }).pipe( + map((res) => res.result), + catchError((err: Error) => { + this._logger.error('Fehler beim Laden der Produktgruppen', err); + return throwError(err); + }) + ) + ), + shareReplay() + ); + } + + @memorize() + getReturnReasons() { + return this.getCurrentStock().pipe( + switchMap((stock) => + this._returnService + .ReturnGetReturnReasons({ + stockId: stock.id, + }) + .pipe( + map((res) => res.result), + catchError((err: Error) => { + this._logger.error('Fehler beim Laden der Remigünde', err); + return throwError(err); + }) + ) + ), + shareReplay() + ); + } + + getItems(arg: { + source: string; + supplierId: number; + queryToken: QueryTokenDTO; + }): Observable<{ hits: number; result: RemissionListItem[] }> { + let result$: Observable<{ hits: number; result: RemissionListItem[] }>; + + if (arg.source === 'Pflichtremission') { + result$ = this.getCurrentStock().pipe( + switchMap((stock) => + this.getItemsForPflichtremission({ + queryToken: { + stockId: stock.id, + supplierId: arg.supplierId, + ...arg.queryToken, + }, + }) + ) + ); + } else if (arg.source === 'Abteilungsremission') { + result$ = this.getCurrentStock().pipe( + switchMap((stock) => + this.getItemsForAbteilungsremission({ + queryToken: { + stockId: stock.id, + supplierId: arg.supplierId, + ...arg.queryToken, + }, + }) + ) + ); + } else { + this._logger.error('Unbekannte Quelle', arg.source); + return throwError(new Error(`Unknown source: ${arg.source}`)); + } + + return result$.pipe( + switchMap((res) => + res?.hits + ? this.getStockInformation(res.result).pipe( + map((result) => ({ + hits: res.hits, + result, + })) + ) + : of(res) + ) + ); + } + + getItemsForPflichtremission(arg: { queryToken: RemiQueryTokenDTO }): Observable<{ hits: number; result: RemissionListItem[] }> { + return this._remiService + .RemiPflichtremissionsartikel({ + queryToken: arg.queryToken, + }) + .pipe( + map((res) => ({ + hits: res.hits, + result: res.result.map(mapFromReturnItemDTO), + })) + ); + } + + getItemsForAbteilungsremission(arg: { queryToken: RemiQueryTokenDTO }): Observable<{ hits: number; result: RemissionListItem[] }> { + return this._remiService + .RemiUeberlauf({ + queryToken: arg.queryToken, + }) + .pipe( + map((res) => ({ + hits: res.hits, + result: res.result.map(mapFromReturnSuggestionDTO), + })) + ); + } + + getStockInformation(items: RemissionListItem[]) { + return this.getCurrentStock().pipe( + switchMap((stock) => + this._stockService + .StockInStock({ + stockId: stock.id, + articleIds: items + .filter((item) => !!item.dto.product.catalogProductNumber) + .map((item) => +item.dto.product.catalogProductNumber), + }) + .pipe( + map((res) => { + const o = items.map((item) => { + const stockInfo = res.result.find((stockInfo) => stockInfo.itemId === +item.dto.product.catalogProductNumber); + + if (!stockInfo) { + const defaultStockData = { + inStock: 0, + remainingQuantity: 0, + remissionQuantity: item.remissionQuantity || 0, + }; + + return { ...item, ...defaultStockData }; + } + + const availableStock = stockInfo.inStock - stockInfo.removedFromStock; + const inStock = availableStock < 0 ? 0 : availableStock; + + let { remainingQuantity, remissionQuantity } = item; + + if (!remissionQuantity) { + remissionQuantity = inStock - (remainingQuantity || 0); + if (remissionQuantity < 0) { + remissionQuantity = 0; + } + } + + if (!remainingQuantity) { + remainingQuantity = inStock - (remissionQuantity || 0); + if (remainingQuantity < 0) { + remainingQuantity = 0; + } + } + + return { ...item, remainingQuantity, remissionQuantity, inStock }; + }); + + return o; + }) + ) + ) + ); + } + + getRequiredCapacities(params: { selectedFilters?: { [filterId: string]: string[] }; supplierId: number }) { + return this.getCurrentStock().pipe( + switchMap((stock) => + this._remiService + .RemiGetRequiredCapacities({ + stockId: stock?.id, + payload: { + departments: params?.selectedFilters?.abteilungen || params?.selectedFilters?.department || [], + supplierId: params?.supplierId, + }, + }) + .pipe(map((res) => res.result)) + ) + ); + } + + searchItemToRemit(ean: string): Observable { + return this.getCurrentStock().pipe( + switchMap((stock) => + this._search + .SearchSearch({ + stockId: null, + queryToken: { + stockId: stock.id, + input: { qs: ean }, + doNotTrack: true, + }, + }) + .pipe( + catchError((err) => of({ hits: 0, result: [] })), + map((res) => [res, stock] as [ListResponseArgsOfItemDTO, StockDTO]) + ) + ), + map(([res, stock]) => { + if (res.hits === 0) { + return undefined; + } + + const item = res.result[0] as ItemDTO; + + return fromItemDto(item, stock); + }) + ); + } + + async createReturn(supplierId: number, returnGroup?: string): Promise { + const response = await this._returnService + .ReturnCreateReturn({ + data: { + supplier: { id: supplierId }, + returnGroup: returnGroup ?? String(Date.now()), + }, + }) + .toPromise(); + + return response.result; + } + + completeReturn(returnId: number) { + return this._returnService.ReturnFinalizeReturn({ returnId }).toPromise(); + } + + async completeRemission(returnId: number): Promise { + const returnDto = await this.getReturn(returnId).toPromise(); + const response = await this._returnService + .ReturnFinalizeReturnGroup({ + returnGroup: returnDto.returnGroup, + }) + .toPromise(); + + return response.result; + } + + async deleteRemission(returnId: number): Promise { + await this._returnService + .ReturnCancelReturn({ + returnId, + }) + .toPromise(); + } + + getReturns(returncompleted?: boolean): Observable { + const queryToken: ReturnQueryTokenDTO = { + start: this._dateAdapter.addCalendarDays(new Date(), -7).toISOString(), + stop: new Date().toISOString(), + filter: { + returncompleted: returncompleted ? 'true' : 'false', + }, + eagerLoading: 3, + input: {}, + }; + + if (returncompleted === undefined) { + delete queryToken?.filter?.returncompleted; + } + + return this.getCurrentStock().pipe( + switchMap((stock) => this._returnService.ReturnQueryReturns({ stockId: stock.id, queryToken })), + map((res) => res.result) + ); + } + + getReturn(returnId: number): Observable { + return this._returnService.ReturnGetReturn({ returnId, eagerLoading: 3 }).pipe(map((res) => res.result)); + } + + async deleteReturn(returnId: number) { + const returnDto = await this.getReturn(returnId).toPromise(); + for (const receipt of returnDto?.receipts) { + await this.deleteReceipt(returnDto.id, receipt.id); + } + await this.deleteRemission(returnDto.id); + } + + addReturnItem({ + returnId, + receiptId, + returnItemId, + quantity, + placementType, + inStock, + }: { + returnId: number; + receiptId: number; + returnItemId: number; + quantity?: number; + placementType?: RemissionPlacementType; + inStock: number; + }) { + return this._returnService + .ReturnAddReturnItem({ returnId, receiptId, data: { returnItemId, quantity, placementType, inStock } }) + .pipe(map((r) => r.result)); + } + + addReturnSuggestion({ + returnId, + receiptId, + returnSuggestionId, + quantity, + placementType, + inStock, + }: { + returnId: number; + receiptId: number; + returnSuggestionId: number; + quantity?: number; + placementType?: RemissionPlacementType; + inStock: number; + }) { + return this._returnService + .ReturnAddReturnSuggestion({ returnId, receiptId, data: { returnSuggestionId, quantity, placementType, inStock } }) + .pipe(map((r) => r.result)); + } + + removeReturnItemFromList({ itemId }: { itemId: number }) { + return this._returnService.ReturnDeleteReturnItem({ itemId }); + } + + removeReturnItemFromReceipt({ returnId, receiptId, receiptItemId }: { returnId: number; receiptId: number; receiptItemId: number }) { + return this._returnService.ReturnRemoveReturnItem({ returnId, receiptItemId, receiptId }); + } + + returnImpediment(itemId: number) { + return this._returnService + .ReturnReturnItemImpediment({ itemId, data: { comment: 'Produkt nicht gefunden' } }) + .pipe(map((r) => r.result)); + } + + /** + * Create a new receipt for the given return/remission + * @param returnId Return ID + * @param receiptNumber Receipt number + * @returns ReturnDTO - ShippingDocument + */ + async createReceipt(returnDTO: ReturnDTO, receiptNumber?: string): Promise { + const stock = await this._getStock(); + + const response = await this._returnService + .ReturnCreateReceipt({ + returnId: returnDTO.id, + data: { + receiptNumber: receiptNumber ?? null, + stock: { + id: stock.id, + }, + supplier: { id: returnDTO.supplier.id }, + receiptType: 1, // ShippingNote = 1 + }, + }) + .toPromise(); + + const receipt: ReceiptDTO = response.result; + + return receipt; + } + + async completeReceipt(returnId: number, receiptId: number, packageCode: string): Promise { + const res = await this._returnService + .ReturnFinalizeReceipt({ + returnId, + receiptId, + data: { + packageCode, + }, + }) + .toPromise(); + + const result = res.result; + + return result; + } + + async deleteReceipt(returnId: number, receiptId: number): Promise { + await this._returnService + .ReturnCancelReturnReceipt({ + returnId, + receiptId, + }) + .toPromise(); + } + + addProductToRemit(item: ReturnItemDTO, reason: string, quantity: number) { + return this._remiService.RemiCreateReturnItem({ + data: [ + { + assortment: item.assortment, + product: item.product, + returnReason: reason, + predefinedReturnQuantity: quantity, + stock: item.stock, + retailPrice: item.retailPrice, + source: 'manually-added', + }, + ], + }); + } + + private _getStock() { + return this.getCurrentStock().toPromise(); + } +} diff --git a/apps/domain/remission/src/public-api.ts b/apps/domain/remission/src/public-api.ts new file mode 100644 index 000000000..2a829a4e9 --- /dev/null +++ b/apps/domain/remission/src/public-api.ts @@ -0,0 +1,5 @@ +/* + * Public API Surface of remission + */ + +export * from './lib'; diff --git a/apps/domain/remission/src/test.ts b/apps/domain/remission/src/test.ts new file mode 100644 index 000000000..b22c5d4af --- /dev/null +++ b/apps/domain/remission/src/test.ts @@ -0,0 +1,25 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import 'zone.js'; +import 'zone.js/testing'; +import { getTestBed } from '@angular/core/testing'; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; + +declare const require: { + context( + path: string, + deep?: boolean, + filter?: RegExp + ): { + keys(): string[]; + (id: string): T; + }; +}; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), { teardown: { destroyAfterEach: true } }); + +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().map(context); diff --git a/apps/domain/remission/tsconfig.lib.json b/apps/domain/remission/tsconfig.lib.json new file mode 100644 index 000000000..a7141c543 --- /dev/null +++ b/apps/domain/remission/tsconfig.lib.json @@ -0,0 +1,20 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "outDir": "../../../out-tsc/lib", + "target": "es2015", + "declaration": true, + "declarationMap": true, + "inlineSources": true, + "types": [], + "lib": [ + "dom", + "es2018" + ] + }, + "exclude": [ + "src/test.ts", + "**/*.spec.ts" + ] +} diff --git a/apps/domain/remission/tsconfig.lib.prod.json b/apps/domain/remission/tsconfig.lib.prod.json new file mode 100644 index 000000000..06de549e1 --- /dev/null +++ b/apps/domain/remission/tsconfig.lib.prod.json @@ -0,0 +1,10 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.lib.json", + "compilerOptions": { + "declarationMap": false + }, + "angularCompilerOptions": { + "compilationMode": "partial" + } +} diff --git a/apps/domain/remission/tsconfig.spec.json b/apps/domain/remission/tsconfig.spec.json new file mode 100644 index 000000000..85392ee8f --- /dev/null +++ b/apps/domain/remission/tsconfig.spec.json @@ -0,0 +1,17 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "outDir": "../../../out-tsc/spec", + "types": [ + "jasmine" + ] + }, + "files": [ + "src/test.ts" + ], + "include": [ + "**/*.spec.ts", + "**/*.d.ts" + ] +} diff --git a/apps/domain/task-calendar/src/lib/task-calendar.service.ts b/apps/domain/task-calendar/src/lib/task-calendar.service.ts index ebdfae77a..4a3886537 100644 --- a/apps/domain/task-calendar/src/lib/task-calendar.service.ts +++ b/apps/domain/task-calendar/src/lib/task-calendar.service.ts @@ -1,8 +1,7 @@ import { Injectable } from '@angular/core'; -import { DisplayInfoRequest, EISPublicService, FileDTO, ProcessingStatus, QueryTokenDTO } from '@swagger/eis'; +import { EISPublicService, FileDTO, ProcessingStatus, QueryTokenDTO } from '@swagger/eis'; import { DateAdapter } from '@ui/common'; import { memorize } from '@utils/common'; -import { combineLatest } from 'rxjs'; import { map, shareReplay, switchMap } from 'rxjs/operators'; import { InfoType, ProcessingStatusList } from './defs'; @@ -17,8 +16,8 @@ export class DomainTaskCalendarService { constructor(private eisPublicService: EISPublicService, private dateAdapter: DateAdapter) {} - getFilters() { - return this.eisPublicService.EISPublicQueryDisplayInfoFilter(); + getSettings() { + return this.eisPublicService.EISPublicQueryDisplayInfoSettings().pipe(map((settings) => settings.result)); } getInfos(queryToken: QueryTokenDTO) { diff --git a/apps/domain/task-calendar/src/test.ts b/apps/domain/task-calendar/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/domain/task-calendar/src/test.ts +++ b/apps/domain/task-calendar/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/hub/notifications/src/test.ts b/apps/hub/notifications/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/hub/notifications/src/test.ts +++ b/apps/hub/notifications/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/isa-app/.browserslistrc b/apps/isa-app/.browserslistrc new file mode 100644 index 000000000..427441dc9 --- /dev/null +++ b/apps/isa-app/.browserslistrc @@ -0,0 +1,17 @@ +# This file is used by the build system to adjust CSS and JS output to support the specified browsers below. +# For additional information regarding the format and rule options, please see: +# https://github.com/browserslist/browserslist#queries + +# For the full list of supported browsers by the Angular framework, please see: +# https://angular.io/guide/browser-support + +# You can see what browsers were selected by your queries by running: +# npx browserslist + +last 1 Chrome version +last 1 Firefox version +last 2 Edge major versions +last 2 Safari major versions +last 2 iOS major versions +Firefox ESR +not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line. diff --git a/apps/isa-app/karma.conf.js b/apps/isa-app/karma.conf.js new file mode 100644 index 000000000..325fab0ee --- /dev/null +++ b/apps/isa-app/karma.conf.js @@ -0,0 +1,44 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html + +const customLaunchers = require('../../karma/custom-launchers'); +const junitReporter = require('../../karma/junit-reporter')('isa-app'); +const coverageReporter = require('../../karma/coverage-reporter')('isa-app'); + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage'), + require('karma-junit-reporter'), + require('@angular-devkit/build-angular/plugins/karma'), + ], + client: { + jasmine: { + // you can add configuration options for Jasmine here + // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html + // for example, you can disable the random execution with `random: false` + // or set a specific seed with `seed: 4321` + }, + clearContext: false, // leave Jasmine Spec Runner output visible in browser + }, + jasmineHtmlReporter: { + suppressAll: true, // removes the duplicated traces + }, + coverageReporter, + junitReporter, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + customLaunchers, + singleRun: false, + restartOnFileChange: true, + }); +}; diff --git a/apps/isa-app/ngsw-config.json b/apps/isa-app/ngsw-config.json new file mode 100644 index 000000000..0fb4c72f6 --- /dev/null +++ b/apps/isa-app/ngsw-config.json @@ -0,0 +1,32 @@ +{ + "$schema": "../../node_modules/@angular/service-worker/config/schema.json", + "index": "/index.html", + "navigationRequestStrategy": "freshness", + "assetGroups": [ + { + "name": "app", + "installMode": "prefetch", + "resources": { + "files": [ + "/favicon.ico", + "/index.html", + "/manifest.webmanifest", + "/*.css", + "/*.js", + "/*.json" + ] + } + }, + { + "name": "assets", + "installMode": "lazy", + "updateMode": "prefetch", + "resources": { + "files": [ + "/assets/**", + "/*.(svg|cur|jpg|jpeg|png|apng|webp|avif|gif|otf|ttf|woff|woff2)" + ] + } + } + ] +} \ No newline at end of file diff --git a/apps/isa-app/src/app/app-domain.module.ts b/apps/isa-app/src/app/app-domain.module.ts new file mode 100644 index 000000000..a6243517e --- /dev/null +++ b/apps/isa-app/src/app/app-domain.module.ts @@ -0,0 +1,19 @@ +import { NgModule } from '@angular/core'; +import { DomainAvailabilityModule } from '@domain/availability'; +import { DomainCatalogModule } from '@domain/catalog'; +import { DomainIsaModule } from '@domain/isa'; +import { DomainCheckoutModule } from '@domain/checkout'; +import { DomainOmsModule } from '@domain/oms'; +import { DomainRemissionModule } from '@domain/remission'; + +@NgModule({ + imports: [ + DomainIsaModule.forRoot(), + DomainCatalogModule.forRoot(), + DomainAvailabilityModule.forRoot(), + DomainCheckoutModule.forRoot(), + DomainOmsModule.forRoot(), + DomainRemissionModule.forRoot(), + ], +}) +export class AppDomainModule {} diff --git a/apps/isa-app/src/app/app-routing.module.ts b/apps/isa-app/src/app/app-routing.module.ts new file mode 100644 index 000000000..f05e982bf --- /dev/null +++ b/apps/isa-app/src/app/app-routing.module.ts @@ -0,0 +1,113 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { + CanActivateCartGuard, + CanActivateCartWithProcessIdGuard, + CanActivateCustomerGuard, + CanActivateCustomerWithProcessIdGuard, + CanActivateGoodsInGuard, + CanActivateGoodsOutGuard, + CanActivateProductGuard, + CanActivateProductWithProcessIdGuard, + CanActivateRemissionGuard, + CanActivateTaskCalendarGuard, + InitStoreGuard, + IsAuthenticatedGuard, +} from './guards'; +import { BranchSectionResolver, CustomerSectionResolver, ProcessIdResolver } from './resolvers'; +import { ShellComponent, ShellModule } from './shell'; + +const routes: Routes = [ + { + path: '', + canActivate: [IsAuthenticatedGuard], + children: [ + { + path: '', + canActivate: [InitStoreGuard], + children: [ + { + path: 'kunde', + component: ShellComponent, + canActivate: [InitStoreGuard], + children: [ + { + path: 'dashboard', + loadChildren: () => import('@page/dashboard').then((m) => m.DashboardModule), + }, + { + path: 'product', + loadChildren: () => import('@page/catalog').then((m) => m.PageCatalogModule), + canActivate: [CanActivateProductGuard], + }, + { + path: ':processId/product', + loadChildren: () => import('@page/catalog').then((m) => m.PageCatalogModule), + canActivate: [CanActivateProductWithProcessIdGuard], + resolve: { processId: ProcessIdResolver }, + }, + { + path: 'customer', + loadChildren: () => import('@page/customer').then((m) => m.PageCustomerModule), + canActivate: [CanActivateCustomerGuard], + }, + { + path: ':processId/customer', + loadChildren: () => import('@page/customer').then((m) => m.PageCustomerModule), + canActivate: [CanActivateCustomerWithProcessIdGuard], + resolve: { processId: ProcessIdResolver }, + }, + { + path: 'cart', + loadChildren: () => import('@page/checkout').then((m) => m.PageCheckoutModule), + canActivate: [CanActivateCartGuard], + }, + { + path: ':processId/cart', + loadChildren: () => import('@page/checkout').then((m) => m.PageCheckoutModule), + canActivate: [CanActivateCartWithProcessIdGuard], + }, + { + path: 'goods/out', + loadChildren: () => import('@page/goods-out').then((m) => m.GoodsOutModule), + canActivate: [CanActivateGoodsOutGuard], + }, + { path: '**', redirectTo: 'dashboard', pathMatch: 'full' }, + ], + resolve: { section: CustomerSectionResolver }, + }, + { + path: 'filiale', + component: ShellComponent, + children: [ + { + path: 'task-calendar', + loadChildren: () => import('@page/task-calendar').then((m) => m.PageTaskCalendarModule), + canActivate: [CanActivateTaskCalendarGuard], + }, + { + path: 'goods/in', + loadChildren: () => import('@page/goods-in').then((m) => m.GoodsInModule), + canActivate: [CanActivateGoodsInGuard], + }, + { + path: 'remission', + loadChildren: () => import('@page/remission').then((m) => m.PageRemissionModule), + canActivate: [CanActivateRemissionGuard], + }, + { path: '**', redirectTo: 'task-calendar', pathMatch: 'full' }, + ], + resolve: { section: BranchSectionResolver }, + }, + { path: '**', redirectTo: 'kunde', pathMatch: 'full' }, + ], + }, + ], + }, +]; + +@NgModule({ + imports: [RouterModule.forRoot(routes), ShellModule], + exports: [RouterModule], +}) +export class AppRoutingModule {} diff --git a/apps/isa-app/src/app/app-store.module.ts b/apps/isa-app/src/app/app-store.module.ts new file mode 100644 index 000000000..3712e531d --- /dev/null +++ b/apps/isa-app/src/app/app-store.module.ts @@ -0,0 +1,34 @@ +import { NgModule } from '@angular/core'; +import { EffectsModule } from '@ngrx/effects'; +import { ActionReducer, INIT, MetaReducer, StoreModule } from '@ngrx/store'; +import { StoreDevtoolsModule } from '@ngrx/store-devtools'; +import { storeFreeze } from 'ngrx-store-freeze'; +import packageInfo from 'package'; +import { environment } from '../environments/environment'; +import { RootStateService } from './store/root-state.service'; +import { rootReducer } from './store/root.reducer'; +import { RootState } from './store/root.state'; + +export function storeInLocalStorage(reducer: ActionReducer): ActionReducer { + return function (state, action) { + if (action.type === INIT) { + const initialState = RootStateService.LoadFromLocalStorage(); + + if (initialState?.version === packageInfo.version) { + return reducer(initialState, action); + } + } + return reducer(state, action); + }; +} + +export const metaReducers: MetaReducer[] = !environment.production ? [storeFreeze, storeInLocalStorage] : [storeInLocalStorage]; + +@NgModule({ + imports: [ + StoreModule.forRoot(rootReducer, { metaReducers }), + EffectsModule.forRoot([]), + StoreDevtoolsModule.instrument({ name: 'ISA Ngrx Application Store' }), + ], +}) +export class AppStoreModule {} diff --git a/apps/isa-app/src/app/app-swagger.module.ts b/apps/isa-app/src/app/app-swagger.module.ts new file mode 100644 index 000000000..89c08dcfa --- /dev/null +++ b/apps/isa-app/src/app/app-swagger.module.ts @@ -0,0 +1,32 @@ +import { NgModule } from '@angular/core'; +import { Config } from '@core/config'; +import { AvConfiguration } from '@swagger/availability'; +import { CatConfiguration } from '@swagger/cat'; +import { CheckoutConfiguration } from '@swagger/checkout'; +import { CrmConfiguration } from '@swagger/crm'; +import { EisConfiguration } from '@swagger/eis'; +import { IsaConfiguration } from '@swagger/isa'; +import { OmsConfiguration } from '@swagger/oms'; +import { PrintConfiguration } from '@swagger/print'; +import { RemiConfiguration } from '@swagger/remi'; + +export function createConfigurationFactory(name: string) { + return function (config: Config): { rootUrl: string } { + return config.get(`@swagger/${name}`); + }; +} + +@NgModule({ + providers: [ + { provide: AvConfiguration, useFactory: createConfigurationFactory('av'), deps: [Config] }, + { provide: CatConfiguration, useFactory: createConfigurationFactory('cat'), deps: [Config] }, + { provide: CheckoutConfiguration, useFactory: createConfigurationFactory('checkout'), deps: [Config] }, + { provide: CrmConfiguration, useFactory: createConfigurationFactory('crm'), deps: [Config] }, + { provide: EisConfiguration, useFactory: createConfigurationFactory('eis'), deps: [Config] }, + { provide: IsaConfiguration, useFactory: createConfigurationFactory('isa'), deps: [Config] }, + { provide: OmsConfiguration, useFactory: createConfigurationFactory('oms'), deps: [Config] }, + { provide: PrintConfiguration, useFactory: createConfigurationFactory('print'), deps: [Config] }, + { provide: RemiConfiguration, useFactory: createConfigurationFactory('remi'), deps: [Config] }, + ], +}) +export class AppSwaggerModule {} diff --git a/apps/isa-app/src/app/app.component.html b/apps/isa-app/src/app/app.component.html new file mode 100644 index 000000000..0680b43f9 --- /dev/null +++ b/apps/isa-app/src/app/app.component.html @@ -0,0 +1 @@ + diff --git a/apps/isa-app/src/app/app.component.scss b/apps/isa-app/src/app/app.component.scss new file mode 100644 index 000000000..592ac705c --- /dev/null +++ b/apps/isa-app/src/app/app.component.scss @@ -0,0 +1,3 @@ +:host { + @apply block box-border; +} diff --git a/apps/isa-app/src/app/app.component.spec.ts b/apps/isa-app/src/app/app.component.spec.ts new file mode 100644 index 000000000..a9a909a8b --- /dev/null +++ b/apps/isa-app/src/app/app.component.spec.ts @@ -0,0 +1,89 @@ +import { Spectator, createComponentFactory, SpyObject, createSpyObject } from '@ngneat/spectator'; +import { RouterTestingModule } from '@angular/router/testing'; +import { AppComponent } from './app.component'; +import { Config } from '@core/config'; +import { ApplicationService } from '@core/application'; +import { of } from 'rxjs'; +import { Renderer2 } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +describe('AppComponent', () => { + let spectator: Spectator; + let config: SpyObject; + let renderer: SpyObject; + let applicationServiceMock: SpyObject; + const createComponent = createComponentFactory({ + component: AppComponent, + imports: [CommonModule, RouterTestingModule], + providers: [], + mocks: [Config], + }); + + beforeEach(() => { + applicationServiceMock = createSpyObject(ApplicationService); + applicationServiceMock.getSection$.and.returnValue(of('customer')); + applicationServiceMock.getActivatedProcessId$.and.returnValue(of(undefined)); + renderer = jasmine.createSpyObj('Renderer2', ['addClass', 'removeClass']); + + spectator = createComponent({ + providers: [ + { provide: ApplicationService, useValue: applicationServiceMock }, + { + provide: Renderer2, + useValue: renderer, + }, + ], + }); + config = spectator.inject(Config); + }); + + it('should create the app', () => { + expect(spectator.component).toBeTruthy(); + }); + + it('should have a router outlet', () => { + expect(spectator.query('router-outlet')).toExist(); + }); + + describe('ngOnInit', () => { + it('should call setTitle', () => { + const spy = spyOn(spectator.component, 'setTitle'); + spectator.component.ngOnInit(); + expect(spy).toHaveBeenCalled(); + }); + + it('should call logVersion', () => { + const spy = spyOn(spectator.component, 'logVersion'); + spectator.component.ngOnInit(); + expect(spy).toHaveBeenCalled(); + }); + }); + + describe('setTitle', () => { + it('should call Title.setTitle()', () => { + const spyTitleSetTitle = spyOn(spectator.component['_title'], 'setTitle'); + config.get.and.returnValue('test'); + spectator.component.setTitle(); + expect(spyTitleSetTitle).toHaveBeenCalledWith('test'); + }); + }); + + describe('logVersion', () => { + it('should call console.log()', () => { + const spyConsoleLog = spyOn(console, 'log'); + config.get.and.returnValue('test'); + spectator.component.logVersion(); + expect(spyConsoleLog).toHaveBeenCalled(); + }); + }); + + // describe('sectionChangeHandler', () => { + // fit('should add class customer and remove class branch from body when section is customer', () => { + // spectator.component.sectionChangeHandler('customer'); + // console.log(renderer); + // console.log('expect'); + // expect(renderer.removeClass).toHaveBeenCalled(); + // expect(renderer.addClass).toHaveBeenCalled(); + // }); + // }); +}); diff --git a/apps/isa-app/src/app/app.component.ts b/apps/isa-app/src/app/app.component.ts new file mode 100644 index 000000000..36c9a4610 --- /dev/null +++ b/apps/isa-app/src/app/app.component.ts @@ -0,0 +1,46 @@ +import { DOCUMENT } from '@angular/common'; +import { Component, Inject, OnInit, Renderer2 } from '@angular/core'; +import { Title } from '@angular/platform-browser'; +import { ApplicationService } from '@core/application'; +import { Config } from '@core/config'; +import packageInfo from 'package'; + +@Component({ + selector: 'app-root', + templateUrl: './app.component.html', + styleUrls: ['./app.component.scss'], +}) +export class AppComponent implements OnInit { + constructor( + private readonly _config: Config, + private readonly _title: Title, + private readonly _appService: ApplicationService, + @Inject(DOCUMENT) private readonly _document: Document, + private readonly _renderer: Renderer2 + ) {} + + ngOnInit() { + this.setTitle(); + this.logVersion(); + + this._appService.getSection$().subscribe(this.sectionChangeHandler.bind(this)); + } + + setTitle() { + this._title.setTitle(this._config.get('title')); + } + + logVersion() { + console.log(`%c${this._config.get('title')}\r\nVersion: ${packageInfo.version}`, 'font-weight: bold; font-size: 20px;'); + } + + sectionChangeHandler(section: string) { + if (section === 'customer') { + this._renderer.removeClass(this._document.body, 'branch'); + this._renderer.addClass(this._document.body, 'customer'); + } else if (section === 'branch') { + this._renderer.removeClass(this._document.body, 'customer'); + this._renderer.addClass(this._document.body, 'branch'); + } + } +} diff --git a/apps/isa-app/src/app/app.module.ts b/apps/isa-app/src/app/app.module.ts new file mode 100644 index 000000000..2c9be951a --- /dev/null +++ b/apps/isa-app/src/app/app.module.ts @@ -0,0 +1,105 @@ +import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; +import { APP_INITIALIZER, ErrorHandler, LOCALE_ID, NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; + +import { Config, ConfigModule, JsonConfigLoader } from '@core/config'; +import { AuthModule, AuthService } from '@core/auth'; + +import { AppRoutingModule } from './app-routing.module'; +import { AppComponent } from './app.component'; +import { CoreApplicationModule } from '@core/application'; +import { AppStoreModule } from './app-store.module'; +import { ServiceWorkerModule } from '@angular/service-worker'; +import { environment } from '../environments/environment'; +import { AppSwaggerModule } from './app-swagger.module'; +import { AppDomainModule } from './app-domain.module'; +import { UiModalModule } from '@ui/modal'; +import { NotificationsHubModule, NOTIFICATIONS_HUB_OPTIONS } from '@hub/notifications'; +import { SignalRHubOptions } from '@core/signalr'; +import { CoreBreadcrumbModule } from '@core/breadcrumb'; +import { UiCommonModule } from '@ui/common'; +import { registerLocaleData } from '@angular/common'; + +import localeDe from '@angular/common/locales/de'; +import localeDeExtra from '@angular/common/locales/extra/de'; +import { HttpErrorInterceptor } from './interceptors'; +import { CoreLoggerModule, LOG_PROVIDER } from '@core/logger'; +import { IsaLogProvider } from './providers'; +import { IsaErrorHandler } from './providers/isa.error-handler'; +import { ScanAdapterModule } from '@adapter/scan'; + +registerLocaleData(localeDe, localeDeExtra); +registerLocaleData(localeDe, 'de', localeDeExtra); + +export function _appInitializerFactory(config: Config, auth: AuthService) { + return async () => { + await config.init(); + await auth.init(); + }; +} + +export function _notificationsHubOptionsFactory(config: Config, auth: AuthService): SignalRHubOptions { + const options = { ...config.get('hubs').notifications }; + options.httpOptions.accessTokenFactory = () => auth.getToken(); + return options; +} + +@NgModule({ + declarations: [AppComponent], + imports: [ + BrowserModule, + BrowserAnimationsModule, + HttpClientModule, + AppRoutingModule, + AppSwaggerModule, + AppDomainModule, + CoreBreadcrumbModule.forRoot(), + ConfigModule.forRoot({ + useConfigLoader: JsonConfigLoader, + jsonConfigLoaderUrl: '/config/config.json', + }), + CoreLoggerModule.forRoot(), + AppStoreModule, + AuthModule.forRoot(), + CoreApplicationModule.forRoot(), + UiModalModule.forRoot(), + UiCommonModule.forRoot(), + NotificationsHubModule.forRoot(), + ServiceWorkerModule.register('ngsw-worker.js', { + enabled: environment.production, + registrationStrategy: 'registerWhenStable:30000', + }), + ScanAdapterModule.forRoot(!environment.production), + ], + providers: [ + { + provide: APP_INITIALIZER, + useFactory: _appInitializerFactory, + multi: true, + deps: [Config, AuthService], + }, + { + provide: NOTIFICATIONS_HUB_OPTIONS, + useFactory: _notificationsHubOptionsFactory, + deps: [Config, AuthService], + }, + { + provide: HTTP_INTERCEPTORS, + useClass: HttpErrorInterceptor, + multi: true, + }, + { + provide: LOG_PROVIDER, + useClass: IsaLogProvider, + multi: true, + }, + { + provide: ErrorHandler, + useClass: IsaErrorHandler, + }, + { provide: LOCALE_ID, useValue: 'de-DE' }, + ], + bootstrap: [AppComponent], +}) +export class AppModule {} diff --git a/apps/isa-app/src/app/guards/can-activate-cart-with-process-id.guard.ts b/apps/isa-app/src/app/guards/can-activate-cart-with-process-id.guard.ts new file mode 100644 index 000000000..c7fe464ba --- /dev/null +++ b/apps/isa-app/src/app/guards/can-activate-cart-with-process-id.guard.ts @@ -0,0 +1,35 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot } from '@angular/router'; +import { ApplicationService } from '@core/application'; +import { first } from 'rxjs/operators'; + +@Injectable({ providedIn: 'root' }) +export class CanActivateCartWithProcessIdGuard implements CanActivate { + constructor(private readonly _applicationService: ApplicationService) {} + + async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { + const process = await this._applicationService + .getProcessById$(+route.params.processId) + .pipe(first()) + .toPromise(); + + // if (!(process?.type === 'cart')) { + // // TODO: + // // Anderer Prozesstyp mit gleicher Id - Was soll gemacht werden? + // return false; + // } + + if (!process) { + const processes = await this._applicationService.getProcesses$('customer').pipe(first()).toPromise(); + await this._applicationService.createProcess({ + id: +route.params.processId, + type: 'cart', + section: 'customer', + name: `Vorgang ${processes.length + 1}`, + }); + } + + this._applicationService.activateProcess(+route.params.processId); + return true; + } +} diff --git a/apps/isa-app/src/app/guards/can-activate-cart.guard.ts b/apps/isa-app/src/app/guards/can-activate-cart.guard.ts new file mode 100644 index 000000000..4917cfc40 --- /dev/null +++ b/apps/isa-app/src/app/guards/can-activate-cart.guard.ts @@ -0,0 +1,27 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router'; +import { ApplicationService } from '@core/application'; +import { first } from 'rxjs/operators'; + +@Injectable({ providedIn: 'root' }) +export class CanActivateCartGuard implements CanActivate { + constructor(private readonly _applicationService: ApplicationService, private readonly _router: Router) {} + + async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { + const processes = await this._applicationService.getProcesses$('customer').pipe(first()).toPromise(); + let lastActivatedProcessId = ( + await this._applicationService.getLastActivatedProcessWithSectionAndType$('customer', 'cart').pipe(first()).toPromise() + )?.id; + if (!lastActivatedProcessId) { + lastActivatedProcessId = Date.now(); + await this._applicationService.createProcess({ + id: lastActivatedProcessId, + type: 'cart', + section: 'customer', + name: `Vorgang ${processes.length + 1}`, + }); + } + await this._router.navigate(['/kunde', lastActivatedProcessId, 'cart']); + return false; + } +} diff --git a/apps/isa-app/src/app/guards/can-activate-customer-with-process-id.guard.ts b/apps/isa-app/src/app/guards/can-activate-customer-with-process-id.guard.ts new file mode 100644 index 000000000..c174bafa3 --- /dev/null +++ b/apps/isa-app/src/app/guards/can-activate-customer-with-process-id.guard.ts @@ -0,0 +1,35 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot } from '@angular/router'; +import { ApplicationService } from '@core/application'; +import { first } from 'rxjs/operators'; + +@Injectable({ providedIn: 'root' }) +export class CanActivateCustomerWithProcessIdGuard implements CanActivate { + constructor(private readonly _applicationService: ApplicationService) {} + + async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { + const process = await this._applicationService + .getProcessById$(+route.params.processId) + .pipe(first()) + .toPromise(); + + // if (!(process?.type === 'cart')) { + // // TODO: + // // Anderer Prozesstyp mit gleicher Id - Was soll gemacht werden? + // return false; + // } + + if (!process) { + const processes = await this._applicationService.getProcesses$('customer').pipe(first()).toPromise(); + await this._applicationService.createProcess({ + id: +route.params.processId, + type: 'cart', + section: 'customer', + name: `Vorgang ${processes.length + 1}`, + }); + } + + this._applicationService.activateProcess(+route.params.processId); + return true; + } +} diff --git a/apps/isa-app/src/app/guards/can-activate-customer.guard.ts b/apps/isa-app/src/app/guards/can-activate-customer.guard.ts new file mode 100644 index 000000000..e7513fefd --- /dev/null +++ b/apps/isa-app/src/app/guards/can-activate-customer.guard.ts @@ -0,0 +1,27 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router'; +import { ApplicationService } from '@core/application'; +import { first } from 'rxjs/operators'; + +@Injectable({ providedIn: 'root' }) +export class CanActivateCustomerGuard implements CanActivate { + constructor(private readonly _applicationService: ApplicationService, private readonly _router: Router) {} + + async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { + const processes = await this._applicationService.getProcesses$('customer').pipe(first()).toPromise(); + let lastActivatedProcessId = ( + await this._applicationService.getLastActivatedProcessWithSectionAndType$('customer', 'cart').pipe(first()).toPromise() + )?.id; + if (!lastActivatedProcessId) { + lastActivatedProcessId = Date.now(); + await this._applicationService.createProcess({ + id: lastActivatedProcessId, + type: 'cart', + section: 'customer', + name: `Vorgang ${processes.length + 1}`, + }); + } + await this._router.navigate(['/kunde', lastActivatedProcessId, 'customer']); + return false; + } +} diff --git a/apps/isa-app/src/app/guards/can-activate-goods-in.guard.ts b/apps/isa-app/src/app/guards/can-activate-goods-in.guard.ts new file mode 100644 index 000000000..bf1596b57 --- /dev/null +++ b/apps/isa-app/src/app/guards/can-activate-goods-in.guard.ts @@ -0,0 +1,24 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot } from '@angular/router'; +import { ApplicationService } from '@core/application'; +import { Config } from '@core/config'; +import { first } from 'rxjs/operators'; + +@Injectable({ providedIn: 'root' }) +export class CanActivateGoodsInGuard implements CanActivate { + constructor(private readonly _applicationService: ApplicationService, private readonly _config: Config) {} + + async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { + const process = await this._applicationService.getProcessById$(this._config.get('process.ids.goodsIn')).pipe(first()).toPromise(); + if (!process) { + await this._applicationService.createProcess({ + id: this._config.get('process.ids.goodsIn'), + type: 'goods-in', + section: 'branch', + name: 'Abholfach', + }); + } + this._applicationService.activateProcess(this._config.get('process.ids.goodsIn')); + return true; + } +} diff --git a/apps/isa-app/src/app/guards/can-activate-goods-out.guard.ts b/apps/isa-app/src/app/guards/can-activate-goods-out.guard.ts new file mode 100644 index 000000000..72822f2f4 --- /dev/null +++ b/apps/isa-app/src/app/guards/can-activate-goods-out.guard.ts @@ -0,0 +1,24 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot } from '@angular/router'; +import { ApplicationService } from '@core/application'; +import { Config } from '@core/config'; +import { first } from 'rxjs/operators'; + +@Injectable({ providedIn: 'root' }) +export class CanActivateGoodsOutGuard implements CanActivate { + constructor(private readonly _applicationService: ApplicationService, private readonly _config: Config) {} + + async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { + const process = await this._applicationService.getProcessById$(this._config.get('process.ids.goodsOut')).pipe(first()).toPromise(); + if (!process) { + await this._applicationService.createProcess({ + id: this._config.get('process.ids.goodsOut'), + type: 'goods-out', + section: 'customer', + name: 'Warenausgabe', + }); + } + this._applicationService.activateProcess(this._config.get('process.ids.goodsOut')); + return true; + } +} diff --git a/apps/isa-app/src/app/guards/can-activate-product-with-process-id.guard.ts b/apps/isa-app/src/app/guards/can-activate-product-with-process-id.guard.ts new file mode 100644 index 000000000..d20c0d2cd --- /dev/null +++ b/apps/isa-app/src/app/guards/can-activate-product-with-process-id.guard.ts @@ -0,0 +1,35 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot } from '@angular/router'; +import { ApplicationService } from '@core/application'; +import { first } from 'rxjs/operators'; + +@Injectable({ providedIn: 'root' }) +export class CanActivateProductWithProcessIdGuard implements CanActivate { + constructor(private readonly _applicationService: ApplicationService) {} + + async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { + const process = await this._applicationService + .getProcessById$(+route.params.processId) + .pipe(first()) + .toPromise(); + + // if (!(process?.type === 'cart')) { + // // TODO: + // // Anderer Prozesstyp mit gleicher Id - Was soll gemacht werden? + // return false; + // } + + if (!process) { + const processes = await this._applicationService.getProcesses$('customer').pipe(first()).toPromise(); + await this._applicationService.createProcess({ + id: +route.params.processId, + type: 'cart', + section: 'customer', + name: `Vorgang ${processes.length + 1}`, + }); + } + + this._applicationService.activateProcess(+route.params.processId); + return true; + } +} diff --git a/apps/isa-app/src/app/guards/can-activate-product.guard.ts b/apps/isa-app/src/app/guards/can-activate-product.guard.ts new file mode 100644 index 000000000..744c0d47a --- /dev/null +++ b/apps/isa-app/src/app/guards/can-activate-product.guard.ts @@ -0,0 +1,27 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router'; +import { ApplicationService } from '@core/application'; +import { first } from 'rxjs/operators'; + +@Injectable({ providedIn: 'root' }) +export class CanActivateProductGuard implements CanActivate { + constructor(private readonly _applicationService: ApplicationService, private readonly _router: Router) {} + + async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { + const processes = await this._applicationService.getProcesses$('customer').pipe(first()).toPromise(); + let lastActivatedProcessId = ( + await this._applicationService.getLastActivatedProcessWithSectionAndType$('customer', 'cart').pipe(first()).toPromise() + )?.id; + if (!lastActivatedProcessId) { + lastActivatedProcessId = Date.now(); + await this._applicationService.createProcess({ + id: lastActivatedProcessId, + type: 'cart', + section: 'customer', + name: `Vorgang ${processes.length + 1}`, + }); + } + await this._router.navigate(['/kunde', lastActivatedProcessId, 'product']); + return false; + } +} diff --git a/apps/isa-app/src/app/guards/can-activate-remission.guard.ts b/apps/isa-app/src/app/guards/can-activate-remission.guard.ts new file mode 100644 index 000000000..a36996c86 --- /dev/null +++ b/apps/isa-app/src/app/guards/can-activate-remission.guard.ts @@ -0,0 +1,30 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router'; +import { ApplicationService } from '@core/application'; +import { BreadcrumbService } from '@core/breadcrumb'; +import { Config } from '@core/config'; +import { first } from 'rxjs/operators'; + +@Injectable({ providedIn: 'root' }) +export class CanActivateRemissionGuard implements CanActivate { + constructor( + private readonly _applicationService: ApplicationService, + private readonly _config: Config, + private readonly _router: Router + ) {} + + async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { + const process = await this._applicationService.getProcessById$(this._config.get('process.ids.remission')).pipe(first()).toPromise(); + if (!process) { + await this._applicationService.createProcess({ + id: this._config.get('process.ids.remission'), + type: 'remission', + section: 'branch', + name: 'Remission', + }); + } + + this._applicationService.activateProcess(this._config.get('process.ids.remission')); + return true; + } +} diff --git a/apps/isa-app/src/app/guards/can-activate-task-calendar.guard.ts b/apps/isa-app/src/app/guards/can-activate-task-calendar.guard.ts new file mode 100644 index 000000000..dbd768a32 --- /dev/null +++ b/apps/isa-app/src/app/guards/can-activate-task-calendar.guard.ts @@ -0,0 +1,24 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot } from '@angular/router'; +import { ApplicationService } from '@core/application'; +import { Config } from '@core/config'; +import { first } from 'rxjs/operators'; + +@Injectable({ providedIn: 'root' }) +export class CanActivateTaskCalendarGuard implements CanActivate { + constructor(private readonly _applicationService: ApplicationService, private readonly _config: Config) {} + + async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { + const process = await this._applicationService.getProcessById$(this._config.get('process.ids.taskCalendar')).pipe(first()).toPromise(); + if (!process) { + await this._applicationService.createProcess({ + id: this._config.get('process.ids.taskCalendar'), + type: 'task-calendar', + section: 'branch', + name: 'Tätigkeitskalender', + }); + } + this._applicationService.activateProcess(this._config.get('process.ids.taskCalendar')); + return true; + } +} diff --git a/apps/isa-app/src/app/guards/index.ts b/apps/isa-app/src/app/guards/index.ts new file mode 100644 index 000000000..d98992815 --- /dev/null +++ b/apps/isa-app/src/app/guards/index.ts @@ -0,0 +1,12 @@ +export * from './can-activate-cart-with-process-id.guard'; +export * from './can-activate-cart.guard'; +export * from './can-activate-customer-with-process-id.guard'; +export * from './can-activate-customer.guard'; +export * from './can-activate-goods-in.guard'; +export * from './can-activate-goods-out.guard'; +export * from './can-activate-product-with-process-id.guard'; +export * from './can-activate-product.guard'; +export * from './can-activate-remission.guard'; +export * from './can-activate-task-calendar.guard'; +export * from './init-store.guard'; +export * from './is-authenticated.guard'; diff --git a/apps/isa-app/src/app/guards/init-store.guard.ts b/apps/isa-app/src/app/guards/init-store.guard.ts new file mode 100644 index 000000000..e98f0da09 --- /dev/null +++ b/apps/isa-app/src/app/guards/init-store.guard.ts @@ -0,0 +1,15 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot } from '@angular/router'; +import { RootStateService } from '../store/root-state.service'; + +@Injectable({ providedIn: 'root' }) +export class InitStoreGuard implements CanActivate { + constructor(private readonly _rootStateService: RootStateService) {} + + async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { + if (await this._rootStateService.load()) { + window.location.reload(); + } + return true; + } +} diff --git a/apps/isa-app/src/app/guards/is-authenticated.guard.ts b/apps/isa-app/src/app/guards/is-authenticated.guard.ts new file mode 100644 index 000000000..e33b65bd3 --- /dev/null +++ b/apps/isa-app/src/app/guards/is-authenticated.guard.ts @@ -0,0 +1,64 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot } from '@angular/router'; +import { AuthService } from '@core/auth'; +import { ScanAdapterService } from '@adapter/scan'; +import { AuthService as IsaAuthService } from '@swagger/isa'; +import { UiErrorModalComponent, UiModalService } from '@ui/modal'; + +@Injectable({ providedIn: 'root' }) +export class IsAuthenticatedGuard implements CanActivate { + constructor( + private _authService: AuthService, + private _scanService: ScanAdapterService, + private _isaAuthService: IsaAuthService, + private _modal: UiModalService + ) {} + + async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { + const authenticated = await this._authService.isAuthenticated(); + + if (!authenticated) { + const token = await this.scanAndGetToken(); + + if (token) { + this._authService.setKeyCardToken(token); + } + + this._authService.login(); + } + + return authenticated; + } + + async scanAndGetToken(): Promise { + const result = await this._scanService.scan().toPromise(); + + if (typeof result === 'string') { + try { + const res = await this._isaAuthService + .AuthLogin({ + code: result, + application: 'isa', + hostname: location.host, + }) + .toPromise(); + + return res.token; + } catch (error) { + const errorModalRef = this._modal.open({ + content: UiErrorModalComponent, + title: 'Fehler bei der Anmeldung', + data: { + message: 'Versuchen Sie es erneut\noder melden Sie sich mit\nIhren Benutzerdaten an.', + }, + }); + + await errorModalRef.afterClosed$.toPromise(); + + return this.scanAndGetToken(); + } + } + + return undefined; + } +} diff --git a/apps/isa-app/src/app/interceptors/http-error.interceptor.spec.ts b/apps/isa-app/src/app/interceptors/http-error.interceptor.spec.ts new file mode 100644 index 000000000..fabecf909 --- /dev/null +++ b/apps/isa-app/src/app/interceptors/http-error.interceptor.spec.ts @@ -0,0 +1,60 @@ +import { HttpErrorInterceptor } from './http-error.interceptor'; +import { createServiceFactory, SpectatorService } from '@ngneat/spectator'; +import { UiErrorModalComponent, UiMessageModalComponent, UiModalService } from '@ui/modal'; +import { throwError } from 'rxjs'; +import { HttpErrorResponse } from '@angular/common/http'; + +describe('HttpErrorInterceptor', () => { + let spectator: SpectatorService; + let modalMock: jasmine.SpyObj; + let httpErrorInterceptor: HttpErrorInterceptor; + + const createService = createServiceFactory({ + service: HttpErrorInterceptor, + mocks: [UiModalService], + }); + + beforeEach(() => { + spectator = createService(); + httpErrorInterceptor = spectator.service; + modalMock = spectator.inject(UiModalService); + }); + + it('should be created', () => { + expect(httpErrorInterceptor).toBeTruthy(); + }); + + describe('intercept', () => { + it('should catch the error and call handleError', (done) => { + const error = new HttpErrorResponse({ + status: 0, + statusText: '', + url: '', + }); + const handleErrorSpy = spyOn(httpErrorInterceptor, 'handleError').and.callThrough(); + httpErrorInterceptor.intercept(null, { handle: () => throwError(error) }).subscribe({ + error: () => { + expect(handleErrorSpy).toHaveBeenCalledWith(error); + done(); + }, + }); + }); + }); + + describe('handleError', () => { + it('should call modal.open with offline message if status is 0', () => { + const error = { + error: { + message: 'test', + }, + status: 0, + }; + httpErrorInterceptor.handleError(error as any); + expect(modalMock.open).toHaveBeenCalledWith({ + content: UiMessageModalComponent, + title: 'Die Netzwerkverbindung wurde unterbrochen', + data: { message: 'Bitte überprüfen Sie Ihre Netzwerkverbindung.' }, + }); + }); + }); +}); diff --git a/apps/isa-app/src/app/interceptors/http-error.interceptor.ts b/apps/isa-app/src/app/interceptors/http-error.interceptor.ts new file mode 100644 index 000000000..0e5be9cd9 --- /dev/null +++ b/apps/isa-app/src/app/interceptors/http-error.interceptor.ts @@ -0,0 +1,26 @@ +import { Injectable } from '@angular/core'; +import { HttpInterceptor, HttpEvent, HttpHandler, HttpRequest, HttpErrorResponse } from '@angular/common/http'; +import { Observable, throwError } from 'rxjs'; +import { UiMessageModalComponent, UiModalService } from '@ui/modal'; +import { catchError } from 'rxjs/operators'; + +@Injectable() +export class HttpErrorInterceptor implements HttpInterceptor { + constructor(private _modal: UiModalService) {} + + intercept(req: HttpRequest, next: HttpHandler): Observable> { + return next.handle(req).pipe(catchError((error: HttpErrorResponse, caught: any) => this.handleError(error))); + } + + handleError(error: HttpErrorResponse): Observable { + if (error.status === 0) { + this._modal.open({ + content: UiMessageModalComponent, + title: 'Die Netzwerkverbindung wurde unterbrochen', + data: { message: 'Bitte überprüfen Sie Ihre Netzwerkverbindung.' }, + }); + } + + return throwError(error); + } +} diff --git a/apps/isa-app/src/app/interceptors/index.ts b/apps/isa-app/src/app/interceptors/index.ts new file mode 100644 index 000000000..23520a2f2 --- /dev/null +++ b/apps/isa-app/src/app/interceptors/index.ts @@ -0,0 +1,3 @@ +// start:ng42.barrel +export * from './http-error.interceptor'; +// end:ng42.barrel diff --git a/apps/isa-app/src/app/providers/index.ts b/apps/isa-app/src/app/providers/index.ts new file mode 100644 index 000000000..bcdb7a700 --- /dev/null +++ b/apps/isa-app/src/app/providers/index.ts @@ -0,0 +1,3 @@ +// start:ng42.barrel +export * from './isa.log-provider'; +// end:ng42.barrel diff --git a/apps/isa-app/src/app/providers/isa.error-handler.ts b/apps/isa-app/src/app/providers/isa.error-handler.ts new file mode 100644 index 000000000..c2a991056 --- /dev/null +++ b/apps/isa-app/src/app/providers/isa.error-handler.ts @@ -0,0 +1,12 @@ +import { ErrorHandler, Injectable } from '@angular/core'; +import { UiErrorModalComponent, UiModalService } from '@ui/modal'; + +@Injectable() +export class IsaErrorHandler implements ErrorHandler { + constructor(private _modal: UiModalService) {} + + handleError(error: any): void { + console.error(error); + this._modal.open({ content: UiErrorModalComponent, title: 'Unbekannter Fehler', data: error }); + } +} diff --git a/apps/isa-app/src/app/providers/isa.log-provider.ts b/apps/isa-app/src/app/providers/isa.log-provider.ts new file mode 100644 index 000000000..915bc8510 --- /dev/null +++ b/apps/isa-app/src/app/providers/isa.log-provider.ts @@ -0,0 +1,22 @@ +import { Injectable } from '@angular/core'; +import { LogLevel, LogProvider } from '@core/logger'; +import { UserStateService } from '@swagger/isa'; +import { environment } from '../../environments/environment'; + +@Injectable() +export class IsaLogProvider implements LogProvider { + constructor(private readonly _infoService: UserStateService) {} + + log(logLevel: LogLevel, message: string, ...optionalParams: any[]): void { + if (!environment.production && (logLevel === LogLevel.WARN || logLevel === LogLevel.ERROR)) { + this._infoService + .UserStateSaveLog({ + logType: logLevel, + message: message, + content: JSON.stringify(optionalParams), + }) + .toPromise() + .catch(() => {}); + } + } +} diff --git a/apps/isa-app/src/app/resolvers/index.ts b/apps/isa-app/src/app/resolvers/index.ts new file mode 100644 index 000000000..6c6b2b637 --- /dev/null +++ b/apps/isa-app/src/app/resolvers/index.ts @@ -0,0 +1,4 @@ +// start:ng42.barrel +export * from './process-id.resolver'; +export * from './section.resolver'; +// end:ng42.barrel diff --git a/apps/isa-app/src/app/resolvers/process-id.resolver.ts b/apps/isa-app/src/app/resolvers/process-id.resolver.ts new file mode 100644 index 000000000..fc0058375 --- /dev/null +++ b/apps/isa-app/src/app/resolvers/process-id.resolver.ts @@ -0,0 +1,12 @@ +import { Injectable } from '@angular/core'; +import { Resolve, ActivatedRouteSnapshot } from '@angular/router'; +import { Observable } from 'rxjs'; + +@Injectable({ providedIn: 'root' }) +export class ProcessIdResolver implements Resolve { + constructor() {} + + resolve(route: ActivatedRouteSnapshot): Observable | Promise | number { + return route.params.processId; + } +} diff --git a/apps/isa-app/src/app/resolvers/section.resolver.ts b/apps/isa-app/src/app/resolvers/section.resolver.ts new file mode 100644 index 000000000..5d42f5abd --- /dev/null +++ b/apps/isa-app/src/app/resolvers/section.resolver.ts @@ -0,0 +1,27 @@ +import { Injectable } from '@angular/core'; +import { Resolve, ActivatedRouteSnapshot } from '@angular/router'; +import { ApplicationService } from '@core/application'; +import { Observable } from 'rxjs'; + +export abstract class SectionResolver implements Resolve { + constructor(protected section: 'customer' | 'branch', protected applicationService: ApplicationService) {} + + resolve(route: ActivatedRouteSnapshot): Observable | Promise | string { + this.applicationService.setSection(this.section); + return this.section; + } +} + +@Injectable({ providedIn: 'root' }) +export class CustomerSectionResolver extends SectionResolver { + constructor(applicationService: ApplicationService) { + super('customer', applicationService); + } +} + +@Injectable({ providedIn: 'root' }) +export class BranchSectionResolver extends SectionResolver { + constructor(applicationService: ApplicationService) { + super('branch', applicationService); + } +} diff --git a/apps/isa-app/src/app/shell/index.ts b/apps/isa-app/src/app/shell/index.ts new file mode 100644 index 000000000..abfb856cb --- /dev/null +++ b/apps/isa-app/src/app/shell/index.ts @@ -0,0 +1,4 @@ +// start:ng42.barrel +export * from './shell.component'; +export * from './shell.module'; +// end:ng42.barrel diff --git a/apps/isa-app/src/app/shell/shell.component.html b/apps/isa-app/src/app/shell/shell.component.html new file mode 100644 index 000000000..96300f420 --- /dev/null +++ b/apps/isa-app/src/app/shell/shell.component.html @@ -0,0 +1,70 @@ +
+ + + + + + + +
+
+ + + +
+
+
+ +
+
+ + diff --git a/apps/isa-app/src/app/shell/shell.component.scss b/apps/isa-app/src/app/shell/shell.component.scss new file mode 100644 index 000000000..04864c8c4 --- /dev/null +++ b/apps/isa-app/src/app/shell/shell.component.scss @@ -0,0 +1,60 @@ +:host { + @apply block; +} + +.main-wrapper { + @apply fixed right-0 left-0 overflow-auto; + top: 8.375rem; + bottom: 5rem; + + main { + @apply w-full max-w-content mx-auto px-4 self-stretch; + } +} + +.shell-header-wrapper { + @apply fixed top-0 left-0 right-0 bg-white; + + shell-header { + @apply w-full max-w-content mx-auto; + } + + button.notifications-btn { + @apply relative; + + .notification-counter { + @apply absolute flex items-center justify-center top-2 right-px-3 text-sm rounded-full w-6 h-6 font-semibold; + background-color: var(--shell-notification-counter-background); + color: var(--shell-notification-counter-text); + z-index: 10; + } + } +} + +.shell-process-wrapper { + @apply fixed left-0 right-0 bg-white; + top: 5.125rem; + + shell-process { + @apply w-full max-w-content mx-auto; + height: 52px; + } +} + +shell-process { + height: 52px; + grid-area: process; +} + +.shell-footer-wrapper { + @apply fixed bottom-0 left-0 right-0 bg-white z-fixed shadow-card; + + shell-footer { + @apply w-full max-w-content mx-auto; + + .active { + @apply font-bold; + color: var(--shell-footer-link-active); + } + } +} diff --git a/apps/isa-app/src/app/shell/shell.component.spec.ts b/apps/isa-app/src/app/shell/shell.component.spec.ts new file mode 100644 index 000000000..0c85a7413 --- /dev/null +++ b/apps/isa-app/src/app/shell/shell.component.spec.ts @@ -0,0 +1,370 @@ +// unit test ShellComponent with Spectator + +import { Component } from '@angular/core'; +import { Router } from '@angular/router'; +import { RouterTestingModule } from '@angular/router/testing'; +import { ApplicationProcess, ApplicationService } from '@core/application'; +import { AuthService } from '@core/auth'; +import { BreadcrumbService } from '@core/breadcrumb'; +import { DomainAvailabilityService } from '@domain/availability'; +import { DomainDashboardService } from '@domain/isa'; +import { NotificationsHub } from '@hub/notifications'; +import { ModalNotificationsComponent } from '@modal/notifications'; +import { Spectator, createComponentFactory, SpyObject, createSpyObject } from '@ngneat/spectator'; +import { DashboardComponent } from '@page/dashboard'; +import { ShellFooterComponent } from '@shell/footer'; +import { ShellHeaderComponent } from '@shell/header'; +import { ShellProcessComponent, ShellProcessTabComponent } from '@shell/process'; +import { UiIconComponent } from '@ui/icon'; +import { UiModalService } from '@ui/modal'; +import { EnvelopeDTO, MessageBoardItemDTO } from 'apps/hub/notifications/src/lib/defs'; +import { MockComponent } from 'ng-mocks'; +import { of } from 'rxjs'; +import { first } from 'rxjs/operators'; +import { ShellComponent } from './shell.component'; + +// DummyComponent Class +@Component({ + selector: 'dummy-component', + template: '
', +}) +class DummyComponent { + constructor() {} +} + +describe('ShellComponent', () => { + let spectator: Spectator; + let applicationServiceMock: SpyObject; + let modalServiceMock: SpyObject; + let notificationsHubMock: SpyObject; + let router: Router; + let breadcrumbServiceMock: SpyObject; + let authServiceMock: SpyObject; + + const createComponent = createComponentFactory({ + component: ShellComponent, + imports: [ + RouterTestingModule.withRoutes([ + { path: 'kunde', component: DummyComponent }, + { path: 'kunde/dashboard', component: DashboardComponent }, + ]), + ], + declarations: [ + MockComponent(ShellHeaderComponent), + MockComponent(ShellFooterComponent), + MockComponent(ShellProcessComponent), + MockComponent(ShellProcessTabComponent), + MockComponent(UiIconComponent), + ], + mocks: [BreadcrumbService, DomainAvailabilityService, AuthService, DomainDashboardService], + }); + + beforeEach(() => { + applicationServiceMock = createSpyObject(ApplicationService); + applicationServiceMock.getSection$.and.returnValue(of('customer')); + applicationServiceMock.getProcesses$.and.returnValue(of([])); + applicationServiceMock.getActivatedProcessId$.and.returnValue(of(undefined)); + applicationServiceMock.getLastActivatedProcessWithSectionAndType$.and.returnValue(of({})); + applicationServiceMock.getLastActivatedProcessWithSection$.and.returnValue(of({})); + + notificationsHubMock = createSpyObject(NotificationsHub); + notificationsHubMock.notifications$ = of({}); + + modalServiceMock = createSpyObject(UiModalService); + + authServiceMock = createSpyObject(AuthService); + + spectator = createComponent({ + providers: [ + { provide: ApplicationService, useValue: applicationServiceMock }, + { provide: NotificationsHub, useValue: notificationsHubMock }, + { provide: UiModalService, useValue: modalServiceMock }, + { provide: AuthService, useValue: authServiceMock }, + ], + }); + + breadcrumbServiceMock = spectator.inject(BreadcrumbService); + router = spectator.inject(Router); + }); + + it('should create', () => { + expect(spectator.component).toBeTruthy(); + }); + + describe('shell-header', () => { + it('should call setSection() on sectionChange event with the section argument', () => { + spyOn(spectator.component, 'setSection'); + spectator.triggerEventHandler('shell-header', 'sectionChange', 'branch'); + expect(spectator.component.setSection).toHaveBeenCalledWith('branch'); + }); + + it('should render the header buttons', () => { + // Test verhält sich anders, wenn die größe des Browserfensters kleiner ist als 640px, da + // die Buttons dann unsichtbar werden und ins Drei-Punkt Menü verschoben werden. + if (document.body.clientWidth > 639) { + expect(spectator.query('shell-header .notifications-btn')).toBeVisible(); + expect(spectator.query('shell-header .dashboard-btn')).toBeVisible(); + expect(spectator.query('shell-header .logout-btn')).toBeVisible(); + } else { + expect(spectator.query('shell-header .notifications-btn')).not.toBeVisible(); + expect(spectator.query('shell-header .dashboard-btn')).not.toBeVisible(); + expect(spectator.query('shell-header .logout-btn')).not.toBeVisible(); + } + }); + + it('should have a anchor tag which navigates to /kunde/dashboard', () => { + const anchor = spectator.query('shell-header a'); + expect(anchor).toHaveAttribute('href', '/kunde/dashboard'); + }); + }); + + describe('shell-process', () => { + it('should call addProcess() on addProcess event', () => { + spyOn(spectator.component, 'addProcess'); + spectator.triggerEventHandler('shell-process', 'addProcess', undefined); + expect(spectator.component.addProcess).toHaveBeenCalled(); + }); + + describe('shell-process-tab', () => { + it('should render for each process', () => { + const processes = [{}, {}, {}]; + applicationServiceMock.getSection$.and.returnValue(of('customer')); + applicationServiceMock.getProcesses$.and.returnValue(of(processes)); + spectator.detectComponentChanges(); + expect(spectator.queryAll('shell-process-tab')).toHaveLength(processes.length); + }); + + it('should call activateProcess() on activateProcess event', () => { + const processes = [{ id: 1 }]; + + applicationServiceMock.getProcesses$.and.returnValue(of(processes)); + spectator.detectComponentChanges(); + + spyOn(spectator.component, 'activateProcess'); + spectator.triggerEventHandler('shell-process-tab', 'activateProcess', processes[0].id); + expect(spectator.component.activateProcess).toHaveBeenCalledWith(1); + }); + + it('should call closeProcess() on closeProcess event', () => { + const processes = [{ id: 1 }]; + + applicationServiceMock.getProcesses$.and.returnValue(of(processes)); + spectator.detectComponentChanges(); + + spyOn(spectator.component, 'closeProcess'); + spectator.triggerEventHandler('shell-process-tab', 'closeProcess', processes[0].id); + expect(spectator.component.closeProcess).toHaveBeenCalledWith(1); + }); + }); + }); + + describe('shell-footer', () => { + it('should render when section is set', () => { + applicationServiceMock.getSection$.and.returnValue(of('customer')); + spectator.detectComponentChanges(); + expect(spectator.query('shell-footer')).toBeVisible(); + }); + + it('should not render when section is undefined', () => { + applicationServiceMock.getSection$.and.returnValue(of(undefined)); + spectator.detectComponentChanges(); + expect(spectator.query('shell-footer')).not.toBeVisible(); + }); + + it('should display the menu items for section customer', () => { + applicationServiceMock.getSection$.and.returnValue(of('customer')); + spyOnProperty(spectator.component, 'lastCartProcessId$').and.returnValue(of(123)); + spectator.detectComponentChanges(); + + const anchors = spectator.queryAll('shell-footer a'); + expect(anchors[0]).toHaveText('Artikelsuche'); + expect(anchors[0]).toHaveAttribute('href', '/kunde/123/product'); + expect(anchors[1]).toHaveText('Kundensuche'); + expect(anchors[1]).toHaveAttribute('href', '/kunde/123/customer'); + expect(anchors[2]).toHaveText('Warenausgabe'); + expect(anchors[2]).toHaveAttribute('href', '/kunde/goods/out'); + }); + + it('should display the menu items for section branch', () => { + applicationServiceMock.getSection$.and.returnValue(of('branch')); + spectator.detectComponentChanges(); + + const anchors = spectator.queryAll('shell-footer a'); + expect(anchors[0]).toHaveText('Tätigkeitskalender'); + expect(anchors[0]).toHaveAttribute('href', '/filiale/task-calendar'); + expect(anchors[1]).toHaveText('Abholfach'); + expect(anchors[1]).toHaveAttribute('href', '/filiale/goods/in'); + expect(anchors[2]).toHaveText('Remission'); + expect(anchors[2]).toHaveAttribute('href', '/filiale/remission'); + }); + }); + + describe('activatedProcessId$', () => { + it('should call _appService.getActivatedProcessId$() and return its value', async () => { + applicationServiceMock.getActivatedProcessId$.and.returnValue(of(1)); + const processId = await spectator.component.activatedProcessId$.pipe(first()).toPromise(); + expect(processId).toBe(1); + expect(applicationServiceMock.getActivatedProcessId$).toHaveBeenCalled(); + }); + }); + + describe('section$', () => { + it('should call _appService.getSection$() and return its value', async () => { + applicationServiceMock.getSection$.and.returnValue(of('branch')); + const section = await spectator.component.section$.pipe(first()).toPromise(); + expect(section).toBe('branch'); + expect(applicationServiceMock.getSection$).toHaveBeenCalled(); + }); + }); + + describe('processes$', () => { + it('should call _appService.processes$() and return its value', async () => { + applicationServiceMock.getProcesses$.and.returnValue(of([{}, {}])); + const processes = await spectator.component.processes$.pipe(first()).toPromise(); + expect(processes).toHaveLength(2); + expect(applicationServiceMock.getProcesses$).toHaveBeenCalledWith('customer'); + }); + }); + + describe('setSection()', () => { + it('should call _appService.setSection() with the argument section', async () => { + await spectator.component.setSection('customer'); + expect(applicationServiceMock.setSection).toHaveBeenCalledWith('customer'); + }); + + it('should call activateProcess if getLastActivatedProcessWithSection returns a value', async () => { + applicationServiceMock.getLastActivatedProcessWithSection$.and.returnValue(of({ id: 1 })); + spyOn(spectator.component, 'activateProcess'); + await spectator.component.setSection('customer'); + expect(spectator.component.activateProcess).toHaveBeenCalledWith(1); + }); + }); + + describe('logout()', () => { + it('should call _authService.logout()', () => { + spectator.component.logout(); + expect(authServiceMock.logout).toHaveBeenCalled(); + }); + }); + + describe('addProcess()', () => { + it('should call navigate to /kunde/{timestamp}/product', () => { + spyOn(router, 'navigate'); + spyOn(Date, 'now').and.returnValue(123); + spectator.component.addProcess(); + expect(router.navigate).toHaveBeenCalledWith(['/kunde', 123, 'product']); + }); + }); + + describe('closeProcess()', () => { + it('should call _appService.removeProcess() with the processId argument', () => { + const processes = [{}, {}, {}]; + applicationServiceMock.getSection$.and.returnValue(of('customer')); + applicationServiceMock.getProcesses$.and.returnValue(of(processes)); + spectator.component.closeProcess(1); + expect(applicationServiceMock.removeProcess).toHaveBeenCalledWith(1); + }); + + it('should navigate to kunde/dashboard if no process is available', async () => { + spyOn(router, 'navigate'); + applicationServiceMock.getSection$.and.returnValue(of('customer')); + applicationServiceMock.getProcesses$.and.returnValue(of([])); + spectator.detectComponentChanges(); + await spectator.component.closeProcess(1); + expect(router.navigate).toHaveBeenCalledWith(['/kunde', 'dashboard']); + }); + + it('should not navigate to kunde/dashboard if processes are available', async () => { + spyOn(router, 'navigate'); + const processes = [ + { id: 1, name: 'test', section: 'customer' }, + { id: 2, name: 'test', section: 'customer' }, + ]; + + applicationServiceMock.getLastActivatedProcessWithSection$.and.returnValue(of({})); + applicationServiceMock.getSection$.and.returnValue(of('customer')); + applicationServiceMock.getProcesses$.and.returnValue(of(processes)); + await spectator.component.closeProcess(1); + expect(router.navigate).not.toHaveBeenCalled(); + }); + + it('should activate the next process when it was not the last process', async () => { + spyOn(spectator.component, 'activateProcess'); + + applicationServiceMock.getLastActivatedProcessWithSection$.and.returnValue( + of({ + id: 2, + name: 'test', + section: 'customer', + activated: 2, + }) + ); + + const processes = [ + { id: 1, name: 'test', section: 'customer', activated: 1 }, + { id: 2, name: 'test', section: 'customer', activated: 2 }, + ]; + applicationServiceMock.getSection$.and.returnValue(of('customer')); + applicationServiceMock.getProcesses$.and.returnValue(of(processes)); + await spectator.component.closeProcess(1); + + expect(spectator.component.activateProcess).toHaveBeenCalledWith(2); + }); + }); + + describe('activateProcess()', () => { + it('should get the last activated breadcrumb by key and if it is defined it navigates to its path with queryParams', async () => { + const crumb = { path: '/kunde/product', params: { id: 1 } }; + spyOn(router, 'navigate'); + breadcrumbServiceMock.getLastActivatedBreadcrumbByKey$.and.returnValue(of(crumb)); + + await spectator.component.activateProcess(1); + expect(router.navigate).toHaveBeenCalledWith([crumb.path], { queryParams: crumb.params }); + }); + + it('should navigate to /kunde if no breadcrumb for this process exists', async () => { + breadcrumbServiceMock.getLastActivatedBreadcrumbByKey$.and.returnValue(of(undefined)); + spyOn(router, 'navigate'); + + await spectator.component.activateProcess(1); + expect(router.navigate).toHaveBeenCalledWith(['/kunde']); + }); + }); + + describe('processAction()', () => { + it('should navigate to cart when process type is cart', () => { + spyOn(router, 'navigate'); + + const process: ApplicationProcess = { id: 1, name: 'Vorgang', section: 'customer', type: 'cart' }; + spectator.component.processAction(process); + expect(router.navigate).toHaveBeenCalledWith(['/kunde', process.id, 'cart']); + }); + + it('should not navigate to when process type is not cart', () => { + spyOn(router, 'navigate'); + + const process: ApplicationProcess = { id: 1, name: 'Vorgang', section: 'customer', type: 'goods-out' }; + spectator.component.processAction(process); + expect(router.navigate).not.toHaveBeenCalled(); + }); + }); + + describe('openNotifications()', () => { + it('should call modalService.open() with the ModalNotificationComponent', async () => { + const notifications: EnvelopeDTO = { + data: [{}, {}, {}], + }; + + spectator.component.notifications$ = of(notifications); + + await spectator.component.openNotifications(); + expect(modalServiceMock.open).toHaveBeenCalledWith({ + content: ModalNotificationsComponent, + data: notifications, + config: { + showScrollbarY: false, + }, + }); + }); + }); +}); diff --git a/apps/isa-app/src/app/shell/shell.component.ts b/apps/isa-app/src/app/shell/shell.component.ts new file mode 100644 index 000000000..993641d1e --- /dev/null +++ b/apps/isa-app/src/app/shell/shell.component.ts @@ -0,0 +1,135 @@ +import { Component, ChangeDetectionStrategy, ViewChildren, QueryList } from '@angular/core'; +import { ApplicationProcess, ApplicationService } from '@core/application'; +import { first, map, shareReplay, switchMap } from 'rxjs/operators'; +import { NotificationsHub } from '@hub/notifications'; +import { ModalNotificationsComponent } from '@modal/notifications'; +import { UiModalService } from '@ui/modal'; +import { Router } from '@angular/router'; +import { BreadcrumbService } from '@core/breadcrumb'; +import { combineLatest } from 'rxjs'; +import { AuthService } from '@core/auth'; +import { DomainAvailabilityService } from '@domain/availability'; +import { ShellProcessTabComponent } from '@shell/process'; + +@Component({ + selector: 'app-shell', + templateUrl: 'shell.component.html', + styleUrls: ['shell.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ShellComponent { + private readonly _now = Date.now(); + + @ViewChildren('processTabs') + readonly processTabs: QueryList; + + notifications$ = this._notificationsHub.notifications$; + + notificationCount$ = this.notifications$.pipe(map((message) => message?.data?.length)); + + get activatedProcessId$() { + return this._appService.getActivatedProcessId$().pipe(shareReplay()); + } + + get lastCartProcessId$() { + return this._appService.getLastActivatedProcessWithSectionAndType$('customer', 'cart').pipe(map((process) => process?.id ?? this._now)); + } + + get section$() { + return this._appService.getSection$().pipe(shareReplay()); + } + + get processes$() { + return this.section$.pipe(switchMap((section) => this._appService.getProcesses$(section))); + } + + get addProcessLabel$() { + return combineLatest([this.section$, this.processes$]).pipe( + map(([section, processes]) => (section === 'customer' && processes.length === 0 ? 'VORGANG STARTEN' : '')) + ); + } + + get canAddProcess$() { + return this.section$.pipe(map((section) => section === 'customer')); + } + + get currentBranch$() { + return this._availabilityService.getCurrentBranch(); + } + + constructor( + private readonly _appService: ApplicationService, + private readonly _notificationsHub: NotificationsHub, + private readonly _modal: UiModalService, + private readonly _router: Router, + private readonly _breadcrumbService: BreadcrumbService, + private readonly _authService: AuthService, + private readonly _availabilityService: DomainAvailabilityService + ) {} + + async setSection(section: 'customer' | 'branch') { + this._appService.setSection(section); + + const lastProcessId = (await this._appService.getLastActivatedProcessWithSection$(section).pipe(first()).toPromise())?.id; + if (lastProcessId) { + this.activateProcess(lastProcessId); + } else { + this._router.navigate([section === 'customer' ? '/kunde' : '/filiale']); + } + } + + // Process werden über Guards erstellt und aktiviert. An dieser Stelle wird nur navigiert + async addProcess() { + const processId = Date.now(); + await this._router.navigate(['/kunde', processId, 'product']); + } + + async activateProcess(activatedProcessId: number) { + const latestCrumb = await this._breadcrumbService.getLastActivatedBreadcrumbByKey$(activatedProcessId).pipe(first()).toPromise(); + + if (latestCrumb) { + await this._router.navigate([latestCrumb.path], { queryParams: latestCrumb.params }); + } else { + await this._router.navigate(['/kunde']); + } + } + + async closeProcess(processId: number) { + this._appService.removeProcess(processId); + + const processes = await this.processes$.pipe(first()).toPromise(); + if (processes.length === 0) { + await this._router.navigate(['/kunde', 'dashboard']); + return; + } + + const section = await this.section$.pipe(first()).toPromise(); + const lastActivatedProcess = await this._appService.getLastActivatedProcessWithSection$(section).pipe(first()).toPromise(); + this.activateProcess(lastActivatedProcess?.id); + } + + processAction(process: ApplicationProcess) { + if (process?.type === 'cart') { + this._router.navigate(['/kunde', process.id, 'cart']); + } + } + + async logout() { + await this._authService.logout(); + } + + async openNotifications() { + const notifications = await this.notifications$.pipe(first()).toPromise(); + this._modal.open({ + content: ModalNotificationsComponent, + data: notifications, + config: { + showScrollbarY: false, + }, + }); + } + + trackByIdFn(process: ApplicationProcess) { + return process.id; + } +} diff --git a/apps/isa-app/src/app/shell/shell.module.ts b/apps/isa-app/src/app/shell/shell.module.ts new file mode 100644 index 000000000..db4f6e7b3 --- /dev/null +++ b/apps/isa-app/src/app/shell/shell.module.ts @@ -0,0 +1,19 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { OverlayModule } from '@angular/cdk/overlay'; + +import { ShellHeaderModule } from '@shell/header'; +import { ShellProcessModule } from '@shell/process'; +import { ShellFooterModule } from '@shell/footer'; + +import { ShellComponent } from './shell.component'; +import { UiIconModule } from '@ui/icon'; +import { RouterModule } from '@angular/router'; + +@NgModule({ + imports: [RouterModule, CommonModule, ShellHeaderModule, ShellProcessModule, ShellFooterModule, UiIconModule, OverlayModule], + exports: [ShellComponent], + declarations: [ShellComponent], + providers: [], +}) +export class ShellModule {} diff --git a/apps/isa-app/src/app/store/root-state.service.ts b/apps/isa-app/src/app/store/root-state.service.ts new file mode 100644 index 000000000..156be85ea --- /dev/null +++ b/apps/isa-app/src/app/store/root-state.service.ts @@ -0,0 +1,95 @@ +import { Injectable } from '@angular/core'; +import { Logger, LogLevel } from '@core/logger'; +import { Store } from '@ngrx/store'; +import { UserStateService } from '@swagger/isa'; +import { debounceTime, switchMap } from 'rxjs/operators'; +import { RootState } from './root.state'; +import packageInfo from 'package'; +import { environment } from '../../environments/environment'; + +@Injectable({ providedIn: 'root' }) +export class RootStateService { + static LOCAL_STORAGE_KEY = 'ISA_APP_INITIALSTATE'; + + constructor(private readonly _userStateService: UserStateService, private _logger: Logger, private _store: Store) { + if (!environment.production) { + console.log('Die UserState kann in der Konsole mit der Funktion "clearUserState()" geleert werden.'); + + window['clearUserState'] = () => { + this.clear(); + console.log('UserState wurde geleert. Bitte Seite neu laden.'); + }; + } + + this.initSave(); + } + + initSave() { + this._store + .select((state) => state) + .pipe( + debounceTime(500), + switchMap((state) => { + const raw = JSON.stringify({ ...state, version: packageInfo.version }); + RootStateService.SaveToLocalStorageRaw(raw); + return this._userStateService.UserStateSetUserState({ content: raw }); + }) + ) + .subscribe(); + } + + /** + * Loads the initial state from local storage and returns true/false if state was changed + */ + async load(): Promise { + try { + const res = await this._userStateService.UserStateGetUserState().toPromise(); + + const resContent = res?.result?.content ?? null; + const storageContent = RootStateService.LoadFromLocalStorageRaw(); + + if (resContent) { + RootStateService.SaveToLocalStorageRaw(res.result.content); + } + + if (resContent !== storageContent) { + return true; + } + } catch (error) { + this._logger.log(LogLevel.ERROR, error); + } + return false; + } + + clear() { + this._userStateService + .UserStateResetUserState() + .toPromise() + .catch((error) => this._logger.log(LogLevel.ERROR, error)); + RootStateService.RemoveFromLocalStorage(); + } + + static SaveToLocalStorage(state: RootState) { + RootStateService.SaveToLocalStorageRaw(JSON.stringify(state)); + } + + static SaveToLocalStorageRaw(state: string) { + localStorage.setItem(RootStateService.LOCAL_STORAGE_KEY, state); + } + + static LoadFromLocalStorage(): RootState { + const raw = RootStateService.LoadFromLocalStorageRaw(); + if (raw) { + return JSON.parse(raw); + } + return undefined; + } + + static LoadFromLocalStorageRaw(): string { + return localStorage.getItem(RootStateService.LOCAL_STORAGE_KEY); + } + + static RemoveFromLocalStorage() { + localStorage.removeItem(RootStateService.LOCAL_STORAGE_KEY); + } +} diff --git a/apps/isa-app/src/app/store/root.reducer.ts b/apps/isa-app/src/app/store/root.reducer.ts new file mode 100644 index 000000000..bd6810d63 --- /dev/null +++ b/apps/isa-app/src/app/store/root.reducer.ts @@ -0,0 +1,4 @@ +import { ActionReducerMap } from '@ngrx/store'; +import { RootState } from './root.state'; + +export const rootReducer: ActionReducerMap = {}; diff --git a/apps/isa-app/src/app/store/root.state.ts b/apps/isa-app/src/app/store/root.state.ts new file mode 100644 index 000000000..add82eec2 --- /dev/null +++ b/apps/isa-app/src/app/store/root.state.ts @@ -0,0 +1,3 @@ +export interface RootState { + version?: string; +} diff --git a/apps/isa-app/src/assets/.gitkeep b/apps/isa-app/src/assets/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/isa-app/src/assets/icons.svg b/apps/isa-app/src/assets/icons.svg new file mode 100644 index 000000000..4a71a3013 --- /dev/null +++ b/apps/isa-app/src/assets/icons.svg @@ -0,0 +1,215 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/isa-app/src/assets/icons/icon-144x144.png b/apps/isa-app/src/assets/icons/icon-144x144.png new file mode 100644 index 0000000000000000000000000000000000000000..85fdc2128a09ea6f2dc9143683e9c5d0d18e64a2 GIT binary patch literal 3807 zcmds4`8yO|7iSQYWri5Wo-Ku}SweOZvLx}1VJxF;lbsl2U!p;FLf@n$TWKt#Fvu80 zg)D=yl|6%O!|=}gXT0wZ=YH;ao_o(Z_uO+o=ecP%w{LQu7d+3vz`$v4W^6~ttp5tz zIr_M4y3j%g#$dafh75H>!mD(J+1tR%fPvwCCi@ATg{}i1nK=bBFmQGMR~QM%Do^?( z#N61xJ_5E`!rH)R0!b#CU4+`_iWi#k!+Q?2UHN_g62 z6m6RzRxy>aJ7TRz)F4M+5JoTbq*d*o#Q7SMHCcx$O{8C#q(g|(1ZUBGOOzP)1~b-MEH--LDa zA!Xinv$cZf+jMKNv_-yroJ;ifmz}YDt|kgV@fQnU(zQF9$uZhKtx8axh=#WDWKJPd zEcA`{aM_>3OXblj)w*b%)5FEOvl}R?F?JbqOMmzG*I@oTugtiQsO$9=`OM&NdG}ze zCo;F63t*iKIfT>)XVxV%b$G@Q&+oaS#zfBkj#%HsE@wg%{%JWqKG+f)E7Lv;WV9+& zy6B^&(U6Rs{9qFHXBs;8yoiaFtFQN|kk@#f%WCh#KrWH%QYGsC&k|Tcrq|vYl$Xa) z-=0vqK?X-pHD(_jRP%kiW39_KO4}Jw{NPVgpKM1(QI13AuFtha?*7glmb}Bx78fEs z-Jtm5Xm@!aYU}eQjp;{5ZZo*hbRDDilO2mz+CC|vi4@Rgg%Q;i#3m-(e{BgR7GHa} zG+1=U=VuL+!~WpUj0;)}eG<5&pyECB{6&5A(GJVdjy|JR+<$?3(~>aE_9CGz%76A# ztFTs3kBbmhFLcd*)QHX4ouuJ6ktNl6)dko0qtYV5cPL+>#B;dJ<%&a%Eb{Z7Lr4f9 z^#ly@ULM~C%?RN zi&7n8{ll(aGofwD-Ws z4I}Nlw>CB;mYe~C4wZ(_7TJTkIsSg(ca=(OxLtaEXUex;nkB1&g&o4YY@EC@PER^* zr;A;&j|rrcWuLizuJTr1P-tOXvD!l(HXh`st;|W5arXg%@|IYivFcw_^z|sHvU{Rj zxA=>jVkor;3b>_~Q0RWde2SVEghg+gtd6}aa7OKo)rlIi(8C_E2lQ8!*2XH$Ioj2r z$ExqnSTdAF{)Ud9>!y<1!jbm^MyqU>pz(vlDq%aBR0f>vC_8fx4nytOHYMkTU} zMbGs|q!!vLMD1^64=8p_V|`h2wZj9PT7$fnF5^ERNeC*r>4SGWLBGWXCR zR{ODYBs~~>!WR-Q>L9=g>ywwxt<>VE!mZ(mK4Xlnb7{k=b7+bdE09qKGuih5JswRi zaxZY9>!vW^6xMfnN?8?7Z2CoaHgHNiPKZjH*J$GL)9&R`exu-x6ObJj@ z6eH?zJ|){5;HlIk0@xW?sd(`HC4LW25mE+m&?FE$EQz~EA%CVP_mVXG`N0dXG+XpY zW!#K#NaJQ8W98${JFhJRH^JXp0vDaNtjl!F56&CSVWb5Q(tnZA7gb7QRX%_c8OzlD zi{&)@r_^>h@EnP8`a+4!21ly>boP`S275+=)BIgKVo+K6B$Xx(uqr&A5po7e(Zrwo z?#%apCh?iHg)bFa>iF$1OTZeb8A-|VZ*{}R2bAE0)@z$_VQg%Qi3*YqMw9V{90w0bivYt6Nck8o zL?qsH!mZ${r&~-sBVy<|bXqPt5a~blF_3U(eV&aVZiDp_up5|c^kx-GQm=@eRGS@+ zc5{3=)r?f6ze0=Z6nF;{VxTXVf_g~jO8jp!oo-jA8Vpvv;q;@sv-xFESFhKxkeQVm z-K__Hc4xsnfCt`Ye!6bJln9!d5QHDR@wz17JWSIws!x!n-bUZ~1gr$Jli#cVv~%m3 z-ZNlEiNe`9%M#ksWUgeEFe+Eq(sg82taiDI0K?Soa?d4XSBGyKp09{dnS+|QU^klnVm8^2u$7bciO`I3;ZwM?nGP~N-2~p zOM?G=KR!z@g(`>mQa9HKrB`ExPBk60Vob2k-)~0d?V6Bn;rO8S%qRl~uYs1B=NHEP zGP_|QM0SKcHf(dsO?aV5pp5-SrsF?YpI=nMkJt46e?bPKV4wGy4wJQ}Y-V*FJa+{OB=m zks+Z!Z93Zk`=)EzMA-&w>OY`_nk+^Hr)tO;k+jDg=MVxHCeES5i-M-fJyN<>xQtxB zeY^0S>m`Gx`h0pZEY*n8xj(UK~yP`%}D(HKo!p!;{= z1FG6c!s>!XQ%-NOz^(~zvmjzq&;7Kx3+XMxE!* zJ_2Qu)@2QfIf2r9^!EBPS{(s^Kg)pfu|WC2rx$4!Xh@N+Ty#rbJwY##J7+g}lZl%C z5$SE0c1>p4|MCu%LHTmNS$3BMk_%M3EoS7Jqf89m>^{A6m&4@Y50^{0tEDFD{=7#P zNXfw~5+YW(`|AhpKQyv^OsjU6(?;KlP)~=RS2(`68C_uyeWQ9_mJ?ym>uIcyc%AjV z!cyMDJ+6)SRUB>D?1zgc{V|ms44=$}X!mMYf*-~zH=yyrtaCq8d;XFu)E)Hv3ZrleVcy-Iii+IhD650xrjx=oFKOc2(PcTML{%TSTO!mjTR(61kNmeAjKIqpYh>)5RZ>M&!$=Vp-D(O<-jhwFj z9^ky)wV*_9lB48njA?{v2%7nhi75hg{zBPx!!SM!|Kc=XRu#*xx)tk9A>Xe?CwwV%08<%-5eXZqxbvUBfD9+cq+o zQ>{PH4WL96WTWDh_n~)zV_Cxiz;%OZQ!RN*GwP*>ZLet7 zaXL+W1k*uDVWC0MqaGc=m=z}4mJ*6U+7^nszOS)1pAyUr5kU^a>os({vB6@poYxt% o&!S|-U&aF@uKXXsdE-+~WQ`Zlk_t|ve@Yq5O>P_48M?*)2j+tDMgRZ+ literal 0 HcmV?d00001 diff --git a/apps/isa-app/src/assets/icons/icon-192x192.png b/apps/isa-app/src/assets/icons/icon-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..66548bbd36475b55e66edfd9bfb8e2ac6cde8543 GIT binary patch literal 4708 zcmeHL=U3Csw*G}qVkjaY#ej&20)hxcq=_`?RS1HRP^2ThcaVs5LFph;M1|11Kp;}2 zDnV-K(o5(?+Rb~CT zxmVaPy-Ik@)WX$V=d|2yq(X!;5*ZfaxRPcn1nE7koZCK{1|MOP;&s?(JVr0G76_nC!39VsbQdj z>85kbco~*1i3L@U19(GOY+%HkFb0lWT(`u>ES!7oQtFRczy}A=h`r3((R*^qL~yk zKh^W|6C$_s+28&0wbMPkHfj1`^L7VED<`#kveagsLPy*+j(lwkTqHEg+L=(No9#0f z#VzMJn6D~7-nYGwV&45dimAYxIOVpK<=tI5Ae-?e8uPrdW3?X)w2U+b6*inK=f&&U z>3APy4ai*t6!(%CP?A)9F_0~Ec5;{klkw~7O1xp+{)Sn%cTxV~5bGw_KH4u}w-25- z`e2pvXYI>r?#&zq0tAh-1kHSTDB)+_i!avE)+;M}xtI2L#~ zX*m)y+sY%y&d9e`b19e^48^?YOSd~lksKts56BqFp6%iJJ)d;OUDLOo6NVjRO1fKJ zoE>Tl~Tx3$NsR8~Q)kas6*rYY!@yyukXwJ(J75eqv+h+kUBq_UpSl&j#r~IuCR(SDAKupuj%D{XMDWd29j*l zsdn;Dx&ww5wg`n2816?r4?f%1t8pAdzyw0RJpJ0ymK3S6H?yhmeZLqu^)EN9bD16W zZt)I-0E+`TT##DBdN=Da62thmdmoYJLvP$)L|toO|4*GDD& z%^|v*A>R-|lt2y)x{W2Lk6BRRG=&=Mg;7GnpuF&@bk9(&hb#Gr2gA5~;Mv)rv;7fK zmlf=tvcZ#_>+jdn=C|(zjHsO7<}AK5GNavr+Yj*z(PdFde0I5rm)?2u z(IHs~G~;bS7iM-APA{8~tAM83EX<%90JT85r($vZXQX(h+T0=qv*7&~y$NRr6TlZ zYMrk|ShC`w)BeP!aM8ekL<<|%l>pSF2VPS*j&o+@vwevo!vSn01B&9qxnFmfpvi)@u1z(Z#xFZd4L$h#RF!g(b zU86S<)=R0D%%2FJuugd%_O6&$#MpaR*t}WVX>Sxm83N9h@^%*S;2Udu1;HCC?+ApL z2cJE~G;TH^rbt;S)tyLZ$^jP&2sVF|+AD51$lE6pW`c&GjIe> zfx?#==-;)p*oZ_G+_IwtAAJ=v1Q=pD;sPnTVv3y8HONLub@H-N*-r%hgP9P-cxyN{ zwd>|Hxy|WG-o0{9Y9lu5w&xx_2DgAUVN2>ksEY7SM zgszh`nQkrsg;XI>#%_FP(vB(k!3!YXatwXa&Z+WeZ7(emV9OT_uSluM#jjW(F{^s<|8~R_CxtW*{Jl zv_?eIbM0;tpwqc|sIi#yySoaXZRO)U+&6()0?xrycYM`L75_qi%IMFPE*?eR=s$na zO(OR40*i6w3(dK5GKVhWOJ!pa;MjX~?(r7&)+rtYtV`8x*G_twTw&@o4^yIdYPBMG z<4keZa($hV zA*8`=zPvU8TEPQkZez>G7W*=&BM&8$`bSJeRzlmH{Qk7-r9!-@z8aOn_H>FEicjYE zuKV2yc}JbWg>xLl-0q*Tl;Pko@@XaCrHv86np1gF0xlP%$`+hJzqWXQhS>t4db1EMW^5YFq@#7i3QrNuwar0;!gx4fG*xuEvExmq0U_QMVQGKT_ z9;y|e%NzoTc9Gq4eJtEh1ZX=l;tZYX^{>H zdzhZeeMigXnwoww(;V-`fp|?TZ;9sR1}8z09U`w3S*kScTFOQ7?-Bt4`q&l?a;X}( zo5ngV*#XZ##TCOPCIc{YO=Z8Zbzr^DWYio}Pk+t6rmU!Zjm#J$Hta>ntba4OOIHG65I?7%OatW4vu5)pHqDud|U6MRSGc$ZeneD#$x$#}sESAv9%_43CVS5}bB0&iT$m3|`BQ|NFp4ot}?{18TH zFyD3c#j7L2)pt$F`{PTQZ}<)aQ4cA3HorbH2~wA+*QByT3T8y zU&%dDoC|%Z)6~#~da6W45FTuJ$G9@fqrfF*m%c#ocm98tN|+xV@9+?tnSz`r46QoU zdGz_B2~K{ddu{G7qG#k_6MMMGNT~no`u=fop3=U1O=nP)_P6hdoJK{y7}gcS?HC2& z=T)|de##}ZtF{)c3TH&@RTvn2sFSDc6ZTp80Sf|OntjA*Pjcve z0Uu4q8=5_MC1v(h*&Ok<9guI@c&)(^0e?-u5*9)duF>4x?7*z_>z`A zw^=Vxxf^NQd&|1pbCR+qBV8KK3p`0JUP6gr<}I%3t&cAsG>iuPj`4q`3nIgLi1CfSU8(%n&76Dca5Tw?=S~-R*PsC8Q!fas4^I$TYuQngVqcP2{J)xJH(vD~mxp%nR@2 zah}pe%vSnO*WV-bU_rV{Zyl4affy>X9)TEs>i~-4R{&ios;r#*7izY_W|oMo?`$2uxdQK zX9xiF;8%Kpg$exF3?A48Kj{1nAE*LFJ?9s}AB@g-b?yQ{N!;;0yCdN5qmLh&`2hf1 z^Wh&|n|Hng07!n;xOdk$*m`-Cx#}`xr_E;(D{_Cg&yLPjSSMr^`x16$k%fij=A~;a znh#VyA7fb%pj*7~@cr1EhZd<99&&P;76{KrMx>lu{WYT_kubBOv$Y?QIUgSA?dueu zmgW%QH`kRS#s~ltY;>Rn(JbKR&OLC`aPiQ1wv*uI@k?+6Qv*)|&R+sI6CD4Z`mYE6 z^}xR#_L?QZKH zVqjO`;D0jn{&%}j)@LpH@6?T~gOT;J&$#zXSjw1I@{)i+ZhUvNv@eKI zhEqxteeOit9jZ^-rQzLoS87(jXvGjm7-0c}W90d+i2iySwf8CtZ(XS=V|VjfP$_rrU;*dw>qHppd5 zCiS}z%s^GQ4q)onE!C}D4taC4^m{fVEQ(F;dzb=JFvGv^cb}FR9JHM1nwQaqi-E|D zhxuWQ5A@^UbquKSJX0j9R1HzmOdKFnlSO_DK%L1r)Uj|N`q02r`ucAl~ zGhhU=D^CJUuK|5Fx@>;BhHLadFOdacxDQ;52K3_q+cR{)f+r*F=_TNU8j$}0xb+%L zR?_qhDkp)Ci@?BTfcriGgz(bYX0rgZ9RJs5*yF|zXq3nE89^^@;}puac7I_=MQFL- z4GhiK^U_awVHUsq-X#_16-H+k^=^MVt@q5ms9G>RY`q;#lK)lG#*b!=r{PzBmu2nW z+*=8%KQ31LTNPvM9ZV{$blaFB1?+=6sr5tV2M3FKk>;U2#kK32gpl81yNh{fq?L8p zS_^t-xNK;ZI8F<^vy`9PlxSjo?r?WG`p|`i^bPLC*t13TwB5(xa4*6sqcXorJ4Nj( zM+1+KQ^zPxVi9X^2>4a?IR1p2QuYq8yFLyH&1EB$dlmIo*oABa}9NsU?h-OOGL5lUvkN3T=I59+FEVT;ux z0Yg@chWHVmHa;}_?(fOw?r~)so4qNn=0KvSiAAgLhPIyEO@eQMWTUnG?;gIb_`vEf8V4blj30C9rqFN;rp<_RgZMHMmJUVlt#?L;u2c(IS$s+WvMDHhkMQ!- z3epm?SbE4cQM&F)1|!Tl^NAn|3>+sSREoAeJv4TE*~@{~;G=QeF#YkHwn|=!hF_uF z#k=Q^{=G=aZh(Ge6@E}Se5af=;)jzOa>d6*?oIm_3~nB|HT%IRl~iE3dunG|;NWAC z)HA6;(`FA76kpJYxYZ}ABb-)Fcym1=xK=HH*zZO-r3hd2E3;28Vm-mXfMccpfOZR~ zu<>yjk%XDUpZ>u{Ht&{St(^-&4mc#}KtpYuff55`zjbhg0rZ*U+iN z$u^(34PG~(eY@LO{$4+8$bS7MTr4H7U!%Ze;&XzorT|>(KNtdAVS<74uJ8-Q+`0AT z!SjAyYxVZOcInFrXOJF1FIU!<1S-HEIgnxIdUtxt0>jEtGeE#JQ2YhK>rQa|nL4rS}^hMHE(! zVFtLF{>KD>b-grcxjf0jtC}$GiX{);UKN9;0NJ88?!%4w_qnXfXD7Z0)2Y}W*}VU= zQ1RfA>pQ3!mzDqf>R4T^r>b-h7`O_lg*-zHu-J_oC*1d51VrWqZ2owVhePnNN|X=E zaKl$A(2KCYVetcfgKaNxjkJd1F`;h&L(4^Us8DWyv)?^5Z!o;YjT4Z3sNbei=*Wx& zzcA$T6r2Kd<(JFNgRlWMl}m>iVz-N@e==G_Fy)(N9WT7(C?wV=jznksoD<4&qp&>w ziA|*@0|n7LspPI0Wp+%pJ8 z8e}>I;O2!-FSo4bs(aP%Z-CPZGD`bcsWknyxi1)2c21s=tf-C!$q^3%y;J}U56-7W> zX4D@&RL-kt88bKa-S~;;2E4gQZvR%=yaSkf*lcz>Ou8xxHKU_%9wtZ_e_;e0kXi*F zWiOV_C-6pqkkdR)+o~iye3B8LXV%rBeW4$P0>?t z=8%xLW=<(BmPG~y$-i59y;0q4W}l~bQV;B=V`QY0J)uzjzq2#wU_YQ&Tm|V_);CsH z$CCPksj@?*<-4+gaY~6kydVNyX)<`u zIo+-(4`N0)L9#i(3=azHxa3;D67G>B-So49E_PPkF|w7XQ}N?A&gg>}9kC#}8S^Y= z<)sJ}26k)Oe1}{^q5T4%<^k`z6Z8jn=Cf>M8E1sjJMxG0=my=q6WP?vj1**yW~tr; z*Sx~xOaGm3RsdtFJ8ZCHwm&CRuaPXOXVhsJt_wV+-oG(9aDh7zf7JUm-2_O3ZWGPm_K0rXNO$$z zxy}_w&U#BE6A+{KmFaZFC#?8>?4Vp>ht1{eC9blfqrEFF2>-xWOmEFdp2qz2up+J; zchPdSXj`W=Pia3a)O8gT%#z>nPGq7+aRhz5dX2qIvty4PF{XqmNE40hoITcRnc(#?_`NS&Uv zIoLHhG(`(CD`~;H*}X8(UCM>)gV4#7GgiaeEGr?EBfeUgGjt8Az%5PQ7kOC`n85T> z3%Hk|RWl((0l6yxgUW#Qt_E?U~tW2%KDD`M* zM=eXL={2LPz=FqXg!Am?z*L9zyG_Or>qAWWr+^pi0@gB}S)du?W;t5&$G2`S>qfZe+ZPUY27dBMb~SoQFLg zB-qKK_;UgB7XO(^pHg0g{>!&015+dWKfvc4Rs zoyiE9OL5j@FY00jV#+HCo2s)lCe(|7@wt};Mlv;bPvia*A12mM91rJZ6EVX8qknmL z(`g=we{OmN(79(AsCrW8>?SA%^0X@Sj95T>gh8ukiw;j_n3c>nY(=X9VD8gX+WA}^ zfovrMz3tA5#hSo(0s3B3ex#0=aYWD2|I(r@dRWh*SWSuH;-|YAefTC+fJGuZpa;V8 ztZZ)^)D?=2I0itN=-jf+WEN@z-1ng|B)W;$H;Nu6FTIrx8Qd-GpcuMJh#lURcH%#` z9b;$!d9ia*KOzk1>3(+pqaT7IM&Hi;YE~ z=*tEnD?Fu!udrml!-a^^8 z{m>J;O)ieiP|~7@JqK(*P=IW)Ga!_K5$WO;iOhgxXj`QHko)s=sRv;_vh$KePIoY~ z=_T_}!5~^#dJN@74w}0c`WN&VpDR%^rc)_?kJd$&H+F#R2ovDc0wRr9(KupHfx4vA zedp?aFSbAg=IPBOR*kZ`8 zGI&+Ff($Cw$ZoYAar-3|`x)IQMi5*668 zH0bBa{I~wj_qhm^hc22MU@8Y)A>u$jl>kEJoL}T@qDkLXWIt#7m&_2iKWi~;ns@vb zkyEQb-EFQRrL{8Qz)|SW+q@n8R;;LFX=|PqYAs8a;KHClM1zgv@?~ zBe(&Czmm>AhJG35gxi_!pQ)WJ^__S4p{VF$L5t+4xk1EBaik+umGjtwW-Uf zr5OU*aMl!kXtHJPLqA-l8urp@J%y5L&?HuXC$wMmIjx1uX2h}>Lb!WvrNtyAwVKyH z5{1((d7X9jAB7G+7h_o=1T5hU9SAPvf9gPaAszwmKmkJVAm&$q^3cD1y(4gZ9?1_~i;J=E$R|QhCwyt@Iv5Gsre4*vsY$WTO6%V*FUIem24X?MTxONqXU4hQ zJ7eI;obCuY0YIFJj7(kUm!1>7Y6`3C)bXx>K3e{wej;6>u=~7fR!k{YVoAh}5{_tZ z^_R7c881;%-Z6Jfgfx+g>oY|)fUg|D+a)}pAJ7zo@Oy^DqBETwkHCbDUOT~WbR<6M zM&BJMa2S*`2V?7D;)Rk{9`}K*n+Svix3-B^C*|d)7SQ{l3nCg+h_xq6LvqwuwM5V# zs_UFU+dwLvFkEB+8MXIxb{p|x1UyY`Kw9I^I=k>Qa+og6Y_ay>(z2_`xqCmo*c}sB z^~r7y37kj&2`s|xa)Pul>(Y;K;oH&Qk~#l+N>O9Oh;X3sst6T z%lnQC-vBHl_sLE~bvRvoR>ZC=#^0SX8f4>{>s8o^m|9zlI0}4fw?kFLX!4>;hoX9= z=B3m=p);YNrE;F0@fw2SM05C8!@!=`Wj%68LyisvSdoBhrMRcgNE{Y=2tgU>Om%o> z3%l~(Evx2_y0WupK&}c2Fua!GX*i1(iH*AS-x7q%<#6PA8UC%gP~M)c%xNekwZRf zr8HB&j!Z^18?rp-N9*t8JsVwyYMP-kl zxBRNr;o@g^)U3~9^El5+!g|f4AsE3|G||6FD3BW>1-z7g0i{CW7ruTwr8Qv9%=*(D ztYY1WK8oZy0zBtj7BTHS0a9`m!tmn)|8$?3@9)uKZ_>Q$s?Q!9(8}qrm&_hVwuJcdZl>~qiWyi{5CUKlD4eD9H8^{DWK$n_L9zPGTun&g8J2RofuGNgoHcFm7)N{oGwq9yMd ze-#(Oz=H3C!L4=g>e(do#dq^#p{so0qT~0gc5)0C`&(PQU77_RueETcEk`532XS@2 z*wg%-}htC_sOU3sXa$P>>s^gxTcxK?2#+a7YomK72qC)o3=#0vMA0^Su&#U@o7>>0vI9&G7GM zA#QtXZS0+5^n_5GFqGaSv+%rXh=Pcl+xoD#n12KL!>i7jNIl>yVPe znB`>zn(v+f#n@r_c!>;T#~^xq&`G3n|4H6Ln~iY`3KtRraKtd@ukR*{EW0j93X+tN z=0|`62@P!-;Yb5cEoCO)$FR`y+ZSR+Jly7vBkH-ph19G@^Dq5<;+}lweXYZodeaP0 zIzfZMGA3Al$dzo@lLx7M4li7svHMSY1&6*MXKfkStY0B%5W^(aGghw#z2D{S_vImshCz=z(UE@uXfAmglDGQf?3 zMO^2K0&}^j>|Z0yg)URsjXd5ttMR3$HNhFFJ_d1AL=dDo-u3dTj$fO$-@bpC30# z@&D50U>brWxuq>{nO^&NHY>LaEFU^iA1#Azl*Lm=yNPz_GRi#O_tNyNE$m!d`Uw|w{^4$?SdZGQ@egq|Sr&1XxTKiu zSuy-2Faph_TQC4GXfxoc)P0`-)@IxG_EBy@y9Uo2iQe_tvl9bwi`m~WQ9OpWONs_7 zMwMQV;w3gK^;P;qKW*>MYf%J3f0dy5qXJ#^3V%_{?${ZeJLXNW5FyrlZq^S$IcJ4S zTQvjS7k)177xb+k*0(Xn?jf;DN3Y9gJ(YGLe07N%^<65+OWHlZSaVTtl{1{Ve7DBk ztlP=#^9C8eUqo?F5e*La{=37GS={(p1xZPnv+Gr_?YMK2{CH9BGG?Hf-U5^j_J^7)`K5EN!6=ZP)zaM^mS9C&R36EVb=gb$4=Oi>pN#|GU@ZM%k9iaLqTLl*E z^Y4a(-3rPx_3a?vg?uYQ=+OOVl0s2|cg_S#HW=ab;CwvY3i(6ktI@h4mA03G>|#ju z30I+^KxUW^D;*H76MAB>bxe3!0{~7j0{)3iJq5@jZC66CF6K!oTPvgWilAvPSxA4B-bYb1TlTMu{d`KLEnJ9{}4F=ZhY-cUg{$ z++%f$;Xc`P>3DQZ9xCu;!mLyJ#aisomXQ;q!H0$DKTDRbEkB zZH+MZEH&7jr0*>bV}#+9SnX&}ro4G0t=xJ^>1)4Y9+#Ql8pJ({c`YRet@U}kdtuP1 zB=(h*`%QPg7`KkKw&TU$BB$AnE8QA*`q;T(5uD2HY}bIB6%Y55xuvd=y+$T_PIVHv zD-o|+dBtwtY23DhSM3qj4oKh3WoeO5gNck+1tm+|4yru%*zw)wQ(BAB$5rZIXE`vl z0QOkjI9@AvdxZ-3SZ@BYSDcaA7&EZVx7wmt%D@OKvCeA2lF89`7+qkKI=}YWb-^ZK z9T#W-2k_#v|25l`oMUu8(~&)lr$6G}EUEslqo{%Xx;0Zay4$U7wI37@8b!cfA!SNv zn2x@o7dpbL?7Fwsyy?^I;rX=&y5=Jt!%H*p*ki5-dyeC8{+O0GrLH8o7#Mk#38psH z`b7H3Q)yjJ$s0w^rB!vT(;?oyFO*-vf**K8wlE7|9*em7pAnu~9|KP7ayM=EN8j?m zl)rd>t9!tw*|8kLnsxb^lnJ|C{HtZEMWVDq$a$S-w->LFDWqy`+57v;M&F1|>Eh4M zP5HJ^UrV+9x~Q=Gm_@9PyGhQS=;=$YtZJ;g<%OzzC8a=%{A7ON+ORwaQRh4n$A2(- zQ#8_iBwvXuvZ#DA%cF0G(npf8x{0M7V}}xT-E+xmAobL9TZmkXfQsuMPFQ68@isk5`?R=z|p$Ajj{XW2x%890&GK7QIWYdvt zYn}tei(Xyq+y}?)=AWNsA+CJ2|LN5!(kvsCb@LbJs9zSEgb5^y zT!Gl=VY@qYq<58888qd-=T`c3%$>3u;8ktLLK!*Nl(|;-O6b_+K!REoDVF1`Rd82e z%+j~h@k6sIc?)zvuMs%ohx0L?m8Mj{6cMo2@Zu45xsNwjWa>qJRgr|$&dkNybImW( zeLEVK2_N^@N@lj^vyR&}Sq|+_MhjMU9_wTu8OyJek#MEQuoW4 zNQE~fZ(q`Eyi6}{rxs(dbmYnPmG?5G9hwZDLRpf}D*K04nl{w73BK(Ij%6`zmfpD- z^4#Zrv8nMo@9dH$va_IRRWVq!vt zksfCg9kmVHE3!0XRN?=9@>P|L7?;(Z$5c`ItCg=pn4MC^tF087yzAbNL_EoH&Ni?L zbnT-NkO^6MbP-AHdn|FN(irMni; zUa35n&rwt-@$AXmF}tLcgacBAM56;@n-V%*+#W(9G<)?IgZ=Ye>%A-Ty*KD=dxq7v zC$KZJe7?OODST&SHdC7Gl1HRl9DMW?y&5)_jWIgB5>_e?+KS&$xU{T(`Zfo&euE22F*B|N1N)?$=t+!{qO>`6;E%ioJ zNK*Ycp5$B0lH}#m`7baD?~w49ih(*_3B@95BVWYnrCI+QUUog|F@By@QYY?Q*|c?{tC>*{ua1DEJMH=dCg9?w4laovzKx zUI2h_v50uS1+Z6W6%QS>_Uodm-~2dsGUKs_FHyitNBTwd`8&NUycW^F9WW2X#FPY@ zzKCFQi|eIbLvdE^IOYhU9a(QQB!P8^w8&5Lj>ss-tM8=WRtrAJYF`$h-58$E@*>QD z?Em;=zg|2QY9&kKzsyAF2t9b%%Hn{#YD0LJ#Wz%FzxHWm%MTYDS-fdjw&ZJ4gNCwC zu1J3SZezo0x2o7?ImDTKwK#EWPEYB2(S}H zp4h&;+Dg_3h(vud)(0XNxXbA^%-puX-mH@zLy>3|hKW_pbC(~w%U z!2tDAbc{B^gAy|{UwhNx1mquR)&yH&0Z(Qocd?b>Te>R=SCbzy<-U?em38wpSvDt( ztFgVjt1>!i(PEn&gGQzE)N&hVUSTfLIhnPJ-Qut?6S?v(B`GTaMCV90?NHtZBSWRndJs? zO}g?|c-J_m`0KYYvvXBBnNEnOWfx+VrJo&{W4D8IGD(*Y`;cdob?BcetObWzRj}BR ztD>zG(1y`krDR1J;b#`!tdoh#LdtRcd`*=U9v5Ux)gEo`DUnU_O7-$=a(w1re03>% zDD3Vk|2zMFQjn?d+UI=+%2i7JwknH_wZu`I$k@(~fFVK89@!f^JG9V8Y1=_IwsqX< zMS5N}i(fu>ueDuOej~jn7ivFwk^$#_?eMjqzp<5z9v18A+}80XiZ~FH&dPS4lel0k z$*0Gaq4Y)i!TF@Uk?(S>m|+Ny(S@w=r1Nn}D3goC1?SMbkixUp*M=WEU!D+&YXx#< zy&tlRE&MvNuRyVESGSTT*iOjKxL25|28$_bKQ=zWu|pNrV?M+06Fkyen7mj2et%0N zY1qDU##zy;tc~H;CmA}|EFv9`c^4KWP z=@hK{G0gIWE6@2@nAFPJ8#cAQ?T=LyUbSyenO{5(J|W25iX8AS z=J5+BFXS3D&&NP2bxYnRbO>cVyA{?mBOMdkt8l^>&)M!Nr?5ezwtL#>Mg7G@@6@&< z1v83%ptO5yUzVm=+Vd2CiM)IM3}BYmWKR(_UICjbTRPu8m_gs%i&`_p&kiEMxjXr*bU1W0i-F@x4+Et@OE$ML42Eq5{kyVmNzb-f-wL_vu9eU8nXejw$8sI8^m3oy z$?kk}IMHmN{LuTQB*7+^b|59UW2Q(Kf=#jzEV``dX!P{qnRotjd0zcDUwz8t?*0vm z8A+!{2{G}~JTAervkYwq0Si~l9=(!1*9!dyRaAjHR~0kIzTfA%658N~6bDiQ1~t7p znTW5Lx*S8F?~V?7itiU~mEInnC_}CDO0&9jdS$sZIkeVjd#_`$gjNH z+w&PYJ!mT}k_WrgJ;%{2**rYL82|ibS6ZabU%iHF0pFQa-JXRF5--$*-!L0W_fbO( zsYrG{S}Q5-+of#>_x1Stgza+*4nP@%$0KfFFL%sIzAN*+eVMjZx~Z;V!@Hjmkj13^ zx=C}8mVY^cX#GU?$xX7-46~{mN=4mSDnD8MHb)2ZDlt!)?SD{H73j^}%C8bi!}se6 z<*uWDSVfMC6A?iZ)$>!Cv-a(oj{~VeTN;?}=(~MPcNx~pxiW1Ag*_jVKjIWlQ_AZV z=E9dshr;y~-k+!9#`D^}i|T}B9d&shDQ-0~3-TI`7GsS?kTHo&Ba81~A(H-S7~%#= zIB9FwJH|60-O~BK%O;i62WQebCsRtu9eiQod?vrss;${XOGwjjQUAF$(s_zhaGR@? z_y%4kyizY_R!?w`h|qZ-wh?pjSDn^g#<6877Nb0!rRs2Q%BGb26djfI8DnC|y@+rP z-;NyBcOk+_J8lv$rU~xxZGG31%|bIW^NBro!Y8D=k&mrBkVAJj#1~`>oUidV1UsB? zF;|cNYduc!f1}0)`8n$^rK~83oFK}c)U8V;1-kZT_5j~)@>C!S33qwxoN%MU-QQ%W zsI73b0+~m3PvTeBrouzmT6U;~eH)0~5o15xxhVax*lt?HG5VIFwh)xl{4zp)DR1BMoRR*>k&rZFyc25`O4r7-nv^}VgE7Rf(f`xVd{wjq5dq?m5lQ? z@N0G0l0IM$a;$$})&JK6|9aqG5Bz`iK#=NoppXqZ=)0P@qJ_mO=Zu+ms))HLLvR4uNR=<599egP)42&JPQEO$r?NHz&+lV2D_XN0BH66D+Lbb zc_ILy5`W(3Y}n11rLrVg>1ACG-@N0zjpqMQ(Ts)j z^z8hL6gjnhPH1x!&*>GtITL5|3nJ2*XYrspIh$gRZhG=n;Le%DI|%0j0){eNJbtg5 zqu!?7UisWHN8M(n1{;TDDTkRV9EN!U4+a0vHBI+xi!e4gr3T9z-4oE{Lm-B%0`3+v?bHMv}FscOV; zsKeVqRW9*(sOCVK{Tv=6W+vgqh~2G?al%&#VqiKTCg!Nr-rm05jX!N2j6hgwgK^97 z0i|RyvpiE(`)F^jhW_f8wS>(QccRQq%*6L>9P_;&CtaRkdVg%kZT{VNm+5k=>@lfn z>~3LU6J`=RBod`ZMMs0kA#ixn^@ZUzCQ?A1!7WwSkyzOIVt-Rq9qv8|!d`N6ROS1B z)&T;6%Or~P4BU$Nhl$tUdXPboQZfz(BfTV&kGfCI%*+&894eu>Ubh`> zOSx<5U-~%w<@cXPTCCbb3&`X-XlvJ|OX9q)F*Jih^C|NI+T)NC0~`s?MT^i1Ki zz+|hyik#chWiv3icmX|JA7A2t7&8ZlhYLnd$!Qxz$;W-QwYBdWq9L+4iG2){ z@{5I=TNMyazXIHru1pF(2hpG)DwTR_ElCNDLLJc2>A8NWIN8rnU0|fIuZqLr#Ksb$ zZWjBVQT~Zb4Qbr|{y5rhm&N{gjndLm_Au0IZ~U{aGY-v}TkBn&SxSGtyky@Xs5(>f zs#-Y9ujH5*gFt-3w)Kc__x&)}zkN_BTjtxhmtm4nuvJjCYv1)q$OtQQTf$UOP=J%~ z-9v1LD5I*f7uD#;@^Z`;-wAlUD^U><{j#O8_H=i5_v)0ZSC7vr@lLe9xzX&FHU*H@ z5!Sm(tS(%YS3_{`F&NLFAg!@!4!MP9PN#c{8e@?}0s$&o!p{k4D-~sus<&df#eoR} zM_79M`?i#^sSh8zAl|ju`TykZVozWfhVSVIz8U&8)M|5O;*GvcCi^66H1YNJ@ln2$ zolQrJPNhz^AhpRJDWi@JC2@)QnnrBStIX|9cpaXr1#;EZ&^tM`BcvzzCL)n79Ynco zXN|>TE!HIjp^tg+A*dnioZMVSv8?016&hx;mc7F!8M+(BR^~q8Q~4S;j9Z5flaI!)k`Aseib1gzZOv%W zd*A7wqeRli*<;URFzg!g>e||eUjK*S#QpYk~osrmidw2CxopKsQx#4;eDyZ7{9*R9he7K7h< z?o-*oR#Hw>Q$sEm{nsw)!8d5V8>!=@4xZmb#7w=1o*l|y;A5U^q2BAfKnU5JU|7bk zWBB>Sfo<)(SdvP~)&_cEJ~Q%GKEWx&`VYlF@lGaEv!}z4Oajb8+ybsSTaG;H2uvCk z(J4DpVrPA?f_ld@4HM)2iE_mv`9T9mHMLdeBd#2{M|r1oQXzd75DTpB&cP;S7{BmR zQ6j`rOiqRZbS{&M0C!jp+~dIGM*uzf&aKYXbDJsXgEak?L#h>bA7gs53Umyi4}2t> zir_&z=RONb>rh@>Lv+@YUKKO$))qjg$m53jYRlKPP2o+`NCGu35^GY$_c~@g_ zdp#g!h(xI z_ly{WvG3c9@4UZ$|A6<0d+&MfJ@=k_?s@JxPlBB-l!sH469fYBSeTn!r$_j|#?DIj zGjGCl=z$^lI@B0c(j&e|2h2X#Y_5SoWhq?$xUtYNN1*xbU=Zj`%fH6Z9`Mz;`HKcf*S#u{!GNw zPkTLypA@z~ah^S^TK81(>@fvc?4^-;i3(JhZz;tSLcEgi=)RU7_T`EC__ z2=DkUYz>P$Mg<=2B3&l1S0nXpHg(YZsy9oV6S^{0BC}q*k?W%lOR}{CBC_t8H8L-g z8>ANdKSAv5Qq$blm&Y_HKVL$SdXJXZ2~xbnrT0n^MXq%^5wznVch^Tw^lAnuc%(Ny5WFhw7$-^XLq`-*bVtb5Ly5df(fltr7!-@K+NE0X& z@x!A%xkgJyM#ealzRxhE@lpRl@LcCptISLHPwCu@f#=irfHaUsucrgK0{(WNEYk^5 z8#AU7VSB4Co}?^CLyUX}C8^K>H`AG}v_NiXFbJ*)?@tm`DnJVK2&dc+_+3*;6|Ne< z!Af0?9zq}x$M$g4!?8=oLHzvLP-%9<8XxoS>0=iv!U43n)xswoZ@?E3ahn zin>?l^!~=IPULQJjDFoH4LXjT4$xCS_e7&~G}ho?00Gk{xkw zq7ciJ)=U9fnT0d!VonXpy}Ak#vZT!J9v|)uc^es{Xe94f+;I}v5dNNnBF7@?9;-jl zG}D&kI8uqYMerN4oJc4-i}j?9S0R_=0k)s;<D%voywV`e`V&%92!@CB22P8qL71vmv2eUFGQ#QbnlEive z>&8L3U0`tl7kLxjmuHHfr6!AM+fHypnUnZ3{y2*@v^)VLnAgia+%Lccl*EG-7p_$M z|2A$*6x?|m5%(tRGJ>W78=&{|iHjHidL<7dKIIY`R1%J^30cfz9>@@9D8`O@OB)4D z)m@Lf+wdiKg)jG*)i9{DXZs*IAf6)&34n3|;es;X`JZ#<>DQSW;SNqbz@Md zeKsx29No|mDd9T@weD@4D`M=77pAd;H9_SJUG=AuDpw^o%sl6K4bR)!c1x^9f3>uQ zwm#vA=ifW7)(=S45mk{}(x^P#Z1zj2P%_ywHhzvRhZ!`91bNJ0P)>u*PldNh+K1WG zzoqyo-~((9{|L{?4kdy1=y*ThphEqvURX)SYc8*gb;%vMavDv6O?J&sEXyr)SLmPH z*B~=mAF+c!S!upfV3SQHqnU1cfT78Q>ut1WZv6{au)(Z}07q)J;FCKFdK7+e*$uz? zz2+nyef8xO`N`hU@w4T!&gpf^&3; z!2!Djg6c%*#btJylfkR2F?$#mgPcj!fDEHN5c>Vw$f-<#iLq%~jjC62yk>MAdiQO0 zFM;KDIFLGe{7TApag1p}obg76jcQG2daOzVv*^h=_LCQ|krUq3BE>4JqtY~|7TJ@O z%w4r{37FX1NXk@ z{t;>#agd`Vjl??_>+H3fzqgvtb!|Vr%FyxjJF@R0oC;sqxLgKB+>fsac3B04c|jQ!^kDd=dUo4=4RSX2E9m| zWW@Wx9!+-oA*Kz})zUl%3I1Py++_HN?XvF^ V7A2v_B6PI`vM{wZDKUm){s(&>nri?6 literal 0 HcmV?d00001 diff --git a/apps/isa-app/src/assets/images/Hugendubel_Logo.png b/apps/isa-app/src/assets/images/Hugendubel_Logo.png new file mode 100644 index 0000000000000000000000000000000000000000..d5439088502fb3113d0bafb26649cb17dbb62cab GIT binary patch literal 4084 zcmV#P)Px^u1Q2eRCodHn+cFs#SzEf!Xl>}A}p{hH=Ym_MNR{Xro><(c!46Us6fREqQrQh z(UL1=Q9Y}`GayZcAAyT8 z%?54^UxWXu@Wo>-k3icOWJb9^la2#Q4;J;Hp=Q86*L3)~uJJ3P3}p&CKogLPo+s1U zE8SgLszT|!y+%M|X2(!Ya-eh^71%m|Le4tSN{d*2n%9S+Dqkk5M9yVNt3Vx2MO(eh zUpzT27CZv}nECTia1Hxf>_lbjEzDf*Tg!oIeBaov>YTG3Sk>v$=@BTz2&A2@5QCI9 zDV`BdQ4i`R@VOC#AhW1uzx3K70T#2lq(}XbNz4AHvH{ z7rQOM)%!hsHJ}^f9AL-c8{|8H7IG44OveE^mmJI0t3{^1^#^(uOOp|tozlTJGuO+M zvt?U@7=9c&TcLBdQJ#K6Oj)V^=Gnby>@?v2^73t4;#L0No=qoYN9|#j1HYUvzmJW5 z*4|#3vB-CNI=_x_i>A)+gVwQz<0Kb&?%Cy^~#=r?2{bmzrfacB64G3ooxm3 z^}sp2wRgN6wI1+E{n0Ms?2^(+*vR=E#X6fEAZMslo~nuaPC^3JP0;&cRSy|QsqAj1 zz$I~oj7||pTY)7FhNuD10OUw0fI5&b$L3n$(UUssCgZ8@Vlh2{k?oR)Q8l zV`z^M-I2B66qPc8b|~v;tC8;pbAiTw0MIx%x*SH)Tw7~O$|YB;;y^GvYGJrn&y$hf zPlkDR&qW4`ShgRUTur1Uog-ENYbU-Ps0sXSXdpVD9z|ixzZV&3m0G=c4YE&iOlvnU zG8pRV0s&0=;sil&R&1?Ku>$R&yYuZI*Hm= zP60I}M6{4pTZB&|P}rN~Z;-WOHEEW9M2K@X4XCAZ2xUPRtAy7ge9Y5rMZOtq_Vgts z>RG?_DxZn$pQleomkGLn4B+T37};CWZZi_dnH*{B)fiZv%iHTvyITpP!FUj?vr5CO zGOa?bq~ap2BsqU0ufl1b?8Mh$G+BxSj#P)X2unK<6j@ZaB{_$lvr42qoU|#By`?;a zAFLwcRo0T)R$m%hCHz!RcH$zK`Jk;+lu)WUoRv_w&Y8OLJJMpS3yyP&C{+~m$51{{ z8)$LLK}GdJ+=9)|bVm7t`O!e$2UZ)M&mN7UIGs(M*Y@Hj-?jI(W4u&1F-0BgY%)1Q zKZcHnf7HmS*eo*rRrzy_PIYAH$B@eDjN|uzsj;!F?DIfLJDcWk4Tz(aD770L7GFvMjYvZ{4Gy*adpI% zkJITK+{QNyGf#hv5jt_@uV?S6!#rN{r8_CssFxS{*>q!i5Y$^2Ot-=c1w|cOY#wNhIekX;P%sHW=sh zYTrdQ^+WyF_^p>;%{VG<8adDD;7+Y^WE5PePw938)+M*qu4kKsr z=_C~PBA|Rt_+l}JbR>Ar$OW)c&WNjRIof?Cj;8n@wEPSS06FZN~> zU4Ca&=fM9KylZq?tv3Qqgr#x_pMq_`IeABTIqyPXNfBota08<)S(l=?tr>NpNM%0& z_Sw24Hv!9lrSlM)TLrwHXFITc(Ctn)Wj&Xro(P9qMFOwmke+j>mtP*azo(mrtoA?j z^gWP=1Ao`i-0C?TDJnh$QtoECk%5eaji50|MH9(%_DW|*#us~fIoVx6Hx;c!t)@4C zEi2xwoMYkLq1@SgkM1TQ#~T3Dmdmqg2lSe-nO?pepcQbArM|rE*~l5?tQUIvcah`h zY?QX`uaH&09Am4Or#ckvv;eeq^#(rzTVA~MqB+3kEk!;ZsH~0^gMcR1+Q>mq0Gc;# zhc-`(?*>o{=o$6=V}Z31-w%j%$yy0){xispfFn8DTwrZpi83d9`oWPtKgsnn55(CT zQkTlxPKaL%^Zz0ii_!FRsm-E_k;@t|p z$jg&`9K|`CqJ>BkFO^M#I>~d|nf;m1P~m zI$LG(UM!%FapW)YY@D;9-seNV-v=v?Qh|tkXPf0RywrEL1>{$VY;s5!0o#VpFC+gm z&&D}hL-cYCzl@dNBL6?eMq}xmO-C7L`z`#ZMs|9eDmZiomJUN053F4huSnk46n@Ps zm+x$4jDclUX;I0RxsAe)0j)frG>HAc3n0VKjO5Lv8u)8~wkJg}N9$nz4wRQvUbeS` zTp%Y%qNQY94sJ>^KyRNbh1p(qD=_>$n4VBzK6*K|7E2s`jZ*mw<1*2`3i<#A1xL1@ zme*Kb0;V}bFZ~j^Cz$E!w;^8vh5<#?B$`XcxnP1ZuFJ=8$! zA@xAQCg2yURtz^?(7I!(#5zfx3qe7NVS_E+31m- zw^Hq3TbyPn&U7zuq=U>z$wue$l=cAqDObv*^JGE~OSxx+p%*wQB_zcIXiF zhrba#i+(=&x8rD4kn^cl7T+p34Gh17`y>}VIn#N(LFjf4_cr&wF|vJl40pDZA|ag| zj1geU_LI&(pTCbdnU@IMXrK5e?~YI8(!Pdc`Z_MhOnQZidn!JPn{cn*LC~IC_al#R z2t&SoIG1vc__gcu5r?-MwTWZRnsUiM1)C@G9i;Xj8y1X43_f!xZc_ek;&zq)?_`)}b*UbHR1BcU(7*5@^Ib{m3dg*VPifsh; zFS2RNYt$&5q#hT96IqE9lp@qsR|Ub+WjNWUJrPIF)~+p4@O$2Uzskhs^8Mg>9EHhW zxQJ+oLw4-MJ?;yl4Rw#eNq*T9M#4_$d;XDk?MwLY0-|0{4j+OjqxMcaEp)(_UkC@J zWlJ1|ZG9*AohD7fD5rk3Yad3;?D|3QhqH0CTD%&a!Cw3<&PUW6at;4snnz^jeQMXP z^l3p5+&V0*GyPj^Bd~wYrgK@18tiX(^B3%V_PX}kFmlb9!TbHi{L61P|33T{pNw%X zk+#-F=tqsM${7*&=ZWMUrZK zb=gRS(iupXhvhieu3cf3I;dB!!b>uuj4lVFty@DryCHw+MWoU>YRl%(Zo~S}M!(hB zDwVg6sH5_#dp%C1?OA!^MT)jME0!yVldcHc*0xx`0evAVin4GhZKrZ1MNXZ|ex^-5 zPlG05pVd$0?bs2PDa!BmpaoBg=cXxc1Q;9UMc=$K^S@z6-VtY-)}!Gt(oG9)Xe{f&T$K2l`~CF&(J@0000 + + + Headphone + Created with Sketch. + + + + + + \ No newline at end of file diff --git a/apps/isa-app/src/assets/images/Icon_DL.svg b/apps/isa-app/src/assets/images/Icon_DL.svg new file mode 100644 index 000000000..c89d8fe49 --- /dev/null +++ b/apps/isa-app/src/assets/images/Icon_DL.svg @@ -0,0 +1,11 @@ + + + + Headphone + Created with Sketch. + + + + + + \ No newline at end of file diff --git a/apps/isa-app/src/assets/images/Icon_EB.svg b/apps/isa-app/src/assets/images/Icon_EB.svg new file mode 100644 index 000000000..2c60a90b6 --- /dev/null +++ b/apps/isa-app/src/assets/images/Icon_EB.svg @@ -0,0 +1,11 @@ + + + + E-Book + Created with Sketch. + + + + + + \ No newline at end of file diff --git a/apps/isa-app/src/assets/images/Icon_GEB.svg b/apps/isa-app/src/assets/images/Icon_GEB.svg new file mode 100644 index 000000000..9dd0fec77 --- /dev/null +++ b/apps/isa-app/src/assets/images/Icon_GEB.svg @@ -0,0 +1,11 @@ + + + + Icon_HC + Created with Sketch. + + + + + + diff --git a/apps/isa-app/src/assets/images/Icon_HC.svg b/apps/isa-app/src/assets/images/Icon_HC.svg new file mode 100644 index 000000000..dc9a9175b --- /dev/null +++ b/apps/isa-app/src/assets/images/Icon_HC.svg @@ -0,0 +1,11 @@ + + + + Icon_HC + Created with Sketch. + + + + + + \ No newline at end of file diff --git a/apps/isa-app/src/assets/images/Icon_KA.svg b/apps/isa-app/src/assets/images/Icon_KA.svg new file mode 100644 index 000000000..1df2d940b --- /dev/null +++ b/apps/isa-app/src/assets/images/Icon_KA.svg @@ -0,0 +1,11 @@ + + + + Icon_KA + Created with Sketch. + + + + + + \ No newline at end of file diff --git a/apps/isa-app/src/assets/images/Icon_KT.svg b/apps/isa-app/src/assets/images/Icon_KT.svg new file mode 100644 index 000000000..9dd0fec77 --- /dev/null +++ b/apps/isa-app/src/assets/images/Icon_KT.svg @@ -0,0 +1,11 @@ + + + + Icon_HC + Created with Sketch. + + + + + + diff --git a/apps/isa-app/src/assets/images/Icon_MA.svg b/apps/isa-app/src/assets/images/Icon_MA.svg new file mode 100644 index 000000000..889abd300 --- /dev/null +++ b/apps/isa-app/src/assets/images/Icon_MA.svg @@ -0,0 +1,11 @@ + + + + Icon_MA + Created with Sketch. + + + + + + \ No newline at end of file diff --git a/apps/isa-app/src/assets/images/Icon_NB.svg b/apps/isa-app/src/assets/images/Icon_NB.svg new file mode 100644 index 000000000..ce9542e55 --- /dev/null +++ b/apps/isa-app/src/assets/images/Icon_NB.svg @@ -0,0 +1,11 @@ + + + + Icon_NB + Created with Sketch. + + + + + + \ No newline at end of file diff --git a/apps/isa-app/src/assets/images/Icon_SW.svg b/apps/isa-app/src/assets/images/Icon_SW.svg new file mode 100644 index 000000000..cfb9822cf --- /dev/null +++ b/apps/isa-app/src/assets/images/Icon_SW.svg @@ -0,0 +1,11 @@ + + + + Icon_SW + Created with Sketch. + + + + + + \ No newline at end of file diff --git a/apps/isa-app/src/assets/images/Icon_TB.svg b/apps/isa-app/src/assets/images/Icon_TB.svg new file mode 100644 index 000000000..751e00d1e --- /dev/null +++ b/apps/isa-app/src/assets/images/Icon_TB.svg @@ -0,0 +1,11 @@ + + + + Book + Created with Sketch. + + + + + + \ No newline at end of file diff --git a/apps/isa-app/src/assets/images/Icon_VI.svg b/apps/isa-app/src/assets/images/Icon_VI.svg new file mode 100644 index 000000000..e34bf1e40 --- /dev/null +++ b/apps/isa-app/src/assets/images/Icon_VI.svg @@ -0,0 +1,11 @@ + + + + Icon_VI + Created with Sketch. + + + + + + \ No newline at end of file diff --git a/apps/isa-app/src/assets/images/Icon_ZS.svg b/apps/isa-app/src/assets/images/Icon_ZS.svg new file mode 100644 index 000000000..79661784c --- /dev/null +++ b/apps/isa-app/src/assets/images/Icon_ZS.svg @@ -0,0 +1,11 @@ + + + + Icon_ZS + Created with Sketch. + + + + + + \ No newline at end of file diff --git a/apps/isa-app/src/assets/images/OF_Icon_AU.svg b/apps/isa-app/src/assets/images/OF_Icon_AU.svg new file mode 100644 index 000000000..fb6192e05 --- /dev/null +++ b/apps/isa-app/src/assets/images/OF_Icon_AU.svg @@ -0,0 +1,11 @@ + + + + Headphone + Created with Sketch. + + + + + + diff --git a/apps/isa-app/src/assets/images/OF_Icon_DL.svg b/apps/isa-app/src/assets/images/OF_Icon_DL.svg new file mode 100644 index 000000000..fb6192e05 --- /dev/null +++ b/apps/isa-app/src/assets/images/OF_Icon_DL.svg @@ -0,0 +1,11 @@ + + + + Headphone + Created with Sketch. + + + + + + diff --git a/apps/isa-app/src/assets/images/OF_Icon_EB.svg b/apps/isa-app/src/assets/images/OF_Icon_EB.svg new file mode 100644 index 000000000..a47b7dfbd --- /dev/null +++ b/apps/isa-app/src/assets/images/OF_Icon_EB.svg @@ -0,0 +1,11 @@ + + + + E-Book + Created with Sketch. + + + + + + diff --git a/apps/isa-app/src/assets/images/OF_Icon_HC.svg b/apps/isa-app/src/assets/images/OF_Icon_HC.svg new file mode 100644 index 000000000..7adc9564b --- /dev/null +++ b/apps/isa-app/src/assets/images/OF_Icon_HC.svg @@ -0,0 +1,11 @@ + + + + Icon_HC + Created with Sketch. + + + + + + diff --git a/apps/isa-app/src/assets/images/OF_Icon_KA.svg b/apps/isa-app/src/assets/images/OF_Icon_KA.svg new file mode 100644 index 000000000..55605d338 --- /dev/null +++ b/apps/isa-app/src/assets/images/OF_Icon_KA.svg @@ -0,0 +1,11 @@ + + + + Calender + Created with Sketch. + + + + + + diff --git a/apps/isa-app/src/assets/images/OF_Icon_MA.svg b/apps/isa-app/src/assets/images/OF_Icon_MA.svg new file mode 100644 index 000000000..757c49e3c --- /dev/null +++ b/apps/isa-app/src/assets/images/OF_Icon_MA.svg @@ -0,0 +1,11 @@ + + + + Icon_MA + Created with Sketch. + + + + + + diff --git a/apps/isa-app/src/assets/images/OF_Icon_NB.svg b/apps/isa-app/src/assets/images/OF_Icon_NB.svg new file mode 100644 index 000000000..fa6d6272d --- /dev/null +++ b/apps/isa-app/src/assets/images/OF_Icon_NB.svg @@ -0,0 +1,11 @@ + + + + Icon_NB + Created with Sketch. + + + + + + diff --git a/apps/isa-app/src/assets/images/OF_Icon_PP.svg b/apps/isa-app/src/assets/images/OF_Icon_PP.svg new file mode 100644 index 000000000..7adc9564b --- /dev/null +++ b/apps/isa-app/src/assets/images/OF_Icon_PP.svg @@ -0,0 +1,11 @@ + + + + Icon_HC + Created with Sketch. + + + + + + diff --git a/apps/isa-app/src/assets/images/OF_Icon_SW.svg b/apps/isa-app/src/assets/images/OF_Icon_SW.svg new file mode 100644 index 000000000..7290c6d66 --- /dev/null +++ b/apps/isa-app/src/assets/images/OF_Icon_SW.svg @@ -0,0 +1,11 @@ + + + + Icon_SW + Created with Sketch. + + + + + + diff --git a/apps/isa-app/src/assets/images/OF_Icon_TB.svg b/apps/isa-app/src/assets/images/OF_Icon_TB.svg new file mode 100644 index 000000000..6545d86b6 --- /dev/null +++ b/apps/isa-app/src/assets/images/OF_Icon_TB.svg @@ -0,0 +1,11 @@ + + + + Book + Created with Sketch. + + + + + + diff --git a/apps/isa-app/src/assets/images/OF_Icon_VI.svg b/apps/isa-app/src/assets/images/OF_Icon_VI.svg new file mode 100644 index 000000000..1586f3157 --- /dev/null +++ b/apps/isa-app/src/assets/images/OF_Icon_VI.svg @@ -0,0 +1,11 @@ + + + + Icon_VI + Created with Sketch. + + + + + + diff --git a/apps/isa-app/src/assets/images/OF_Icon_ZS.svg b/apps/isa-app/src/assets/images/OF_Icon_ZS.svg new file mode 100644 index 000000000..7d4d02d66 --- /dev/null +++ b/apps/isa-app/src/assets/images/OF_Icon_ZS.svg @@ -0,0 +1,11 @@ + + + + Icon_ZS + Created with Sketch. + + + + + + diff --git a/apps/isa-app/src/assets/images/Star_empty.png b/apps/isa-app/src/assets/images/Star_empty.png new file mode 100644 index 0000000000000000000000000000000000000000..cfbdc90fd95aa4c98c84d9c5031081c4a4118e67 GIT binary patch literal 1001 zcmVPx&qe(3P-)6?PLb0)E-$XC7h0MT+5aKBx(Yyd#!Bnv(yn0JT$f%a^$bYI(q zkR9dQtpkx1U4!G}`TE-KXq(W$NU#mrsn!Ft5L@NcQm4MQJK7>N#Oy@C?xdj17l#6Y zq9l0+0$YS4!@<+28&6^!WNcGpj?N^?Gg(t6#>zGj2;@!9iub6YG6UZ^5^Kv_o62Sl z^rqsBGc-9z9VTQ{unk1v^QKWvlo@4cXmZjW`MmG}gtnMq3V=r3uEHIM8p@~4>{98{ zBBZwII{+7T&^ugJ*>$JW}%AK zvodZIf*l4JmSQ5B2TlmVLPF34@IwwkA=0#o!wr?6ppWVxH1P86EhcJ(U@JN&Yx~mV?*VB>{8}0)UWZ>UC$eatAD=8=l{@yLk?JR6Y%3l8)I>-&t8_E~^ zyk763;-INc$alNC_UyVK&U>Hw)&tLr*7Cbc;Xxy9!_i+1daH_XLc)2J6@+KtGl7)cq54~%b~PL{GS zUS@ZWnz_>2^nG+(;86k8_ z_Jq)IJJWfqbZqd=oeM&_%Mc5y8XaYq6?wxg!+GRD3&#E`tbi=_%z59I-&I-J84`Ln z^3H>gcDE@2i7A{d=kQ(AhFdaVY73=|O3Q6izcVSHWs24cY<#K*XUaF6U{_1s#eMbl zn-`P~GJ0SA$wJnB^rwJa6G9 zg?xd?8cBJ4Dw`-p5<5kK+4J8Fv1}~AxxLM_PP`T421oaAcx%N}uUE()2(OPTT9cU= zMH2f&fvNxJhE}o7g{|$kzu*W}PAD6Ca_`nxIM_@K)(RQh^zifZ5vlIv>V%?h!x5oK zA_^dEEG%|(o?-)UaB_a@Lz@mADtPzw`$&-LF4I$BRLTL$fUcJ%WUUgH%zPVNcXR+c z0f3L719Un7=yc}gJ(^uH{7F?+RZl;IKAJl!$?Ar_5Vgudk*@fFF(osO*VZPXFTmZE z6t&zcSzqO?o;0O^Wvjlyb@h!yzI;tym1K2%Uy=8gXIi;y*cmSLvZsb>wjC?^lze|h zQnbmvC9{L4NY_n0zi4)5&+0j4b%N0E^|S7nBx`f+h~VaW-Nbb#1PfYt=2OmNZ_KC% zA8wtNWOX?%u@xLe5|;;;7A(tlGx)0y{kn8<2NJTj#flgzhjJ5V=T9WoW*&8{#^?`l z4RJ-?jS}ch-0OB76nUl&La67;Gwswka|_oHPhpc z*N&JTBr`+D60%m~b(c02ItLv*9bWqZRU_HrzlL>WA=Fbl008erAvE36mZj^$Y!F&8 ih|0lw2BAp$GX4Xf6JRl?Y=XN00000zt>=iIq)W^x#YXCiP+_qqD9)pZML=xfVw655K| zO!QE$WFP>5te^6%!~m|e)~?(i8_Df!Yx8FUB^jt%Pu7?$@S9liYaM5?=MLv>ea%Oe zuN(*fu(qG_Fo}f`P&gFkM(v{omrtLbl?o-vp(=f6o!R2}Dj5jmF}8$UByoY#|!#emTN&^1FfR4G6B-+@j7 zkTpVif~yru1d1|I><^o*xV>IQrwb*@A@rDRFS2(>W!Nb*4cml(&~^9hoc+au9|UEA zN1(1|N-S6$sJyv(y?jmK9vRLRVI1}*CzBQ7ELTKXphApTqC?5?kqCn=hl0zYP{hkH zQD--m$obAitz&*dV4TUko1*UmnY{*n%(M8yb0BFnF~k zh`*t1;xdIn`BH&UuHV1Jxac2q4FqDtfQh>Y*zJS#>_fF!O72niqg2-Nn@3>_ecYQ1cDqSGYp9|hm z%f#NE3w85}_5RiNXhu!=vinRlJf1F8IzV$C?~G}}ymz%XgVoL5VOv#n~++A_fR;=6Y3Gk&qTY5J3)5G&~_BA_mB}=nrWtO0tEt_%}5{{$ipb z>I%Unw1-w$7W?-0!wqPF@``!5$)&}+E=1(PoRf_jzl7OlR@`jm)^(FgX?^yP6U)<@F{LPMc58N~E*q+ literal 0 HcmV?d00001 diff --git a/apps/isa-app/src/assets/images/barcode.png b/apps/isa-app/src/assets/images/barcode.png new file mode 100644 index 0000000000000000000000000000000000000000..b2c94f183e627c56dc6cb84ffa73012f82bf95c5 GIT binary patch literal 653 zcmeAS@N?(olHy`uVBq!ia0vp^%|L9#!3HGjxihu_DaPU;cPEB*=VV?oFfh4$x;TbZ zFut9%H!I6g#O-U?vdJ7yT(e8OxCH+=N(ZmjS11z;D+>$z#guvFkXw+Zht$T&4qVcP zj;f8+ozpOsi`%Q$~^u0_3HJaT&(fdbN#;k__4zH z+2@_>%Xi0So6Y|A?c1~SrT?WnU38>+Z{=;jTzNGsv^sk2w7vWH&$h1Ke?MAj;)cTy z7u>7b`>y)-+iiFA%;V$rri<~lzkT@dVBhh@Ri~eB+G)MRaQ0ap37%hjKUUoNt~%K> zT1TvIZ`kUeTm6WOl#_&J8xfptumYG^XJaq+i9COhG;#FeEzxeD{s4V{kIC6Z>Ha6@BH}pZ(D>; z+QNVnc6N3(XKmS<8^77v+yAcq^Sf+k%(?Y{e^xWHvDh%CIIsvhuq{+jVCL%pMiMKd zWPtXXkbDnJUkskEelF{r5}E+Jehq8@ literal 0 HcmV?d00001 diff --git a/apps/isa-app/src/assets/images/bookmark_benachrichtigung_archiv.svg b/apps/isa-app/src/assets/images/bookmark_benachrichtigung_archiv.svg new file mode 100644 index 000000000..981b57a21 --- /dev/null +++ b/apps/isa-app/src/assets/images/bookmark_benachrichtigung_archiv.svg @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/isa-app/src/assets/images/bookmark_branch.svg b/apps/isa-app/src/assets/images/bookmark_branch.svg new file mode 100644 index 000000000..26716700c --- /dev/null +++ b/apps/isa-app/src/assets/images/bookmark_branch.svg @@ -0,0 +1,12 @@ + + + Group 3 Copy 2 + + + + + + + + + \ No newline at end of file diff --git a/apps/isa-app/src/assets/images/bookmark_customer.svg b/apps/isa-app/src/assets/images/bookmark_customer.svg new file mode 100644 index 000000000..56a98958d --- /dev/null +++ b/apps/isa-app/src/assets/images/bookmark_customer.svg @@ -0,0 +1,18 @@ + + + Group 3 Copy 2 + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/apps/isa-app/src/assets/images/bookmark_subscription.svg b/apps/isa-app/src/assets/images/bookmark_subscription.svg new file mode 100644 index 000000000..a92019835 --- /dev/null +++ b/apps/isa-app/src/assets/images/bookmark_subscription.svg @@ -0,0 +1,21 @@ + + + + Bookmark / Subscription + Created with Sketch. + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/apps/isa-app/src/assets/images/checkbox.svg b/apps/isa-app/src/assets/images/checkbox.svg new file mode 100644 index 000000000..b8f857bb4 --- /dev/null +++ b/apps/isa-app/src/assets/images/checkbox.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/apps/isa-app/src/assets/images/checkbox_checked.svg b/apps/isa-app/src/assets/images/checkbox_checked.svg new file mode 100644 index 000000000..59fd7e6bf --- /dev/null +++ b/apps/isa-app/src/assets/images/checkbox_checked.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/isa-app/src/assets/images/email_bookmark.svg b/apps/isa-app/src/assets/images/email_bookmark.svg new file mode 100644 index 000000000..2d72d59c5 --- /dev/null +++ b/apps/isa-app/src/assets/images/email_bookmark.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/apps/isa-app/src/assets/images/hugendubel-logo.png b/apps/isa-app/src/assets/images/hugendubel-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..a112c9ab6bffcc4eac0b18ce079eff4cbf37b0fc GIT binary patch literal 22039 zcmd43gL5X)*DV}Av28rDZQFJ-!Nj(0Ol;e>Z6^~>GO=y*&hNe7d+&SygRfRq@2;-u zefHUXy3aXluj)u81xW;0Tv!kg5Cmx{F%=LHFw%eL9?*b)$2ozF#D50VSw&I=q;?kn z6a+*FL|RN(%>(o@2Py!6q~Y27xe)>kB?UYYniSS3Q7(Rh8#kEfw;?hIs2wM`iBHuqKX#~B+%cXd}+ zcUMruca=tt(ct80J;vPE^2cL2S^rw6RjSu6+w`{9^CdP93o|E_u z^C_rGcpADZ?KMg?RJQKX%O!V`&W(oT{}c1_sP$n|oG@F7E&rnYZ^Q@B1f>N04+wEe z_v4aol6lCk$n^hTc!}BI{r5fpu_qERT5(dGu-l2P|Hs$K`F~n4um^4Z&;a{yY*K+* zN4ON-&|CiN#{WiOFT=lU5099JQ}+KS#QB*23h)0n4g=4689;Qu_+YB#r1>?^vqFkVHRO*Lvo=XnfSArqV5pNOst)@kz#2exhrnHYGco*5MNnQWRc;>z8_Hk+&mZ^remf~c@2gcxi=8O< zJ&6*9z~r$pc<8#@567!KAe{{Vzh-V0uFzi8BvIW*8SD<%FLm#E4 z6->q5?C}-wu6z8EdeAzAJfvI**3(sF=}sFdyGtc(v)xW+-W~x!P}5x$9i`6umi$mR z7&%#SHz{>0qttT&iPYt=8C{=RrN;A;E3>{TeGhFp(gbL#1;IIM2~l|r^-!@^(Y5*= z2QB35Mju2(Sj?D;+F7Z;8n8n4wvp@5f$@xoY=Z9I{Dhc8y5T&${=#)yK6+=&-9 z)Q@P{(*XHQBU<6LFFJXTdgz(RSBF-(gqMGAi_AKOqQ#=bbhOTZIcE5hjT51n@;ja1 z>4gBSHV)d^C#0j;7l-fD*#Yt!hqDi??B;EMC_N__%@xp}CwxyY!b_rs7s1WCm^T#8 zhtQE9IHK5*tN|`-H9tLa^!;p-Jbt;8jaBHg=JcEHqYnn}-F$?7tO4}kzMQsH?cgZ+ znZv;kt+Cr6;Ob{kH_Bny=guRFhy#D|8~c$&5PZ5%LT*!KBw%yI%r*!Vv;t%!%-0a9(=!`Kt?NRZx{)L?o#hh0G z-&BvI7!HIQ_G&|mc%TP7SnDi$wAh1G0lD@O@I@+E5an9bRx@Trf$bf9AV_v$>eL?= zW-jm#0?=ZMx|aOl`ZK*vBoT?;7!XDX3D+vE-@p*&dRXcMajMGm9cMK+Bk+wp{CvbM zhk_>94?WmganKtKn&abnp21LkbY-+%byu~B-fi>BG+HBtrdz~_732)mfMbsIwbI~% zd3SHX4oReq-?a$06cI+Po~W4ZjowQpMg-@xTRliN%Li1~&5zh}g592MuYKJ~U=InB zE%HfASOlP?7x5Al0+~#agr%hO$l3A5afara$EDdH*>2yjo0K#pr~e)Mr8g~fkF?9%z}V1DQJ|5Kk_6vf4Cr%? zlyXzYi3tv;dEm39LuG6s#PTnPDuS>aU$0w?V&BTyprsZpBwNAd4^xS zE&*j!rzECyHL0^#kl$OT#k-+8hgl+$f-w+wQ)2qBgTvOA*LCo}{XwKfvBf~|?GSu% ze(*v}8|btazba08@JF_k(ZE+D=Ju&b-sySXSF(0qZs`)1ehOH3Ouk0dlfODX&+TRjM^qk>f*hnWN5?**&`$nD$5sS zED?W#XmP;ARB#0?=ewd!jG`2-XKO~E4a_g~H+eRMn0n^2m5F{UIHWl{%$WMJT6`sq zCv+f9-tk&0h&blrfwirVTw;?*hW77Lo;#d;=D>@>%=odaHXWgWDQ_U7CL;Guql9m5rLT)mlNwa3A667b@ zhQNLIW#TD^%Sahv3?;Gd+i-P*Z)V%6UJE^R29~tA-74Fa6n+A%h0on;hc>aPb@djq zk*+?$c2-C2Zo3o%x{Vr7E~#dCigLxncPrWsQl9StuuU>)m{gXrL)a*rXYnpA${$2K z^Nn)ug9FHE%&RR<7qh5c^N6|`>^n<_Xe;@SM)Zs_5^)x;-47{g!x9HlhPK77td5x& zZtNCw5ogsoO0`dpRBXk`4?L#i{aeWoUUGHDhNXG{EqvTnlC_PPfgj3#wyEy~%?1H~ z22o)~cb@wun(QM_i{vp`p#mMmo2BSE&hE>k3V2h2Eq{-&*O$y6>bD`PVdQbYh&*XU z?T1hBnC~Sgo$>iB3x6LIXOp0^hLbj2LjPdjk4I72ts_xa!i&nPI&D_&r+364!!;#5n0+kc<^3@pV<7uXgB9~5EjgUj8yFgz}Ei4V>93!&O7K1fz zn-67w^MjkcPO8GBcWIVVUvK<{-XqByjNh6+sP=xHGvz7a6hJi6JN$W}+&6?nN?p{C z3rNvEgklE4SI(#oJ+eYbp0G89>B474z|RY?7FcyVAb&ow2-17gQGf*A@Eq$Q;$liV^oX!dVV6jCq_Q z;GEvALj^Bo+23&p;K2*e&0wq`pLUR6Z)RH??}(!_N0BFV?7W5`enV;!+!dDuw!HAC zzB`5lhSUOVGdJjnmFY81Ts1vab1CVL1n>@wwBsDOSomVkZ!W^gw^kFLac{2V-t1+k z7n2fW!nG&955eTDrH@ceke4MSCLDQ;0F6Ju^l?niQ6-SWQ0UZBAuyGACz1_xg@Fe4U3x-Ph>p>F#cPEgBCF^Ns7Q(5618(E8xNz{d;Z8G_H6Ob zIu;iogs1BQ5Jre>&VnHnkxaC#2YJ<%;4?>1JI|^*QZ8A)_GYC+zD&)guL$WM%RncS zWer$kieN$kKXHKTub)N`4=fMt?HHMl*AO)rpr`CX@5CY%OxTB>KZJBt^!jK-PKx{6 zd*1&*-Bdqb6QMnkUP%2}d?n!Y58HZ>vcSD(b-)a|ELg+`t-&HGY_zy>vc_U@aVwc_ z6SCI+=lfC#d_W?^W`3el5iCr#8GY+&Y3 ze1YKaUGmH%S*!qi`~qw_5i_HQ(ZCUC6Mmv;U+kXSJni;Id+*CYv-P(QKBb~3yNHZf zJn7E6Y>OJ5fLua^qT1Rrx?P1F`5f9kc^bPfR3ZHgj{GWx zWj=5)yU}p0A35dkI#QiDgw-T_eLaG5BQ|^L+zhu_N}4=dg1~!2O#*5I?B-~+;Q(~O z>tG#y{O@?4_BwWj<&kPWObgm@^5O3y917t1%6=!N_zptN_v4Wfdb5 zPX+Qi0HP<@B;+>u?E%Pm*}yW%!8@0be$RJdn*alRyk_uM0_ffARN;)fG#}9={MU$J zEqm}?w#ZwObd_nPVLV$AtO-8CmoFS2HxP8U%w1k`ADrj|bX)bT^sSR%sFE!Z$B*}z&QodP6#{nOL zlL1GqVHa4N;wI^PoT@OVoa8eM-w zerFeYs8IJdWZ9fL0#q?t`ZPW22c=Z|qb3Hp4t5)IM-G=Faz|{8p{P>|>g_7H&r)zg`J5 zNI@m>y&do-&XNqih3J}1Ug_VIvg!0&s%$CVV3#CL_YoG(_eWRSQKP-$QRlx(Y3PN4 zmr6~_aED3#DkdM!^H|<7UFeAanvfhc^a7sR0IrTjq^3CRZW*LCB__%{jSlN>I2Um2 zU&M-fl=Vkb6xCAlHgHK(3?%)ymZo*V^9($~;Lqrk1I;+r+c7aLl2&p-<1JgiodSIH zwVMa-wd=xjZ<&uTDFm|^_?2sXke&5`A6AFg`J9pcj|@fgH8m5^+Wg486`GZ4P?P7m zrrxHiS2$h`8lm{vJdPaU$Lubyt zD8t!`q~kl+kA?^dTWTY>0kz#^b*>T!)Y(QGEo=BOyWy!^AHtIs7EfsvQfDEaO;WIJ zV_pa@zHLyT`x-llNM{q{zcb*CL!&oaWx;LGG%!~?68_19G)|Oz7q+Iu7OEH zi5wofCVyBW5@#iMSo%Kn@T9W;j*X&lZXuyk+D@-6qLc_%k1P^TDJ*y!``&MZv$j(w z5^BLuv7nZVoZ2A^p*Bt2`vmxmG>#9EZYcM+5^FDW7KV6ExaaQS2v9~E>`S~;w=%?b z9-zk*XKOf}4Z`#t-L{1$wF{(NMI7=+LNZ~8iq&c2vN9qWyhKO}p-gWQ<+|CA5`%Nd z)Co&y%sH}h-VYtKuOj~@g!`r>qa=MSG>f`{WCIUFV$}Bo%%2C9<1 z4^9HR*U1nwZ%(GlTaR1<@$*Q0UVItQ#GPbL7qP0$<&V4jD__737htu>XdiCVq01uRqlAD#p(T z$J-Xs*CA{|PJuu@+ALQ(+?CEWFK_C(-}@y#oDDqSRT%+W?s!Vs03;gP7rH9Bcj->i zHyY}lNqty6NC=B;zB6T$B)atals6G{!mbo1NEsnq(gg8G!~z0{r?3_$wMHvYc2?-T z9YFZ3{$@drYu3`y4AF#;qaa6sNkZ5h6vQ)Ev%G~unxtQGCdECt4OJo;rKC4!>r;4I z1`9o{v0H_&8}DLSjprk*rhqNrEd=_dxzdDwC%!;;v7vGJOUs#&s{kXt+_FM@CAVu~ zo)hB?UPkQ!M5jescL|P%+u*yw6AUtyEH4IiZsrPC`!WzIic^Mu!tZL6Wfo`>vf7x1 zNdeu_!q1M$gCp>$klGB%l3c75r@fpf2=Yv(5f4`5YC3fcCPdVF@t{J5)W`XlBRhpo zkQt_$IxSW3!sk?Nbi&Cl46#lhsU)akW#dDOYKUPATv(|n?gnD?IVj@WPl-A3i^T7W zOJ7!tQY}8)cz_$KM+qIa6nB_HHkvy0RG>8Ri9n81Yv2=frGzXU&LJvjr=94(R|Dg) zHHh8B}`yqTT4Y20sfHIrZ zRsd3p7SYxTAL3)gRn}2a;OKuyr9oD(PLKofes~YPl){*CyRw~h5qXJQLR`^@k_ZLwRiV<4Z+vMhkW=c0)u5ITeCZ<%G;QR3`%|!&0`r& zE@31E6JH7gyo!+Yll;7T`M}3Io~4CC>HU^!tiV4D{Q(fW2K%rB00A(Ejl} zi-`(mA{03Z9oMluQHf^%{I4KKHnxQHV}$#3$oua4@#DG8U2Kl8lrCt z_~{NeKrYkT@=u%6vV!)?UwkrR_%Y@z7|ofY*7CRbl;#<%1?x#7UB7~P9Fyk*{LPx6 zktf@AWH+uEG zy0)}BhW;puuQe;n65~uLPX`<6OD`!D*@}WF>jv@tNWFS#(b3@~D+6QtM zz@(O$Ht%7SQoB#KCqunqCa$a0WNtn3nD#G&Opi;La7@3Hw*row;T+~AAvNQ zYNo%8wIJ`Y#dIq#g*mBok`X!NCtG16p7#}AgH93TbE~&b$j0x!BPyMf^A_)vpm}5y zmi35GWd8<9Ha$}b$)X4gSvLwICjMquOyQ1xq1xQ73sSV7dkOXsr?Td9v zK7-0iEqK`mH&^pjeS*P!e1&=iH9T`0!)WP7s;EY2zkTrj6QF(Oi(^6$rA)WJ+CaX$ zO%mG1{#ul+?gJ@+0~B_s6<&R36^+n4&^bo}Q#a{MWulNe0`tTi{5y5SQ7DAlCh2rL zxy-wHY1H2yc)**qW1dPM{B0%@PfpSJNHxDOy&BTu>rB8ZsXVrt#ON+`8$*+0GkUf zBPg0vkSTH)2ovMSf+0=I_@)g(u%=HVWxpYI(tV(2bw!xMFp7h~T*)LQg~0Pedt=~+ zn-#f^CeIPDX5*&`wHh7pLO3IVzN5eLtqN*cb~cs1`QwBT3)Xy^4`EiA!Q+>><>I+I ziDi`@qo`DvJ_9}ubX(*G?|l)7&$#U2kuWbcNG+LDFR3OMiC-NUQXbTfF@S9|ItII) z5HWzUDEJFzi^{BsrCJbrApFdxh_7SRLzOI|HhJsr8`g{A6%KqsVS>Z1c3OP=8eDWY zI)ihdK$wFrIwVdz(35%AsU8nW5^~UTdd+TJWS9UiAkK6iFGaiw7vCPDpA`#V`3iLU zCw1oE-d?sMPWgvUBjkBG!#-l1MbFv8w;trzo<@Ow@DA$WK@;3JV-q)4r)~#3vD_}^ z#{+6AEI!bm-*zkM+!;RSV#7<7$@k{Hj+Qqp*xkP1Y$yEah2@z&xtOGVjiU>rnffGg zm_I?&1WZGQa}MY7Kk$VrzGy9uSdRWCI(<@*eSIa%Zxs%J-j>^2f*k_lkWaUPnG1Nz z2wUsjjP?c#Vku8O&j%*!0fEra)IO{+WmeqY&& zNd5&|Ky0|@llx#V1j2zU#BD9dqL5Y^vGJkT&%-aicqN5&xEy~7Vx}OzW2o`tRPiRV zzIO}M_JeB&(G}}`)FXk(Qf)1!f28v>TCl0x`bi1)2<1N}kWROLH7S(c(rhfy^8X5m*r^smR$Shb$C6?X)v_hlT#$~#-6E0 zqv{dCyOkpELuw^yUq1T|{cu@=qkgx^wiK<)HLkw$v>T!l-5EI#d$vAvJoDGOfx|@r zPP^8|>%RYyM_5)`71|yJJuQc#(z~t%XL%Vg|G@#W>1H+7M=6TVE5xARlY7>dO%t~Y zvVfOnrh@t_3lD6ZMz_^=X^lvzl%Km~P|Mzs!NlRLGstOzX(jCW zCsT5>W+J6j*HmdnC*$0L97m47N{^*W{C-lxJ&zeHUG}T^pXLu_Fl20v=1_ zU_bLbf{H(*Fq?a1=PJXDT6ls3;{=`?aGnWweLG=98eX}vdz)nXUm2tl+8T``U-)|J zzNf<=RLL$Ksm6Kt+@A#9BsFm=Z!&*=^9H7*Be>Dh>=er7X;9tdMDc~cU`^3%As6}w zLR3do0d^*ti@zi(^;9n;j5CmhH&euV6z^X#Z}W8LPtJLs&<(K>ZUH}(HbLJQ8Y0PM zT!%ype441)p`z`mJhDuNNA+~FtE!?WL(nRbllr$$#tnv%Hc!k}#BUoPtOb0T%={?~ zOP3#B$^;h>b8W)v`VG&}I&Ujqe1R^?fVLGeq%`To7@6cLt~x zJ^Z9r-Hu#YKMAkkVpIU*F!M0`%--En z+Fo^%Awl46A?0NkJc~ViMs@S0-wN%K#2a@ER`J@x4sOP-|t z1$aj7JYJm0m^_qDoYbw?RRi?_*n2b+Lm{d~X|t7B`(Xa>t&pY@54^zQ^m7(?g1T;% z+O~YVgPw?mdk(nlyQdA%E#j?Qf`qBq1D~WD(r5c%NmiAa4K?@7f|%nBeff<*Vn!Kc zE!aG)_y(jItGi(rW@eQwWe;jL+D$-)U6N#pHL#iNmCgqrg+#lhA^(z2go9MPB+$i# zM=Z*Nyb)W!t$5L2Qv#zsL-bl8@5>L)GUz1cI#F{@lHpRi zA>MG&D)I643UY?wFR96{w!Py?h{r>}F_zUa~S}n1@SZU=QKV_pv)%IpAd7T4lpW{lru2DW?8z%hq|@oA>=qw&6Dp4U^$AMy893$5k-^-U=X;pebzZ!^AnI;wF zRlX&IDu=73ID;ijDS~lZ%`L0y1z!n|kKi1*vzMe2dXh^WA$zXRGnd_SOyrO_P*qXn zLOIr;_uNW!`cK1X*AixJ;*zeZm(AoH%c@NIo-)`!Cu>!pyxLuQZpg;z}@l*faQf4sb&x2y&FnI4i$Z0yHs+3D!B-a)#o=nKh& zLQr@OGpXOmCtk?WLL*NF8{Cq$$7--K>ZGtf2##6@@-Q?^qB&?tTo_RY$JFlS1ij{y zJp!_3@wv0c6S#LCB`6N?Zt_$v%!%>GW?F61>-+*9{$Y?T)f_o7(t@4rp|scO7}-~z z3ag87CpKI2L6vax#`~N=U~Ke9pM)O6X-rY4y#4UBziKV`Zo%9+!uyJx-&87|Hu$+G zo06v#?p;zygf!1sGTe1EvR2{b8@I%;y|5dG5&s}&$Zd#nvafAz6|zZnD>0g<;&79h zkP06W>NEYSNIs|#>=cqkz$AZjW%7sDkJs-62iRTwa>Ch`+T>`G1&h%d9LMCWZISv# zU?YdBqSLvc7{QNm*RqI?6+&3gBL+&B5ijq=yKJPIIO@2QSYk04dzIWpp%I;G9WlscZX1^y8T0O0)NGv;)wum#g5dK*BS}8Z>ujnVd3j>K(PeUW=PnVVa z@F{G`+d8pjNKT$E5LhpI=>qHAb?v&_yI3@|i%V*Hpb{><3x;u#EFybMjXRs+# z-_m~TK~HKSfM-IVKHjB4^L*5%Z~Wr-2lyoEVdKKs^<5frdzd7@2O2eZoadKZlVa?* z{JGCS?hRn-PYF!-9OjH!N_g!!L41m#JdzXJa2ed)@uCOm)zdyvYU?s;=ADgR3*+fV zd-MA%YlpMp*C&!u%o*f$;*!6{3wl61H>VR41<7723XN;?5p?IH;IU{wihO#Q^Ct({ z1O&Nn4)qB6WAI4ogjj8=)+Bi~-ya70pfbg=V95UJ@C#*7CCW#9=*sUO$5c>Bh(-bxNnm%Z^=VYZTas}R`s-tHfXJhCvTbt6Yc z23-<#_GvuGTVh6)s4yC^p4)9;)l?FcdPfL+(O$S8q$gwp!3Tj#>ex=s7f^hcqJ6)sS+@NauzzR~XkV_2RXMnAs{S;o-Gv3Z#pcZV4Vrfv2!=Ju_(*%pW6=)s| zQ?in=K2sL5LB3Ql?3<=!g}0PWa$Rn+4_GkbRsJF>K6gsiB~9bH-2Y7y99Q!G=bgc_ zlZ8gtewWO=C6T$2oWwqH_MDU3{_1{=~B_7}l)71xCs!mcmAE zo|q-Oo+3vwEbOrFFX$~@{ZGc@P@McV=x?ICXqf_6O2WOgV;voPXwqf`Xf`sJ1E3* z0-(As@;#_36}n`DoupC?8_byI*TV=^P;7)jN|nxEmM=fim#7;Y;b41UL0bUenjQuX z`po{!|IE75czNH(gqXkLi+t$KSOl+NfmqkRT9Lc>*;LPLy?7ZfMZ-9ih&HepV$bDI z8jKx-%yqkAo-`8BWd_|;$|{v)L`Am#KQbvL0hsc+N0)eIn@0(;mU5il&@J-7A*$IY z2*Fxj>aZ-B%nUI|9fR#@N|u?#&UNyvfKszk%xZ{gh)xNxGiQE+b?Sg+Je1`^b}>>^ zlac($ri=W!viH#9CVz4sQLip<} zER#g4dy#OVVz?rWw>15KYux^01EW&<5wS@X-3pi&05#?bqa+Z%4GaHMZYM;P762dg zW39juN52f znf^`^!Vb+Iq9Sx0byDB7hE0^`u(4LP(I+uaV!;FQU&1~iELlTBqI$hCE`z13pOdM2 znzDwO6$W+d&2=1qn|u3Gm}~4F%(K>S@c|fkmO26s-dmfF|EBIM+;X}%*ZQL1Nf)a# z;=#-p7L6gH)43i*303ce-3I(%(eSL(_@=efsw-DMpW>THOYHSIGQZMQYnNhcz#c{F z0j=*%qQ%iZ51ixtv`pr~lDgbb^YZi_WQNPrI!Rzg=@Uro#wP>wgjv$d&veV4g8hn~ za|G0M^Rv;r$8kZXUs`#yFH(bS--*Du7U2z%fBguv#17N%?4Bg6)Rni<$8a5>v-eE4 z@i9G9sNih)wJz_Ze)ieGv-m+ob(2D=);AF16kqX9F!}EBU>eQF>GLylE+czkU%63* zg@LX>=cU2`;V?!)u@-R(91MIwtB7s##`dDNd;! z<|>p~#v9P`rYc#cjImghW0g@<0e1Yvle)B5>8e*nVwv*M>wC||U#TzM?o9E7_OPzm z$u5(=BheNO1h z_UtCr7d(1o!LL0;TM3^lOpo>)xXw>hi)^7|n9-KMm$VweNuTvg=GXf+$pg**{2Jjo z*QyDh6jb!jk0aweSTM~OCO5N^{N**>OiyHzS?Y#!3kwtzRMoUD6nGf@c|REOYCW)s z$~n>LMn(^rk*Q90*rN}l&bJESZ-F|M7J=U^w-0s2+v&0Fe-scKv}B#oKvy38CnMD$ zNUbM=DMvFhp)S_s-(p(KH!IA(<~SX`X!^8`fmwp zc3fqNIZI4eCYKlH^u+1_JS%lR&U`D=Odrl~ukf|8ZF2Ez?T5l?x&UAFc@a|WLUKjb zHPRu(U7gudS{KiPXOm~h;Nnnh4U=+9S$iiW4%Et$wrD(t37%Hb$7a%$<7`1PZvViL z^@iiH7_pYSvs#heHpjeeG(Qr!5sFf_(}U|#1yebD5&7EsQ8FZ}T_Y@E8y%<^J0XCO zDBfZ8WAVWx3gBmi^(UQN`)m97u3Wc}D>aLwbAk4piN0t!C`G6)so!Doj!@@@NyoDY zmm2_MU3RHC=UCfQ+ujRHW?W2*Ud&i(v!_o23Xl9mU!3~g-o{~HSH5fkC;aZfM@r3) z?i~G3aUe!({HrD!^@i62k#i<*V0Lak?2@I+bH+A{L-qTUrBUm){ByE?56%RqnhItI zQrZmu2LQ{wRaY6_jhQtuG;|hK2x_-j0rbuh9;Hi?42a@=nSzS_Gn43`WtkE;dQCN$ z$vTnJp(h7%ryU9o!;9x#wo-o6L@$!Je$m_a4xTuk{}o~NSqsVEODTZeqi1ET@i*ZO z3ux>Ls8O_hJ6s5>**X!CjgA5Bu;w3gYVMGcyU?6Y&NqOVCNQLdf{p3CtD@00VB)N)p91!PBFQv=w=1&v$FmBb!`)u7`qEe#qgVO~7@KLX5Q`m4Q?f+K67isO&nO zW|AH}^qwuzpo9`yzxpM%jIb@A&|NX}31(>^lN#cz=+=FW@##fsv2qB*H}EO5CF7{4 zpE)ub2u;U*8&)0JhsJq{gv4B9pAr@C#2X(k6{ub6Hk4kmPUNFYa?b9t6_qV`{B>%! zerK^RO%vYNm&o4k20mAcj4O17vGWAR~ zPmZ4unKSZo$JZgM>wwSXO9w5haFtblQN=Ozdw~HzE<$BMH;+>TPIgM51{!9X?UUl< zhffdq9PjbHCzNlp!KhjK`p}6UOpmpq1;Zh~m&GN^*ivuly|fGAP+@icHcuZ!uFlDa z`)pZo>R^(-ZOE5;_hO&gYajVZG$`aI9OGQ3v@;sNmmxLRXYl}W*PFG)$p+|Eo|tw4 zR3SJ)dRX@sMvP6yseYhS!&LhfuG^xe5(7;6`)2(8-#zYNRv_!A2H%_@$nzCB3E6KK z1w_EL>z*VR%VR#ziv6u3_g0qO*%Lz;8n7a@^$ypEEdEplfrTZz|C4fZJ#l~$H^bS% z;#cAbT60dq$j!U2z$P0$#3sZ?o9ZZ|AOoqj$#0=f+VLUk0D{Kx<_S68W0Tpxm|5zJ z!9M1-RL|OpI38k6IoqA?v(-E6sgasLNEWwUX9+&iW)JhTSBKu67`GCf(ZUv7M-Jj= zAj4)FnkyJL)*DWQ>>)ksQZE|s54VgrY~|L{DmL^4-=?dXLDsSZ^ZD#SKXN!xQTm zrM{f=o?Zk>Y||l$?)SK4?=sn%TP6dnz3KK1Ul}jtq2_EKOE+$VC{uDjhp|R4SWEk> z^<-!*&+M+ptX=NJty%}vcAOokES|Vfzc|1==@~-MQnNY-n&j?{M7_o zcZhyRSdO}&eMe*@Me!z&H-v=I7%S)+opFeOej0itpgAblz9)P!90dKr7k2n*azWt0 zAYxXfOqm6&QkQ1UeScT7gW3o6mb2^;YH~Mg{;uXUv+jW58WM6H;4(c@Y7?X6Jc@I$ z1M7P>GRfwcX4^aowKuMG1e7f;X!MHuK8^iivSU|ur%qiVp?P8X6SgYx^2?cE#+r=h zTmo|4wL*G}8AJh2j}>t zO*N0H_I)~esVUb7Vf>tZBc^@S-I(QAET@>-qR$D2H`D_xf=eyh=Bc9F`PG`5gkCaC5QCghkC( zOJ-&Q?s_bHz8+CE;ehR&+vzPB!Qd2w==t7a)uSMP_FAtLyws$>Hz%O6!GgvnflqWl zJw*Q%gXqL5v7-v^Mc1DV22@+vFgOu5&Vqvrx>_xq6mfpG8D04ByBr>^sqJm_?WnI2 z%xA@SN-xTYulN1iaM6I`dKqH>O!blfLQ;~!xyg1sX%^v`aCM3PI{VX(pC>GS9WuCP zmu|V0p)i^#dg-nszo({`|4V5@?pI=xmHZ|%&Vk)3_MEZ^RDr9~pNUYaw!G%7qnhx& zFVmZiUqu;feW<*c0b#2rJCN4`$mtu%Gfj&fyzlUr)CzX$u|-uWiD`UXuGeT>aW`Gg&j{ z-6MjCtIqSorbf#2Fir96MH)-_@Ve|^ZWtKcr@jL zw`7YKwbiuBem=b6Wo7yOt1jmTI|)Dljdz{U-r1eDoFXl^G}sbLy>tN?U1}BpQu#8< zmW)ndPdy+ejSroNp@OKx-*wP5IFg|7DeM9hc&y5tTG4jIzsz90gZGLrozZJ1gxR-k z&EYzBytn%*m~iO#kjCHdNO0)B^><{?BdVUaaU<tUWyD8NBBzJh9oF?yWtFm-AUZj~RRC+HL*MvH$VH@_0fLAstN%;W zY^FTWb)5)^BPU>siUClCqH8KUmC>in|9O6nPE&)v>x)%nnXUL z-em~Bz&jpN=~I%(wSUW}P+8(8w%|0TTRJSJ#-4uKZ_Uyp-uAd-l;&(w&M!NC%Z$^{@E5t& zA>GBC1~S1%_}*}cTt)G$_<*n4*9sNpVqSGyp@-XCpOJpnS=t(t5+12#+fP~5oZA6@ zc3HYU^(>M&ReN}3xSMX9>r^jTeTx^5d~o;8q;Ch8c3rD)$M!L#UVoK4p0+XQ_6`@b@SU7xios!XJCHY3T@n}L1%Gy zgHi-}^x9Qh2~(o_r@cp68p+PFlNndu$DSoA)jV$&n_bl4oPw@qmJ*MFdfBbhZAN|x zc`O*BU9XJ4Q)`3M>~B~QA7yBJK#1bpPb%Mz&9c;2++_2toFCiF9*ABAGE6moOwrfu z?YM#uI^ELgCnNbWJj)<$e;0gZGnk*#sj~j2t=T&q350@Tlof2Fnst^a4FAVi(1?Rk zpL26@YTY@9f|Ez3${fsh0fIk$?R3A+{z_g}deUi?GDLNa3*PHpxeZRLfc^+I8jM z{h4`B+B7!ReG|)6Q@6j|p7>7_(a6R3k-bwH%;oC{+*Q^w(^n1Sg$ku9Wrg3Lnp?Wu zbPB#PB2m9}=Pi@^d;{ZTzY|R9R4*iB9WlUIXN!coKsBo~py6*);3!(TJP6Zt+;JVw zuq?81>q(R^Wb7!(i%ysNrW7HanbN9mNPM1d-<^)FdTK>!Oggnr`Lr}R9|-3YotZAq zJ4q%!mlRxdL2BCXs6a&fo6WBw+DPc$35k?jp(f6BkMTBM(04i~$!!c2gHi;Og=jGE zj%d!R{F`;lHl9iOxq{uybHIf3XUU6i3)yzTw1eSEr0O3v$*9cpGXci|?e0Y`wCL83 z6e&b=U9Q_i^4lYo!`+pF;Tf0nwyPXYAr6zwm1^T;gDqn_SblSsZ#(5-Cx+ex{B$DN z8Ig6WSiYsBw8>)bTH4Gbd}R#JTjZ$Yulgy(nZHNQR*`|hnOOUWwuR1Bsm=9b8N2l! zs`Fc|tO{k+p=e^+R;@@7O;8uAgUVk)4KNIfK)GY+Q4JM+Jp^3XPmT2DlB~OcqHzBh z-pn@YMBSy#Bf(?x@u>2F(20Onwh}HmaQ?~Vc1sp%gjQS+Q@y2?EC_v5cWl$t8T04w zIqhHkXx)7Z>1U=_)WKt=uL!ZkIZ1a*HyQ)NxJr=l(400G(bg5)cK*6@|JaLR`^DfJ z_v4H=WxG@4G~@hYlT@u4etDYKO|q+D9X`C(+cj*2T7XbYMULE|O+G$$FcBhq@D)Rd zb33!wuTw(mpSUv7=YF9J=NZ^$$nRmGFHa(t+O2QU#?H!}{B<4kugI1K_`d~zWwHO9 zcV8zn75$_B$MK3O{_STxUlNBWCbi!L9+859ldQbA!&}OvgkVu+QMj7FOq9O~VG%;> z6ryWrf*H5H>#(9Zx7g=tVn|a3cRe>3q0xG%2kzk z1Pe5A1QaOd%b?+mtHh|uxAz#!OqeYRNo_;${&4fIu@`}$C;X)P3FkS(#XDil%5L2j z^S251L3%*^-{%A(=LjES{r70}Y>t`htdmu>KHl=VqSY-Gg9PTFTh3{IQ<_w_1{J_f ztbE#}CRLoT8MzWOs?r!V?13wwl?~CWy=d%iz9Hd!s#_`!8j#eo_5v*fMO#Nz3y2Z- ztnhriwbsjuxv84Q&i!-$VumOKsSu z^)GixGy9aR03mCP&JiQ@yl+(FCz!q^UBk6dzJ|;yZBjB5>q8qLtMpXg5`E4lOG2e= zH4bM?4&nT%Hn&-X3zi?;URi61ZU=#=OdgTk5rJ_-zh9*vW@siV!!E;<{&hCiaNuZm-;&y;aW?#$G6kjBUQj5HNj;kSOh9^H*72FK<)O5PmryS&6A z$AeBgN6+jSm3INK=ba!o&-xGe1BGIe$HmuWE)7Q2TmEl0N1BJJ(1qWvI_8;w@F$xy z-l%-xbb&+yKe-bF{Ttya7s%%+-4Dtp<0@E|;}!+546CPTo0+Fb&xh8$U#Y3l6K+Uk z8zWUK%Gyg#@v(+>WQ9qFl&Isyc`=pDR9jR@3QY%O@+GB&a)4Cx+D)6=Q>huDx!_TF zF*q)M9M6}-ou`)FmJX~rFQyv>NK8UCwSioauM!b^r};Bfv47c&m2?2rpH_13Q}7U2 z=jwZNd}b7rcB_hyx92t~Un-<}eb_b{7G=Ev(mA1p%IRuF9n6e;`1`=asTw@Uu9= z-Sp`4QgK$sZ;e3ABTiwBzEET2rGh0RP8_AZ)6Z!b_u2esa=A-OG0-n~S!quBmX^`Ewsj z$RjQlCh=&ie{TF+x5UK@btXXVVU(QvxbfJ+PkT==Ps0_x1^GgL%6$rBqd+Yd;r5Nr_$c^u=+3g-9uN2<8I1Z^ zwvYKihg(4S4_6mKitI-r{N7c^g;x?YW?s+-xHD^8P_W4X)vTH$YR*STEt+f)!?qhP zs9$+m@IsVTzyQ?N&^tqE#q zd#X-bupBGEb<^Vs{sxe~ND+RztiFD*)>3-aghTPf_j#m$ae~G6k~XShg~><=ZG~$J z+8w9KnxU1~=J&vn3SYsDU6sa`@nle=;Gb!^H#eVmwJ%hwDS>r6T`$Kh7Q?aZki^;= zm41z`-{jMweKh?A2S+CDsKWh{HM`C&xv-?9c-c5~z@EYXht5d7kt-k?VfaWEKHApQ zgpO0a9M@h7t*nB9_G}GRK-28U{Njp9g!uyRXeGOt9)xKTjVf3P%^$t12`uyZHRK{; zKmcH-BAlB;%x-eYenFFY{xf~{&e=@&+`1p#7Cz1mQ-&K4_=Lz@_cNXjWYu*???;s0 zOAm-%_xhR-seXJKlZ`^zek0O|a=u8Pbc?i&NSt5>tKwF!?R1VUJgP+`r2hX3WfYq0 z_`=4Ec37YsT8nbZjaFQpjV}|ta7um|nv#=(Hp(#Qd9H)A#PLj{sr?>#*_U7rWWX38 zJk-fL$b4Vo_GKgDg&fxO7}&2L1k@Ii*xV&r=&#Q*$X^jk!+=;3nb$aJgZIqj03b@XAj63{C4UO*|6`zj7QGn z8G(MntLN`xl-xya-8hUHav`j=2WNm(8gNK_Wr0Cv?lQbu#}CE$rSF}}KsH{4;JqHY zo6(;(qd%QiXX@XYVUoA6FwM7&V*yEwJ$6oY2hR+ zqdEOfZ3x3$=hQfZdEs2Crx-2^Rnl~6*DN6qe5ads35k_NI-wA-ABPi+9giU1(S*}R ztRYfJV)j`@NFc1zW@gQpZZ=|e?Q{}0+i5oK3ZLP!WM+R2v&0iKbeRf%N?vw`C-12@ zt)H?kAZG}#p*0MpLo=pkQo5pC8A)0aHrB@;VY(9?Gkl=kQj-H7g0EZW(&mPsDYnHs7SFOXsF^az%E%H|Ofjg;r2`Hvs-$%a*({uG1v z1P-pc;;3tmZZQovj5Le#rZTv(>(I2m*-N>(n;(LQhj4kU)@159=*nn-J&0Y4!{h_! z#MX&eJ2#4Dr=v?iD;sXrL*tQI+^(msYUKA@ke+Xj!qK43Z!%VH$=dhK(wMR-9;=(du!%br2P5Pqro_cFo8!dagrmJ45h5 z6xvh~rZS|EPs+{wlv%J$%VmBe!-9=y(`*=x^$`@uJUQztSUuyvf?NFp{+o`qIO+jg#t3)kON!O-RIfcOQQC zV>9dKxg1rQ_)HDV3bd1C~iR7nV4V=FMctoD1S10~)B^0FUkrW+tsEev=$1 zSa=h421knD#AxZNl!eHdcHIDaO&P7R^9qyLezB>&TP$>czv%`+Sdu+c0_ydCfjt+e zpcyBq;}H@l)IWhIyT7kQS!-Ii;nmJT>J)VJcTi}ztbFKlgF?{k$vMEOP)Fi4#WC|t z|1I>DP}T$3qTmkenq_lQpl9$}@Eg*E@Lh#BCvro?ox1!>N1|ugr z#Gg#t^lOl2@`WHBC@cGFMoYd5^9vlPrdMKVlV{0*m+bH`#5s>*J1?S~*}+$8Q{Xj_ zdgU!Oo+Y?(Y2G)V?l@}PbFR(n@$mjr`qNwLPw-v~?|J)}@Q{8?e{V{+Gq3n!ZKB;&HpMDp!T8M$(yv?>(x2tz#p1RSD7yrBOJxrXj|~@*bVN$dX861Mt>JffX8nIK$$yYSZ>=jfub7W@>l$qOW*pqAL

bdHPS^KVRbWGVK*@<%Q#bd5flanOx zXGgu|J#vi@PtgLBC;=FdFKKYvWZSDOJ?>L13w1m-Q(@c}XpA~?@7}#6&#-_4UJ+Q-n=P@Q}j3!Wc&UmxfNk+ezI!i z4eB3)sFr5*u}IqUl=NgpAE*0aibCPP^rb!c&e9qxCl&KyyJ=DXEeILj3nt2X7_`DO z^^=9l-dYJ%mIN}5J990CJ7&_y(24$+wEfj#aVs|_Ox5Ycc}~=L=D3nISfQ|n6r+PO zVDn@O3UwsX>98NZP-<#fgWf5V$Lq?h(s|W1^*r&lSGu6_e&kq)#vo_YZtQ*yP?V2R zCvg(EGvk$OR(%hn#ne8@=F`_Wv*Pbx;Qh!=i2Hvgb)en)T58$+E7PxFUEmQJjBKk& zD?XM?H;c8h)d|{6&11BIIjUQ}aJtnXzRA)lfy)nGZ9SDYMqcMT8ZYp+(@`!-LPv?` zXhE&LIM5E~LfdJRm}ff_%fYL#gC#owd9lyjf%>mg`%<|w%QWsO!^Ap=e$_Ji*K+!3 zF{447l-Cwd0*oUZK%Ifc#kQ>2U23@M$1KmBI;Ft!47ey>Vka$Cr5c+1X}+S8p{x6- zmS%(t9dTz1=w)`|#XXd73~i!#FPMfMM8DDp`7*1&DM7X!%sB)yIF`d))9k6Eag zBS)C!T)hDW`!SYCx^tUJY<%3wD>XFEK;<7GMIip@;F_6aSnZSzL*yA*INEzcg2Q<$ zXM;`&vVN#@qF5#hFN$UbUYa+v9!GMUW-`Gww4R~NH&CuQLWD7}8z;zK?8W3~j$^9r z8aAtK(~qyxvRsl_&*fV_3!Ir+^55bU*T&k_ru|GWoF9;cblMl9W7suFzRJjyXNZi# zO^;l!herWNn#X>};bqji(;WSvj>HsmQ^N{vm;Pn92&8Lac4xW|O9fIPHn zImLl5tUjrMj6j=A{yVK9E;*qSB){7V}!~>uzZ|?e-R)&rp5PuXkME)Sl~>PI0{Md?aU< zaITPpb7r-vdD<(jynMga%wX7eksJ{-&7+ANrOqj(sVj>1J!VZmV)Gr|_`*|Q#rIfC)+w9~ zbPgT(#F-z>Q-&~Wf>w;*W9iH%GN7Fa4GaadjNfa6xu(jj%5DBJ1WQfJW4vDY7K&g? z@UBC+4+VJ^pDllZj82AW%vh>S!>+i@{{Y?o@SthhV;pP#QDLtAKTQu+8&H<3$X!f# zWHyo|noaeUI%q7LB4>RxqL|NRDdiT*#kVh#=b~6ph)EP*>!)nSUCtD=9*@NORM&Yt zKS=aZ`g7TLa6ZIUwVhyNs*MBQZe|9zjf^*hdO98n9f^Lk=qWLE!C5alqT2ildF>}8 z&|d)`H|n_?EHf?FBuxFj4dCVZNa#O-v3XIeF^{*Kra5EbWxDA{l_d6HaK0S+Ye+co z`^S-H#S3mYGjZ?}2#N|n5Z2ZEjCAR@ukzR* zjWIpl#4d!bo1|MXR4+sx7{TV+_f7Mc2%$}1tWB8KeJRKPz8GHjrlm}6GtoiFc~f{C&Y3UU@kZdId;k^82o4RK>8yx5FsICkQf2qojeVCCOPVw4f>5y)P{1tmD^5+h|)1I z-Nr0Or*d9E+A6-Tc}x=@_%A*JYB-J5 z6ZdFr!PzEu8iUsZ$Z%cAjYED9@hbV}(;W|@!@w~9I_HVdR%ATti?e5eR3di;o;^z6 z_-VQw4JOee3_Qp;R>y2%;1ZL3h%`65W-c*uB!ev9WZ^W}SUWhIi6_eMAaF5 zs|6!6j&;v|uf#~nSoOHvB;RI}=lO+5@Y?sy4CHIEbaxr~q;pQC%tx(n>KZu|t(m(Xx=oNCCljmMb0{Gkv|a5%b)G~5;kCCUV76$B<%pER0=!!fpK&t^D@H$l$#qOZQ9 z-z|nm>9A2;GpKk?Id(GTe54@a)I(oq)9g7==b$g?C-|LN2#NVnmNT$)$|w2)_;Sjb zxu$=jlikN!@{zRZ5IEc}ZTS>TTji2Ks?-yfs#Jle~V@XL2>oR1OW z2}uDoR1b^b_vSj&d?C|#=d6RwbGK{9$gCBwlE68!65f-gW+(|O=p-BX(M7=%`1Z(a z47|HzV5&{-LWSuYL1W=(tTHLHctgfbRQ{vL>MXn&U+S3`%r?!Bu50SF3?O4f<5CrW z1|jQon@W0m54>=?#Sb-2PtGt6dr?yHJ}EueZUEN=r~D3acwdeIdmoOWEl1 z6FM>UQs-Yv_8y7wQ1|0A5X)ZWX#Q=)7c{7%nDf?~R~{R0CD82>Usg zmCjwmhr8M|Q*WXJFI{AszS+=iZKQL?l74-_ev{Qr%YpR3W0`qv#+8kxhTZo`PDPFV zC1WJ$>=E`bNcqK30*%FtA8^EGvF6T+qCZ4o>`FeJTcDmk1BLw{vzt5dFuYX~lRgu% z9K-+<8_K?uHhA>~@W=lZZw+yrBi>nOIj=Lg2GtxpQ6Cmr#=rw9y;X8 zIR}NS44;4EQ@9gD^V9rX`7u*@#Ta>xq>NJBUlnTuQ!qlunuaY&uQr82vf{1bDbG

?s8)G!I(@vX*o8@o86Ux3Iu;f^?O|^-uP3}e*P9DKbczgr{`*zx7(WT8) zmegOCPxr)ZKaN3lGh!r{PpH3pYfaS?>*Or&W#{vIFIjGA>L-E!hn()a3;Yg(e>K)= zT<~Tl@4&ct*Dv0wcv`AW?LKU7J)ZJUpe)*iJBxaL06MpV^Sxp?)faF|Y^ikZrj1ur zTHl)N*tp&~rTKcMS2rD + + + + + + diff --git a/apps/isa-app/src/assets/images/radio_checked.svg b/apps/isa-app/src/assets/images/radio_checked.svg new file mode 100644 index 000000000..a7847d38c --- /dev/null +++ b/apps/isa-app/src/assets/images/radio_checked.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/isa-app/src/assets/images/recommendation_tag.png b/apps/isa-app/src/assets/images/recommendation_tag.png new file mode 100644 index 0000000000000000000000000000000000000000..3e5471389ae83cf75afd75e6e2829a60bd05445a GIT binary patch literal 2841 zcmchZ_dnH(%jLWD{qZ@xF90OGUL)ycZrS#z&RpWtVSJORYn zNkWRCQ)3PMO`3aNtC&+*Flv}WNvf-Sv8 zlQ?|$zUpx?z6l`1RAN^)1e9CsWEKq<8EeP#U^?hN{zQ=*tSm-l5#^--aOdNldL#r)rM^2@p{u));jh7g-ZFChy9YQh^)(iQNntn$EfRn*tT>G@ENR;0 zKh_5F6GG&F(FJ3rZ|MmEL!=M+`@jdQ+;U+Kw70v>Axn>QTRr%9WYNBg?EGPCa`6s2c6!Mgh)aK*h3Ga?Ogz{oX-qFj%_ zWrmOL4`e5KsmCOT)w7W2JS5yvpQy*AG&3XP1CzKX#mfQ>VJa-MAoN}wSOnF|!NgwX zre^^r4b9T|BZiGeh4QPlkG!z(gLSMnI*A2EP&FZ=f-JC9Rfs0W|3YQiL4=@2INy7N zSi7}WpvMFx9z4zoy;*LXIyS(r9^%}`AaZ%PKBAQVnreeu%ZI}}>j9EgacffGMt{gh zGLvhqO7F9r5KX${!_D+7ufg5#Zka$k6N}1^Bfo~bJdQP~-5oR!^kCgQwaZvKTF^Hg zQ$W3M8sjzoLm91$8TAbq9ni-gW6Vq0U@sqX^$?2_Y;fu1-R3nF2xAD)+1s%~#>4n-=NlcwCvL(7Z;2PJjWCAN>R(?JKeRJL1@dLO@;1Nn8fmNXU zz6qmUd#}!}cN&|$F)f3Y1UQ6s;G63jS>_g5yJ1a}8|n6jPCdq;T^h9|a%dl3g*q8e ze?o8f`twlBvjWZ>qwj4^H|Z+IuY&De=-eNP^udqIl5Oa`h>qw4+O?|svVe0A#P(ce zn2R=m-tTSEMk5+P5SEK2BkftW>jjAe^VUC@Dg_z>3*o&-^(V`q_J?H%We`*feX09o zxwOTTFS)Sc444$w{_L=5J?#gm{E8rb@m(lT+LP}~cyP9Lfk!fa)L~JUPURPmwd>i1 zt>uT^BTaIFRq$q^rEW5JZoxZP|vb3{2M~M!@w1Ap$(8b7*?A5kw z=e_R^AZO~Ze#xFcA+T}qgrW>7-S5R5w&+raQ1i3G+wix{*hIdGq&u^ODdhd_)5uNA z)=cU*8HSqYPfLwYU;$#|F_feYJFjmt(qnN7%Xf#pX74)9FKmZ8)X6+SOj(y~xMaOd z7SK({7!3;P;Ysi8F$1ksJw%5^uQ3P~{wUr<Ljzqm*P49} z3xSM--|6vRZ1gUwN3Cn(t_GT5$`U7YGD&1$U_|m}2B{#MHgiyvjX+o%+@rgt7iUax z{Q<(b57o`dH1s=l?#tFf`HT#KK6P9+XW`rVV#hwGznY-6F9h-hpH8V6+4PUy1g+wK$ANFS$3$O5O8+LT;kI6^ ze(Mr)pbm1Qwx#U}IWdjAY`QZ^r-jKDN|ShBMPM^#Lxh~MlKc{{sq^WaZSJjna+CPa zq|%s)Px(#(YZNW(u*{M@^5DMd^_0IF*g^Q8pwjLot>}_MdrM1s)gYa;r*91{6UQn5 z6^q2Vbj&4fL7mo!ue@V9da~Iqzf}Ht{UHte!&SFh|Hr#r#T)JQo9Vvf*+VF_^Ak?V z0-OExq#`dm!3PR7VADzj4i%iQ`Vn^d{$*mMr^5=O*xq}HSBR+xR~6pw`<>%ZTxhmM zG#m2+6flV>378dBxiUsvI+!fC6~jMS$^i45m*2_S{cbV+qut70qO(#}NO!V{j@$XHk|T{8(sg!={nks0W7MhYPXU|1arJ{4<6w1 zTy+Jg7!Drw_MT=^3lJzaJkqJ3AE7JoFLHQH>5jpoDvC$Sy$UQVQ1K zAd%=MX7>M|qL~k1#l(W&aC;S)rq1E~7xTj-p1)X@iI(;EqgfP}-xPv;rFSC=p$#N& zqsLH|zGq3`R%&BN3a|X5Y;uHJBVAXc6Ts@|E%x+52&zkMWTa0tAY-7nVPlkkh1s0I zP?9y6lS^;o;@I`zP)pu{X?$3Be5%Jj-~f7q$ON;06-$)S@43Q*^lo~TFPeq4`bjBN zH^aZapw>s|Wf0NwF=s}EUW;9+BVxs9Jyy#V^S^Q6E*`cLS+}!BJ2BULsK7gTp_TEr zv_{XTozsqwtb-WxuUz>znch&43sUnlpU7bo$jihtSZ3r!UC$eDEpLzuu5riD{ z#7Vu~UOdYEP)Lr~N=ZTtEWsti2tkXcf2y& z&dx!f@c~ZDKEy6Hp?@+Brz5k|@e6v*Z?Xo}@_8!0c<$qE0%Z|aKjW@jjl0d*AqG~9 zo*Ib#i#$WngrOr9T@S`*r%f1BTqI^+H{*S0hjW6qyn_TVIs4x1Mab%5d(|3>^Q8S0 zlC#bH2?`@kT|MV!?yWy!Rh@q@;ov_mn`e$|3pGLym{rpfOyVzAIY~Z;$XLu&j~=T$ zc034Xji@0SW~HX1;O%$=$wl@hJMf+DkNeK7YR~zmD*KJACWaNi+`}7XP4^zG{9Pw_ qQBk7$g4Iw;LnlN{pVCh}6_ynLF>*m6jqSh73z)&JjA{(r6aNQ}Q)t%! literal 0 HcmV?d00001 diff --git a/apps/isa-app/src/assets/images/sms_bookmark.svg b/apps/isa-app/src/assets/images/sms_bookmark.svg new file mode 100644 index 000000000..95e3be20e --- /dev/null +++ b/apps/isa-app/src/assets/images/sms_bookmark.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/apps/isa-app/src/assets/images/spinner.svg b/apps/isa-app/src/assets/images/spinner.svg new file mode 100644 index 000000000..9a92a8232 --- /dev/null +++ b/apps/isa-app/src/assets/images/spinner.svg @@ -0,0 +1,11 @@ + + + + Icon_Loading + Created with Sketch. + + + + + + \ No newline at end of file diff --git a/apps/isa-app/src/assets/images/tag_icon_preorder.svg b/apps/isa-app/src/assets/images/tag_icon_preorder.svg new file mode 100644 index 000000000..20fb702e9 --- /dev/null +++ b/apps/isa-app/src/assets/images/tag_icon_preorder.svg @@ -0,0 +1,17 @@ + + + + Bookmark / Preorder + Created with Sketch. + + + + + + + + + + + + \ No newline at end of file diff --git a/apps/isa-app/src/config/config.feature.json b/apps/isa-app/src/config/config.feature.json new file mode 100644 index 000000000..98b0116a6 --- /dev/null +++ b/apps/isa-app/src/config/config.feature.json @@ -0,0 +1,62 @@ +{ + "title": "ISA - Local", + "@cdn/product-image": { + "url": "https://produktbilder.paragon-data.net" + }, + "@core/auth": { + "issuer": "https://sso-test.paragon-data.de", + "clientId": "hug-isa", + "responseType": "id_token token", + "oidc": true, + "scope": "openid profile cmf_user isa-isa-webapi isa-checkout-webapi isa-cat-webapi isa-ava-webapi isa-crm-webapi isa-review-webapi isa-kpi-webapi isa-oms-webapi isa-nbo-webapi isa-print-webapi eis-service isa-inv-webapi" + }, + "@core/logger": { + "logLevel": "debug" + }, + "@swagger/isa": { + "rootUrl": "https://isa-integration.paragon-data.net/isa/v1" + }, + "@swagger/cat": { + "rootUrl": "https://isa-test.paragon-data.net/catsearch/v5" + }, + "@swagger/av": { + "rootUrl": "https://isa-test.paragon-data.net/ava/v4" + }, + "@swagger/checkout": { + "rootUrl": "https://isa-test.paragon-data.net/checkout/v3" + }, + "@swagger/crm": { + "rootUrl": "https://isa-test.paragon-data.net/crm/v3" + }, + "@swagger/oms": { + "rootUrl": "https://isa-test.paragon-data.net/oms/v4" + }, + "@swagger/print": { + "rootUrl": "https://isa-test.paragon-data.net/print/v1" + }, + "@swagger/eis": { + "rootUrl": "https://filialinformationsystem-test.paragon-systems.de/eiswebapi/v1" + }, + "@swagger/remi": { + "rootUrl": "https://isa-test.paragon-data.net/inv/v1" + }, + "hubs": { + "notifications": { + "url": "https://isa-test.paragon-data.net/isa/v1/rt", + "enableAutomaticReconnect": false, + "httpOptions": { + "transport": 1, + "logMessageContent": true, + "skipNegotiation": true + } + } + }, + "process": { + "ids": { + "goodsOut": 1000, + "goodsIn": 2000, + "taskCalendar": 3000, + "remission": 4000 + } + } +} \ No newline at end of file diff --git a/apps/isa-app/src/config/config.integration.json b/apps/isa-app/src/config/config.integration.json new file mode 100644 index 000000000..81b01797b --- /dev/null +++ b/apps/isa-app/src/config/config.integration.json @@ -0,0 +1,21 @@ +{ + "title": "ISA - Integration", + "@core/auth": { + "issuer": "https://sso-test.paragon-data.de", + "clientId": "hug-isa", + "responseType": "id_token token", + "oidc": true, + "scope": "openid profile cmf_user isa-isa-webapi isa-checkout-webapi isa-cat-webapi isa-ava-webapi isa-crm-webapi isa-review-webapi isa-kpi-webapi isa-oms-webapi isa-nbo-webapi isa-print-webapi eis-service isa-inv-webapi" + }, + "hubs": { + "notifications": { + "url": "https://isa-integration.paragon-data.net/isa/v1/rt", + "enableAutomaticReconnect": false, + "httpOptions": { + "transport": 1, + "logMessageContent": true, + "skipNegotiation": true + } + } + } +} \ No newline at end of file diff --git a/apps/isa-app/src/config/config.json b/apps/isa-app/src/config/config.json new file mode 100644 index 000000000..81d65a66d --- /dev/null +++ b/apps/isa-app/src/config/config.json @@ -0,0 +1,62 @@ +{ + "title": "ISA - Local", + "@cdn/product-image": { + "url": "https://produktbilder.paragon-data.net" + }, + "@core/auth": { + "issuer": "https://sso-test.paragon-data.de", + "clientId": "hug-isa", + "responseType": "id_token token", + "oidc": true, + "scope": "openid profile cmf_user isa-isa-webapi isa-checkout-webapi isa-cat-webapi isa-ava-webapi isa-crm-webapi isa-review-webapi isa-kpi-webapi isa-oms-webapi isa-nbo-webapi isa-print-webapi eis-service isa-inv-webapi" + }, + "@core/logger": { + "logLevel": "debug" + }, + "@swagger/isa": { + "rootUrl": "https://isa-integration.paragon-data.net/isa/v1" + }, + "@swagger/cat": { + "rootUrl": "https://isa-test.paragon-data.net/catsearch/v5" + }, + "@swagger/av": { + "rootUrl": "https://isa-test.paragon-data.net/ava/v4" + }, + "@swagger/checkout": { + "rootUrl": "https://isa-test.paragon-data.net/checkout/v3" + }, + "@swagger/crm": { + "rootUrl": "https://isa-test.paragon-data.net/crm/v3" + }, + "@swagger/oms": { + "rootUrl": "https://isa-test.paragon-data.net/oms/v4" + }, + "@swagger/print": { + "rootUrl": "https://isa-test.paragon-data.net/print/v1" + }, + "@swagger/eis": { + "rootUrl": "https://filialinformationsystem-test.paragon-systems.de/eiswebapi/v1" + }, + "@swagger/remi": { + "rootUrl": "https://isa-test.paragon-data.net/inv/v1" + }, + "hubs": { + "notifications": { + "url": "https://isa-test.paragon-data.net/isa/v1/rt", + "enableAutomaticReconnect": false, + "httpOptions": { + "transport": 1, + "logMessageContent": true, + "skipNegotiation": true + } + } + }, + "process": { + "ids": { + "goodsOut": 1000, + "goodsIn": 2000, + "taskCalendar": 3000, + "remission": 4000 + } + } +} \ No newline at end of file diff --git a/apps/isa-app/src/config/config.production.json b/apps/isa-app/src/config/config.production.json new file mode 100644 index 000000000..40c821fa7 --- /dev/null +++ b/apps/isa-app/src/config/config.production.json @@ -0,0 +1,21 @@ +{ + "title": "ISA - Production", + "@core/auth": { + "issuer": "https://sso.paragon-data.de", + "clientId": "hug-isa", + "responseType": "id_token token", + "oidc": true, + "scope": "openid profile cmf_user isa-isa-webapi isa-checkout-webapi isa-cat-webapi isa-ava-webapi isa-crm-webapi isa-review-webapi isa-kpi-webapi isa-oms-webapi isa-nbo-webapi isa-print-webapi eis-service isa-inv-webapi" + }, + "hubs": { + "notifications": { + "url": "https://isa.paragon-systems.de/isa/v1/rt", + "enableAutomaticReconnect": false, + "httpOptions": { + "transport": 1, + "logMessageContent": true, + "skipNegotiation": true + } + } + } +} \ No newline at end of file diff --git a/apps/isa-app/src/config/config.staging.json b/apps/isa-app/src/config/config.staging.json new file mode 100644 index 000000000..ee6f1bd7a --- /dev/null +++ b/apps/isa-app/src/config/config.staging.json @@ -0,0 +1,21 @@ +{ + "title": "ISA - Staging", + "@core/auth": { + "issuer": "https://sso.paragon-data.de", + "clientId": "hug-isa", + "responseType": "id_token token", + "oidc": true, + "scope": "openid profile cmf_user isa-isa-webapi isa-checkout-webapi isa-cat-webapi isa-ava-webapi isa-crm-webapi isa-review-webapi isa-kpi-webapi isa-oms-webapi isa-nbo-webapi isa-print-webapi eis-service isa-inv-webapi" + }, + "hubs": { + "notifications": { + "url": "https://isa-staging.paragon-systems.de/isa/v1/rt", + "enableAutomaticReconnect": false, + "httpOptions": { + "transport": 1, + "logMessageContent": true, + "skipNegotiation": true + } + } + } +} \ No newline at end of file diff --git a/apps/isa-app/src/config/config.test.json b/apps/isa-app/src/config/config.test.json new file mode 100644 index 000000000..fdaa967fa --- /dev/null +++ b/apps/isa-app/src/config/config.test.json @@ -0,0 +1,62 @@ +{ + "title": "ISA - Test", + "@cdn/product-image": { + "url": "https://produktbilder.paragon-data.net" + }, + "@core/auth": { + "issuer": "https://sso-test.paragon-data.de", + "clientId": "hug-isa", + "responseType": "id_token token", + "oidc": true, + "scope": "openid profile cmf_user isa-isa-webapi isa-checkout-webapi isa-cat-webapi isa-ava-webapi isa-crm-webapi isa-review-webapi isa-kpi-webapi isa-oms-webapi isa-nbo-webapi isa-print-webapi eis-service isa-inv-webapi" + }, + "@core/logger": { + "logLevel": "debug" + }, + "@swagger/isa": { + "rootUrl": "https://isa-integration.paragon-data.net/isa/v1" + }, + "@swagger/cat": { + "rootUrl": "https://isa-test.paragon-data.net/catsearch/v5" + }, + "@swagger/av": { + "rootUrl": "https://isa-test.paragon-data.net/ava/v4" + }, + "@swagger/checkout": { + "rootUrl": "https://isa-test.paragon-data.net/checkout/v3" + }, + "@swagger/crm": { + "rootUrl": "https://isa-test.paragon-data.net/crm/v3" + }, + "@swagger/oms": { + "rootUrl": "https://isa-test.paragon-data.net/oms/v4" + }, + "@swagger/print": { + "rootUrl": "https://isa-test.paragon-data.net/print/v1" + }, + "@swagger/eis": { + "rootUrl": "https://filialinformationsystem-test.paragon-systems.de/eiswebapi/v1" + }, + "@swagger/remi": { + "rootUrl": "https://isa-test.paragon-data.net/inv/v1" + }, + "hubs": { + "notifications": { + "url": "https://isa-test.paragon-data.net/isa/v1/rt", + "enableAutomaticReconnect": false, + "httpOptions": { + "transport": 1, + "logMessageContent": true, + "skipNegotiation": true + } + } + }, + "process": { + "ids": { + "goodsOut": 1000, + "goodsIn": 2000, + "taskCalendar": 3000, + "remission": 4000 + } + } +} \ No newline at end of file diff --git a/apps/isa-app/src/environments/environment.prod.ts b/apps/isa-app/src/environments/environment.prod.ts new file mode 100644 index 000000000..c9669790b --- /dev/null +++ b/apps/isa-app/src/environments/environment.prod.ts @@ -0,0 +1,3 @@ +export const environment = { + production: true, +}; diff --git a/apps/isa-app/src/environments/environment.ts b/apps/isa-app/src/environments/environment.ts new file mode 100644 index 000000000..66998ae9a --- /dev/null +++ b/apps/isa-app/src/environments/environment.ts @@ -0,0 +1,16 @@ +// This file can be replaced during build by using the `fileReplacements` array. +// `ng build` replaces `environment.ts` with `environment.prod.ts`. +// The list of file replacements can be found in `angular.json`. + +export const environment = { + production: false, +}; + +/* + * For easier debugging in development mode, you can import the following file + * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. + * + * This import should be commented out in production mode because it will have a negative impact + * on performance if an error is thrown. + */ +// import 'zone.js/plugins/zone-error'; // Included with Angular CLI. diff --git a/apps/isa-app/src/favicon.ico b/apps/isa-app/src/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..052ee0daed7bccc523681f1767dcb739aa07ce0c GIT binary patch literal 7406 zcmeI0c|6rwAICqHrG3#Nm68&n>&nui4J|5aG&K6#J->6mzjN>TzCS=W^Z=I& z;(H>dKcEjlNvZR`Cs0b-1`hmm?%@*7I|gfOYqYm_;MJ>FNJvP4hldB! z($etyl^9EwEI~<03Enh`arW$4eEj$k#f3s7Cnuxixe$jA9m2|$D^Xr5gsZD7?%%(U z>_>boT(}Unwzhcl<_+#7x8mBhYw-5=hJk?rDk>^);lc$tIXR)NtqrZM?eO>ahd|Jd z$_f#lJb8jD;;UbZP(v&}J|6Dw?$FoQM_sK5^~Cn<*#k2(Gn_ed2J6rHb#0X zA9;CsIC}IbR;^lv`ucio+_({XdU{B{-ip-JRP5fp8>XhFxSiCBwQJYn{rmU0apMMN z&6ExT9KZfj(z*~A?0Q(mM&e2bLY-MU0of` z&CQ65YR1i*H(_XKh@6}p96o%Q?5B2ESXh8hd|X@{swzcrb8~}^jt<%5?Wn01p_bU9 zMT<~hCqmw10qpGTuy^kssHmtQKTm+SZ{K3`=FR`{X$1)`6$DKhznO}~V zQcQ4I_Cq>p+`OQ(=XkI1g{1nucmE&$6@L6UYn{6!T-$hr^mNg8w)#bJmJEsUAcOqQR>%dI?d3K6xElw-S=dC;D>S7c62aj-}-G}9QeJ=EkuLa<%GU*{hu zt*P!MH8gC(P~#Z`!!{+xyB_NvF#P8zlPfNoa);B5`ws9JwK(4V^Lj!P!+Rdd?Rwg; zK2)C6@0yNhEoP=`x_XA+3XeNWqxhRL+=?`8;eF~!=WY2-x8zn50Z9b@>R#uLNh6ZFm6f%Cu$r3XDTd-gO zV~9r?d~Dyo9YsY&jOUgwU(T3h-n@AT2?-&5+6F&AKa`f#GDgXGKp2A<&GB7acv!Y< z8D0<$d6XeQaBwhVEXp3GFNB1VM2rWMl9EtUQ;W*VO2%H4-JawM7#~tbp-fC!A}lP7 zF(PH$f~Nw;ca;?_gnxyMohWxVG&JJfTQRJxtWZ%VV*HnzEo5936cmKg(o)80l!Yi8 zQ=X>0MtSr3^D@TclyNC17Z(>ZF082*Gw!SJ9jR~?~`!vX~ud~E1+yk8JV&u<*b^TSJ=9BE1o`miuLQ)Gv=a*f8o!ea6+#EhdU3sPNx@)Kn;%EFYbsXkC!BPMM14mnu@#;-GG z%wW8oot=%Umz9h~yS9nM|Fa`N_K~FgB_d$pw#s^vx1Fx$_{lS^du%Y->NaNPlFut! zw^M)Q6hHH?R?P0%`+Jv-OQlstDE0U2#p7{h=jzA~RJKuAGu+H`+jO08l-0~EWIUu~ z7JfHu&>$X9YJt&wOxrR|K~B-ZVePl-J7#d4>^UoKUFDq|>FiS2tToS=<7~CMTcDPq zdfz@6Z8^ks@pich%Y10RDaU4%`nbVg8!qlXbdlFs)d92mnJu^1)0m^}8bJH~-DOp$ z63^vvH}SY!o|Kg*Z@n+MZ2pK+8*8S^f|h7)Wx7gG5^tGd+}^-&8ZdbLSp83q=SF+{w;ws&P{d zm7euJK0fR`-LYc_(YT4We&zzpd*~S)9UVtBW)Vt=)zvkEyhw63?`1hhS&0ZVZ>X-W zMjO%1sdi0uXsYAK#U-$ODkdhLb{us_oMQ^xBeSxEh + + + + IsaApp + + + + + + + + + +

+ spinner animation +
+ + + + diff --git a/apps/isa-app/src/main.ts b/apps/isa-app/src/main.ts new file mode 100644 index 000000000..d9a2e7e4a --- /dev/null +++ b/apps/isa-app/src/main.ts @@ -0,0 +1,13 @@ +import { enableProdMode } from '@angular/core'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; + +import { AppModule } from './app/app.module'; +import { environment } from './environments/environment'; + +if (environment.production) { + enableProdMode(); +} + +platformBrowserDynamic() + .bootstrapModule(AppModule) + .catch((err) => console.error(err)); diff --git a/apps/isa-app/src/manifest.webmanifest b/apps/isa-app/src/manifest.webmanifest new file mode 100644 index 000000000..942b277aa --- /dev/null +++ b/apps/isa-app/src/manifest.webmanifest @@ -0,0 +1,41 @@ +{ + "name": "ISA", + "short_name": "isa-app", + "theme_color": "#f70400", + "background_color": "#f70400", + "display": "standalone", + "scope": "./", + "start_url": "./", + "icons": [ + { + "src": "assets/icons/icon-72x72.png", + "sizes": "72x72", + "type": "image/png", + "purpose": "maskable any" + }, + { + "src": "assets/icons/icon-96x96.png", + "sizes": "96x96", + "type": "image/png", + "purpose": "maskable any" + }, + { + "src": "assets/icons/icon-144x144.png", + "sizes": "144x144", + "type": "image/png", + "purpose": "maskable any" + }, + { + "src": "assets/icons/icon-192x192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable any" + }, + { + "src": "assets/icons/icon-512x512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable any" + } + ] +} diff --git a/apps/isa-app/src/polyfills.ts b/apps/isa-app/src/polyfills.ts new file mode 100644 index 000000000..8a120c374 --- /dev/null +++ b/apps/isa-app/src/polyfills.ts @@ -0,0 +1,64 @@ +/** + * This file includes polyfills needed by Angular and is loaded before the app. + * You can add your own extra polyfills to this file. + * + * This file is divided into 2 sections: + * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. + * 2. Application imports. Files imported after ZoneJS that should be loaded before your main + * file. + * + * The current setup is for so-called "evergreen" browsers; the last versions of browsers that + * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), + * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. + * + * Learn more in https://angular.io/guide/browser-support + */ + +/*************************************************************************************************** + * BROWSER POLYFILLS + */ + +/** + * IE11 requires the following for NgClass support on SVG elements + */ +// import 'classlist.js'; // Run `npm install --save classlist.js`. + +/** + * Web Animations `@angular/platform-browser/animations` + * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. + * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). + */ +// import 'web-animations-js'; // Run `npm install --save web-animations-js`. + +/** + * By default, zone.js will patch all possible macroTask and DomEvents + * user can disable parts of macroTask/DomEvents patch by setting following flags + * because those flags need to be set before `zone.js` being loaded, and webpack + * will put import in the top of bundle, so user need to create a separate file + * in this directory (for example: zone-flags.ts), and put the following flags + * into that file, and then add the following code before importing zone.js. + * import './zone-flags'; + * + * The flags allowed in zone-flags.ts are listed here. + * + * The following flags will work for all browsers. + * + * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame + * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick + * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames + * + * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js + * with the following flag, it will bypass `zone.js` patch for IE/Edge + * + * (window as any).__Zone_enable_cross_context_check = true; + * + */ + +/*************************************************************************************************** + * Zone JS is required by default for Angular itself. + */ +import 'zone.js'; // Included with Angular CLI. + +/*************************************************************************************************** + * APPLICATION IMPORTS + */ diff --git a/apps/isa-app/src/scss/_branch.scss b/apps/isa-app/src/scss/_branch.scss new file mode 100644 index 000000000..7a9602cf5 --- /dev/null +++ b/apps/isa-app/src/scss/_branch.scss @@ -0,0 +1,26 @@ +body.branch { + --bg-color: #edeff0; + + // @shell/header + --shell-header-button-color: #89949e; + --shell-header-button-color-active: #586470; + --shell-header-button-color-disabled: #c6cbd2; + --shell-header-switch-background: #edeff0; + --shell-header-switch-color: #596470; + --shell-header-label-color: #000000; + --shell-header-label-color-active: #ffffff; + + // @shell/breadcrumb + --shell-breadcrumb-background: var(--bg-color); + + // @shell/footer + --shell-footer-link-active: #596470; + --shell-footer-link-inactive: #9ca5b0; + --shell-footer-link-disabled: #c6cbd2; + + // @shell/process + --shell-process-text-active: #596470; + --shell-process-text-inactive: #9ca5b0; + --shell-process-badge-background: #edeff0; + --shell-process-badge-active: #596470; +} diff --git a/apps/isa-app/src/scss/_customer.scss b/apps/isa-app/src/scss/_customer.scss new file mode 100644 index 000000000..da5ecc84c --- /dev/null +++ b/apps/isa-app/src/scss/_customer.scss @@ -0,0 +1,29 @@ +body.customer { + --bg-color: #e6eff9; + + // @shell/header + --shell-header-button-color: #9db2c6; + --shell-header-button-color-active: #557596; + --shell-header-button-color-disabled: #d7e6f4; + --shell-header-switch-background: #e6eff9; + --shell-header-switch-color: #1f466c; + --shell-header-label-color: #000000; + --shell-header-label-color-active: #ffffff; + + // @shell/breadcrumb + --shell-breadcrumb-background: var(--bg-color); + + // @shell/footer + --shell-footer-link-active: #1f466c; + --shell-footer-link-inactive: #557596; + --shell-footer-link-disabled: #d7e6f4; + + // @shell/process + --shell-process-text-active: #1f466c; + --shell-process-text-inactive: #557596; + --shell-process-badge-background: #e6eff9; + --shell-process-badge-active: #1f466c; + + // @page/dashboard + --page-dashboard-card-title-color: #1f466c; +} diff --git a/apps/isa-app/src/scss/_root.scss b/apps/isa-app/src/scss/_root.scss new file mode 100644 index 000000000..013b304d0 --- /dev/null +++ b/apps/isa-app/src/scss/_root.scss @@ -0,0 +1,18 @@ +:root { + // @isa-app/shell + --shell-notification-counter-background: #f70400; + --shell-notification-counter-text: #fff; + + // @shell/footer + --shell-footer-background: #fff; + --shell-footer-link-active: #1f466c; + --shell-footer-link-inactive: #557596; + --shell-footer-link-disabled: #d7e6f4; + + // @shell/process + --shell-process-add-icon-text: #fff; + --shell-process-add-icon-background: #f70400; + --shell-process-add-label: #f70400; + --shell-process-border-active: #f70400; + --shell-process-background: #fff; +} diff --git a/apps/isa-app/src/silent-refresh.html b/apps/isa-app/src/silent-refresh.html new file mode 100644 index 000000000..551b4bad8 --- /dev/null +++ b/apps/isa-app/src/silent-refresh.html @@ -0,0 +1,7 @@ + + + + + diff --git a/apps/isa-app/src/styles.scss b/apps/isa-app/src/styles.scss new file mode 100644 index 000000000..56b4e50ad --- /dev/null +++ b/apps/isa-app/src/styles.scss @@ -0,0 +1,23 @@ +/* You can add global styles to this file, and also import other style files */ +@import '~@angular/cdk/overlay-prebuilt.css'; + +@import 'tailwindcss/base'; +@import 'tailwindcss/components'; +@import 'tailwindcss/utilities'; + +@import './scss/root'; +@import './scss/customer'; +@import './scss/branch'; + +* { + @apply font-sans; +} + +body { + background: var(--bg-color); +} + +::-webkit-scrollbar { + width: 0; // remove scrollbar space + background: transparent; // optional: just make scrollbar invisible */ +} diff --git a/apps/isa-app/src/test.ts b/apps/isa-app/src/test.ts new file mode 100644 index 000000000..227ad05ad --- /dev/null +++ b/apps/isa-app/src/test.ts @@ -0,0 +1,24 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import 'zone.js/testing'; +import { getTestBed } from '@angular/core/testing'; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; + +declare const require: { + context( + path: string, + deep?: boolean, + filter?: RegExp + ): { + keys(): string[]; + (id: string): T; + }; +}; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), { teardown: { destroyAfterEach: true } }); + +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().map(context); diff --git a/apps/isa-app/tsconfig.app.json b/apps/isa-app/tsconfig.app.json new file mode 100644 index 000000000..fd37f74d7 --- /dev/null +++ b/apps/isa-app/tsconfig.app.json @@ -0,0 +1,15 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/app", + "types": [] + }, + "files": [ + "src/main.ts", + "src/polyfills.ts" + ], + "include": [ + "src/**/*.d.ts" + ] +} diff --git a/apps/isa-app/tsconfig.spec.json b/apps/isa-app/tsconfig.spec.json new file mode 100644 index 000000000..b66a2f0b1 --- /dev/null +++ b/apps/isa-app/tsconfig.spec.json @@ -0,0 +1,18 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/spec", + "types": [ + "jasmine" + ] + }, + "files": [ + "src/test.ts", + "src/polyfills.ts" + ], + "include": [ + "src/**/*.spec.ts", + "src/**/*.d.ts" + ] +} diff --git a/apps/isa/remission/src/lib/mappings/features-to-assortment.mapping.ts b/apps/isa/remission/src/lib/mappings/features-to-assortment.mapping.ts index 8c5685b66..91839abca 100644 --- a/apps/isa/remission/src/lib/mappings/features-to-assortment.mapping.ts +++ b/apps/isa/remission/src/lib/mappings/features-to-assortment.mapping.ts @@ -19,3 +19,13 @@ export class FeaturesToAssortmentMapping implements Mapper<{ key: string; name: return source.reduce((acc, curr) => acc + `${curr.name || ''}|${curr.key.slice(curr.key.length - 1, curr.key.length)}`, ''); } } + +/** + * + * source.features.map((feature) => ({ + name: feature.value, + key: feature.key[feature.key.length - 1], + })), + * + * + */ diff --git a/apps/isa/remission/src/lib/rest/rest-remission-products.service.ts b/apps/isa/remission/src/lib/rest/rest-remission-products.service.ts index d915d6b40..8c23099e0 100644 --- a/apps/isa/remission/src/lib/rest/rest-remission-products.service.ts +++ b/apps/isa/remission/src/lib/rest/rest-remission-products.service.ts @@ -7,8 +7,8 @@ import { MappingService } from '../mappings/mapping.service'; import { Mapper } from '../mappings/mapper'; import { RemissionFilter } from '../models/remission-filter'; import { RemissionProduct } from '../models/remission-product'; -import { isNumber } from 'util'; import { RemiService, ReturnItemDTO, ReturnSuggestionDTO, KeyValueDTOOfStringAndString, ReturnService, StockService } from '@swagger/remi'; +import { isNumber } from '@utils/common'; @Injectable({ providedIn: 'root' }) export class RestRemissionProductsService { diff --git a/apps/isa/remission/src/lib/services/rest-remission.service.ts b/apps/isa/remission/src/lib/services/rest-remission.service.ts index b23898e21..19313b983 100644 --- a/apps/isa/remission/src/lib/services/rest-remission.service.ts +++ b/apps/isa/remission/src/lib/services/rest-remission.service.ts @@ -38,7 +38,6 @@ import { ShippingDocument } from '../models/shipping-document'; import { ActionResult, CapacityType } from '../models'; import { compare } from '../utils/compare'; import { addDays } from '../utils/add-days'; -import { isNullOrUndefined } from 'util'; import { RemiService, SupplierService, @@ -58,7 +57,7 @@ import { } from '@swagger/remi'; import { ItemDTO, SearchService } from '@swagger/cat'; import { PrintService, InventoryPrintService, PrintRequestOfString } from '@swagger/print'; -import { memorize } from '@utils/common'; +import { isNullOrUndefined, memorize } from '@utils/common'; import { HttpErrorResponse } from '@angular/common/http'; @Injectable({ providedIn: 'root' }) @@ -648,7 +647,6 @@ export class RestRemissionService extends RemissionService { return this.stock$.pipe( first(), - tap(console.log.bind(window)), mergeMap((stock) => this.returnApi.ReturnCreateReturnItem({ data: { diff --git a/apps/isa/remission/src/test.ts b/apps/isa/remission/src/test.ts index be4725e7c..8ff0d4a25 100644 --- a/apps/isa/remission/src/test.ts +++ b/apps/isa/remission/src/test.ts @@ -1,8 +1,8 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files import 'core-js/es7/reflect'; -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/isa/remission/tsconfig.lib.json b/apps/isa/remission/tsconfig.lib.json index 1c34c36b8..02b80298e 100644 --- a/apps/isa/remission/tsconfig.lib.json +++ b/apps/isa/remission/tsconfig.lib.json @@ -2,6 +2,7 @@ "extends": "../../../tsconfig.json", "compilerOptions": { "outDir": "../../../out-tsc/lib", + "declarationMap": true, "target": "es2015", "declaration": true, "inlineSources": true, diff --git a/apps/isa/remission/tsconfig.lib.prod.json b/apps/isa/remission/tsconfig.lib.prod.json index b1d501abb..2617a83e3 100644 --- a/apps/isa/remission/tsconfig.lib.prod.json +++ b/apps/isa/remission/tsconfig.lib.prod.json @@ -1,5 +1,8 @@ { "extends": "./tsconfig.lib.json", + "compilerOptions": { + "declarationMap": false + }, "angularCompilerOptions": { "enableIvy": false } diff --git a/apps/modal/availabilities/src/test.ts b/apps/modal/availabilities/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/modal/availabilities/src/test.ts +++ b/apps/modal/availabilities/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/modal/history/src/lib/history.component.html b/apps/modal/history/src/lib/history.component.html index 5e0d69db7..c49481cf7 100644 --- a/apps/modal/history/src/lib/history.component.html +++ b/apps/modal/history/src/lib/history.component.html @@ -2,14 +2,7 @@
Kundenname - - {{ ref.data.item.organisation }}, - {{ ref.data.item.lastName }}, {{ ref.data.item.firstName }} - - - {{ ref.data.item.order.buyer.organisation }}, - {{ ref.data.item.order.buyer.lastName }}, {{ ref.data.item.order.buyer.firstName }} - + {{ customerName }}
Kundennummer diff --git a/apps/modal/history/src/lib/history.component.ts b/apps/modal/history/src/lib/history.component.ts index f4585380d..d2ba8efb8 100644 --- a/apps/modal/history/src/lib/history.component.ts +++ b/apps/modal/history/src/lib/history.component.ts @@ -3,7 +3,7 @@ import { CrmCustomerService } from '@domain/crm'; import { DomainOmsService } from '@domain/oms'; import { CustomerDTO } from '@swagger/crm'; import { HistoryDTO as CrmHistoryDTO } from '@swagger/crm'; -import { HistoryDTO as OmsHistoryDTO, OrderDTO, OrderItemDTO } from '@swagger/oms'; +import { HistoryDTO as OmsHistoryDTO, OrderDTO } from '@swagger/oms'; import { OrderItemListItemDTO } from '@swagger/oms'; import { UiModalRef } from '@ui/modal'; import { BehaviorSubject, Observable, of } from 'rxjs'; @@ -22,6 +22,20 @@ export class HistoryComponent implements OnInit { history$: Observable; + get customerName() { + if (this.ref.data.mode === 'customer' || this.ref.data.mode === 'goods') { + const data = this.ref.data.item as CustomerDTO; + return [data?.organisation?.name ?? data?.organisation, data?.lastName, data?.firstName].filter((i) => !!i).join(', '); + } + + if (this.ref.data.mode === 'order') { + const data = this.ref.data.item as { order: OrderDTO; orderItemSubsetId: number }; + return [data?.order?.buyer?.organisation, data?.order?.buyer?.lastName, data?.order?.buyer?.firstName].filter((i) => !!i).join(', '); + } + + return ''; + } + constructor( public ref: UiModalRef< any, diff --git a/apps/modal/history/src/test.ts b/apps/modal/history/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/modal/history/src/test.ts +++ b/apps/modal/history/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/modal/images/src/test.ts b/apps/modal/images/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/modal/images/src/test.ts +++ b/apps/modal/images/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/modal/notifications/karma.conf.js b/apps/modal/notifications/karma.conf.js index 3543776e3..5971088fa 100644 --- a/apps/modal/notifications/karma.conf.js +++ b/apps/modal/notifications/karma.conf.js @@ -1,5 +1,8 @@ // Karma configuration file, see link for more information // https://karma-runner.github.io/1.0/config/configuration-file.html +const customLaunchers = require('../../../karma/custom-launchers'); +const junitReporter = require('../../../karma/junit-reporter')('modal-notifications'); +const coverageReporter = require('../../../karma/coverage-reporter')('modal-notifications'); module.exports = function (config) { config.set({ @@ -9,23 +12,31 @@ module.exports = function (config) { require('karma-jasmine'), require('karma-chrome-launcher'), require('karma-jasmine-html-reporter'), - require('karma-coverage-istanbul-reporter'), + require('karma-coverage'), + require('karma-junit-reporter'), require('@angular-devkit/build-angular/plugins/karma'), ], client: { + jasmine: { + // you can add configuration options for Jasmine here + // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html + // for example, you can disable the random execution with `random: false` + // or set a specific seed with `seed: 4321` + }, clearContext: false, // leave Jasmine Spec Runner output visible in browser }, - coverageIstanbulReporter: { - dir: require('path').join(__dirname, '../../../coverage/modal/notifications'), - reports: ['html', 'lcovonly', 'text-summary'], - fixWebpackSourcePaths: true, + jasmineHtmlReporter: { + suppressAll: true, // removes the duplicated traces }, + coverageReporter, + junitReporter, reporters: ['progress', 'kjhtml'], port: 9876, colors: true, logLevel: config.LOG_INFO, autoWatch: true, browsers: ['Chrome'], + customLaunchers, singleRun: false, restartOnFileChange: true, }); diff --git a/apps/modal/notifications/src/lib/notifications-list-item/notifications-list-item.component.html b/apps/modal/notifications/src/lib/notifications-list-item/notifications-list-item.component.html index e75952d4b..b1d47313b 100644 --- a/apps/modal/notifications/src/lib/notifications-list-item/notifications-list-item.component.html +++ b/apps/modal/notifications/src/lib/notifications-list-item/notifications-list-item.component.html @@ -5,4 +5,4 @@
-
{{ item.text }}
+
{{ item.text }}
diff --git a/apps/modal/notifications/src/lib/notifications-list-item/notifications-list-item.component.spec.ts b/apps/modal/notifications/src/lib/notifications-list-item/notifications-list-item.component.spec.ts new file mode 100644 index 000000000..716a84126 --- /dev/null +++ b/apps/modal/notifications/src/lib/notifications-list-item/notifications-list-item.component.spec.ts @@ -0,0 +1,48 @@ +import { createComponentFactory, Spectator } from '@ngneat/spectator'; +import { CommonModule } from '@angular/common'; +import { ModalNotificationsListItemComponent } from './notifications-list-item.component'; +import { MessageBoardItemDTO } from 'apps/hub/notifications/src/lib/defs'; + +describe('ModalNotificationsListItemComponent', () => { + let spectator: Spectator; + + const createComponent = createComponentFactory({ + component: ModalNotificationsListItemComponent, + imports: [CommonModule], + mocks: [], + }); + + beforeEach(() => { + spectator = createComponent({ props: { item: {} } }); + }); + + it('should create', () => { + expect(spectator.component).toBeTruthy(); + }); + + describe('item input', () => { + it('should display text and headline if available', () => { + spectator.setInput({ item: { text: 'test', headline: 'testHeadline' } }); + expect(spectator.query('h1')).toHaveText('testHeadline'); + expect(spectator.query('.notification-text')).toHaveText('test'); + }); + + it('should not display text and headline if not available', () => { + spectator.setInput({ item: {} }); + expect(spectator.query('h1')).toHaveText(''); + expect(spectator.query('.notification-text')).toHaveText(''); + }); + }); + + describe('notification-edit-cta', () => { + it('should emit itemSelected event with the item after clicking the CTA', () => { + const item: MessageBoardItemDTO = { text: 'Test' }; + spectator.setInput({ item }); + const cta = spectator.query('.notification-edit-cta') as HTMLButtonElement; + let output: MessageBoardItemDTO; + spectator.output('itemSelected').subscribe((result) => (output = result)); + spectator.click(cta); + expect(output).toEqual(item); + }); + }); +}); diff --git a/apps/modal/notifications/src/lib/notifications-remission-group/notifications-remission-group.component.html b/apps/modal/notifications/src/lib/notifications-remission-group/notifications-remission-group.component.html index 15f42e527..a873fa400 100644 --- a/apps/modal/notifications/src/lib/notifications-remission-group/notifications-remission-group.component.html +++ b/apps/modal/notifications/src/lib/notifications-remission-group/notifications-remission-group.component.html @@ -16,7 +16,7 @@
diff --git a/apps/modal/notifications/src/lib/notifications-remission-group/notifications-remission-group.component.spec.ts b/apps/modal/notifications/src/lib/notifications-remission-group/notifications-remission-group.component.spec.ts new file mode 100644 index 000000000..1e9e55b84 --- /dev/null +++ b/apps/modal/notifications/src/lib/notifications-remission-group/notifications-remission-group.component.spec.ts @@ -0,0 +1,101 @@ +import { createComponentFactory, Spectator, SpyObject } from '@ngneat/spectator'; +import { CommonModule } from '@angular/common'; +import { RouterTestingModule } from '@angular/router/testing'; +import { UiIconModule } from '@ui/icon'; +import { Router } from '@angular/router'; +import { UiFilter } from '@ui/filter'; +import { MessageBoardItemDTO } from 'apps/hub/notifications/src/lib/defs'; +import { Component } from '@angular/core'; +import { ModalNotificationsListItemComponent } from '../notifications-list-item/notifications-list-item.component'; +import { ModalNotificationsRemissionGroupComponent } from './notifications-remission-group.component'; + +// DummyComponent Class +@Component({ + selector: 'dummy-component', + template: '
', +}) +class DummyComponent { + constructor() {} +} + +describe('ModalNotificationsRemissionGroupComponent', () => { + let spectator: Spectator; + let uiFilterMock: SpyObject; + let router: Router; + + const createComponent = createComponentFactory({ + component: ModalNotificationsRemissionGroupComponent, + declarations: [ModalNotificationsListItemComponent], + imports: [ + CommonModule, + RouterTestingModule.withRoutes([ + { path: 'filiale/goods/in/results', component: DummyComponent }, + { path: 'filiale/remission/create', component: DummyComponent }, + ]), + UiIconModule, + ], + providers: [], + mocks: [UiFilter], + }); + + beforeEach(() => { + spectator = createComponent({ props: { notifications: [] } }); + router = spectator.inject(Router); + uiFilterMock = spectator.inject(UiFilter); + }); + + it('should create', () => { + expect(spectator.component).toBeTruthy(); + }); + + describe('notifications input', () => { + it('should display the right notification-counter value based on the length of the input array', () => { + spectator.setInput({ notifications: [{}, {}, {}] }); + expect(spectator.query('.notification-counter')).toHaveText('3'); + }); + + it('should not display notification-counter if input array has length 0', () => { + spectator.setInput({ notifications: [] }); + expect(spectator.query('.notification-counter')).toHaveText(''); + }); + + it('should render modal-notifications-list-item based on the input array', () => { + const notifications = [{}, {}]; + spectator.setInput({ notifications }); + spectator.detectComponentChanges(); + expect(spectator.queryAll('modal-notifications-list-item')).toHaveLength(notifications.length); + }); + }); + + describe('itemSelected()', () => { + it('should navigate to results with queryParams from UiFilter.getQueryParamsFromQueryTokenDTO()', () => { + const item: MessageBoardItemDTO = { queryToken: { input: { main_qs: 'test' } } }; + spyOn(UiFilter, 'getQueryParamsFromQueryTokenDTO').and.returnValue(item.queryToken.input); + spyOn(router, 'navigate'); + spectator.component.itemSelected(item); + expect(router.navigate).toHaveBeenCalledWith(['/filiale/goods/in/results'], { queryParams: item.queryToken.input }); + }); + + it('should emit the navigated event after select item', () => { + const item: MessageBoardItemDTO = { queryToken: { input: { main_qs: 'test' } } }; + spyOn(spectator.component.navigated, 'emit'); + spectator.component.itemSelected(item); + expect(spectator.component.navigated.emit).toHaveBeenCalled(); + }); + }); + + describe('actions CTA', () => { + it('should navigate to remission page after clicking the CTA', () => { + const cta = spectator.query('.cta-primary'); + expect(cta).toHaveText('Zur Remission'); + expect(cta).toHaveAttribute('href', '/filiale/remission/create'); + }); + + it('should emit the navigated event after clicking the CTA', () => { + const cta = spectator.query('.cta-primary') as HTMLAnchorElement; + spyOn(spectator.component.navigated, 'emit'); + spectator.click(cta); + expect(spectator.component.navigated.emit).toHaveBeenCalled(); + }); + }); +}); diff --git a/apps/modal/notifications/src/lib/notifications-remission-group/notifications-remission-group.component.ts b/apps/modal/notifications/src/lib/notifications-remission-group/notifications-remission-group.component.ts index 6e84457aa..c2de21dbb 100644 --- a/apps/modal/notifications/src/lib/notifications-remission-group/notifications-remission-group.component.ts +++ b/apps/modal/notifications/src/lib/notifications-remission-group/notifications-remission-group.component.ts @@ -18,9 +18,8 @@ export class ModalNotificationsRemissionGroupComponent { constructor(private _router: Router) {} itemSelected(item: MessageBoardItemDTO) { - console.log('clicked item', item); const queryParams = UiFilter.getQueryParamsFromQueryTokenDTO(item.queryToken); - this._router.navigate(['/goods/in/results'], { queryParams }); + this._router.navigate(['/filiale/goods/in/results'], { queryParams }); this.navigated.emit(); } } diff --git a/apps/modal/notifications/src/lib/notifications-reservation-group/notifications-reservation-group.component.html b/apps/modal/notifications/src/lib/notifications-reservation-group/notifications-reservation-group.component.html index 060c79260..d3bf537dc 100644 --- a/apps/modal/notifications/src/lib/notifications-reservation-group/notifications-reservation-group.component.html +++ b/apps/modal/notifications/src/lib/notifications-reservation-group/notifications-reservation-group.component.html @@ -16,7 +16,7 @@ diff --git a/apps/modal/notifications/src/lib/notifications-reservation-group/notifications-reservation-group.component.spec.ts b/apps/modal/notifications/src/lib/notifications-reservation-group/notifications-reservation-group.component.spec.ts new file mode 100644 index 000000000..b1266b34a --- /dev/null +++ b/apps/modal/notifications/src/lib/notifications-reservation-group/notifications-reservation-group.component.spec.ts @@ -0,0 +1,101 @@ +import { createComponentFactory, Spectator, SpyObject } from '@ngneat/spectator'; +import { CommonModule } from '@angular/common'; +import { ModalNotificationsReservationGroupComponent } from './notifications-reservation-group.component'; +import { RouterTestingModule } from '@angular/router/testing'; +import { UiIconModule } from '@ui/icon'; +import { Router } from '@angular/router'; +import { UiFilter } from '@ui/filter'; +import { MessageBoardItemDTO } from 'apps/hub/notifications/src/lib/defs'; +import { Component } from '@angular/core'; +import { ModalNotificationsListItemComponent } from '../notifications-list-item/notifications-list-item.component'; + +// DummyComponent Class +@Component({ + selector: 'dummy-component', + template: '
', +}) +class DummyComponent { + constructor() {} +} + +describe('ModalNotificationsReservationGroupComponent', () => { + let spectator: Spectator; + let uiFilterMock: SpyObject; + let router: Router; + + const createComponent = createComponentFactory({ + component: ModalNotificationsReservationGroupComponent, + declarations: [ModalNotificationsListItemComponent], + imports: [ + CommonModule, + RouterTestingModule.withRoutes([ + { path: 'filiale/goods/in/results', component: DummyComponent }, + { path: 'filiale/goods/in/reservation', component: DummyComponent }, + ]), + UiIconModule, + ], + providers: [], + mocks: [UiFilter], + }); + + beforeEach(() => { + spectator = createComponent({ props: { notifications: [] } }); + router = spectator.inject(Router); + uiFilterMock = spectator.inject(UiFilter); + }); + + it('should create', () => { + expect(spectator.component).toBeTruthy(); + }); + + describe('notifications input', () => { + it('should display the right notification-counter value based on the length of the input array', () => { + spectator.setInput({ notifications: [{}, {}, {}] }); + expect(spectator.query('.notification-counter')).toHaveText('3'); + }); + + it('should not display notification-counter if input array has length 0', () => { + spectator.setInput({ notifications: [] }); + expect(spectator.query('.notification-counter')).toHaveText(''); + }); + + it('should render modal-notifications-list-item based on the input array', () => { + const notifications = [{}, {}]; + spectator.setInput({ notifications }); + spectator.detectComponentChanges(); + expect(spectator.queryAll('modal-notifications-list-item')).toHaveLength(notifications.length); + }); + }); + + describe('itemSelected()', () => { + it('should navigate to results with queryParams from UiFilter.getQueryParamsFromQueryTokenDTO()', () => { + const item: MessageBoardItemDTO = { queryToken: { input: { main_qs: 'test' } } }; + spyOn(UiFilter, 'getQueryParamsFromQueryTokenDTO').and.returnValue(item.queryToken.input); + spyOn(router, 'navigate'); + spectator.component.itemSelected(item); + expect(router.navigate).toHaveBeenCalledWith(['/filiale/goods/in/results'], { queryParams: item.queryToken.input }); + }); + + it('should emit the navigated event after select item', () => { + const item: MessageBoardItemDTO = { queryToken: { input: { main_qs: 'test' } } }; + spyOn(spectator.component.navigated, 'emit'); + spectator.component.itemSelected(item); + expect(spectator.component.navigated.emit).toHaveBeenCalled(); + }); + }); + + describe('actions CTA', () => { + it('should navigate to reservation page after clicking the CTA', () => { + const cta = spectator.query('.cta-primary'); + expect(cta).toHaveText('Zu den Reservierungen'); + expect(cta).toHaveAttribute('href', '/filiale/goods/in/reservation'); + }); + + it('should emit the navigated event after clicking the CTA', () => { + const cta = spectator.query('.cta-primary') as HTMLAnchorElement; + spyOn(spectator.component.navigated, 'emit'); + spectator.click(cta); + expect(spectator.component.navigated.emit).toHaveBeenCalled(); + }); + }); +}); diff --git a/apps/modal/notifications/src/lib/notifications-reservation-group/notifications-reservation-group.component.ts b/apps/modal/notifications/src/lib/notifications-reservation-group/notifications-reservation-group.component.ts index a7e31064a..be7bd2216 100644 --- a/apps/modal/notifications/src/lib/notifications-reservation-group/notifications-reservation-group.component.ts +++ b/apps/modal/notifications/src/lib/notifications-reservation-group/notifications-reservation-group.component.ts @@ -19,7 +19,7 @@ export class ModalNotificationsReservationGroupComponent { itemSelected(item: MessageBoardItemDTO) { const queryParams = UiFilter.getQueryParamsFromQueryTokenDTO(item.queryToken); - this._router.navigate(['/goods/in/results'], { queryParams }); + this._router.navigate(['/filiale/goods/in/results'], { queryParams }); this.navigated.emit(); } } diff --git a/apps/modal/notifications/src/lib/notifications-task-calendar-group/notifications-task-calendar-group.component.html b/apps/modal/notifications/src/lib/notifications-task-calendar-group/notifications-task-calendar-group.component.html index 4332b9ea6..484a00491 100644 --- a/apps/modal/notifications/src/lib/notifications-task-calendar-group/notifications-task-calendar-group.component.html +++ b/apps/modal/notifications/src/lib/notifications-task-calendar-group/notifications-task-calendar-group.component.html @@ -16,7 +16,7 @@ diff --git a/apps/modal/notifications/src/lib/notifications-task-calendar-group/notifications-task-calendar-group.component.spec.ts b/apps/modal/notifications/src/lib/notifications-task-calendar-group/notifications-task-calendar-group.component.spec.ts new file mode 100644 index 000000000..f52be2cb0 --- /dev/null +++ b/apps/modal/notifications/src/lib/notifications-task-calendar-group/notifications-task-calendar-group.component.spec.ts @@ -0,0 +1,101 @@ +import { createComponentFactory, Spectator, SpyObject } from '@ngneat/spectator'; +import { CommonModule } from '@angular/common'; +import { RouterTestingModule } from '@angular/router/testing'; +import { UiIconModule } from '@ui/icon'; +import { Router } from '@angular/router'; +import { UiFilter } from '@ui/filter'; +import { MessageBoardItemDTO } from 'apps/hub/notifications/src/lib/defs'; +import { Component } from '@angular/core'; +import { ModalNotificationsListItemComponent } from '../notifications-list-item/notifications-list-item.component'; +import { ModalNotificationsTaskCalendarGroupComponent } from './notifications-task-calendar-group.component'; + +// DummyComponent Class +@Component({ + selector: 'dummy-component', + template: '
', +}) +class DummyComponent { + constructor() {} +} + +describe('ModalNotificationsTaskCalendarGroupComponent', () => { + let spectator: Spectator; + let uiFilterMock: SpyObject; + let router: Router; + + const createComponent = createComponentFactory({ + component: ModalNotificationsTaskCalendarGroupComponent, + declarations: [ModalNotificationsListItemComponent], + imports: [ + CommonModule, + RouterTestingModule.withRoutes([ + { path: 'filiale/goods/in/results', component: DummyComponent }, + { path: 'filiale/task-calendar/calendar', component: DummyComponent }, + ]), + UiIconModule, + ], + providers: [], + mocks: [UiFilter], + }); + + beforeEach(() => { + spectator = createComponent({ props: { notifications: [] } }); + router = spectator.inject(Router); + uiFilterMock = spectator.inject(UiFilter); + }); + + it('should create', () => { + expect(spectator.component).toBeTruthy(); + }); + + describe('notifications input', () => { + it('should display the right notification-counter value based on the length of the input array', () => { + spectator.setInput({ notifications: [{}, {}, {}] }); + expect(spectator.query('.notification-counter')).toHaveText('3'); + }); + + it('should not display notification-counter if input array has length 0', () => { + spectator.setInput({ notifications: [] }); + expect(spectator.query('.notification-counter')).toHaveText(''); + }); + + it('should render modal-notifications-list-item based on the input array', () => { + const notifications = [{}, {}]; + spectator.setInput({ notifications }); + spectator.detectComponentChanges(); + expect(spectator.queryAll('modal-notifications-list-item')).toHaveLength(notifications.length); + }); + }); + + describe('itemSelected()', () => { + it('should navigate to results with queryParams from UiFilter.getQueryParamsFromQueryTokenDTO()', () => { + const item: MessageBoardItemDTO = { queryToken: { input: { main_qs: 'test' } } }; + spyOn(UiFilter, 'getQueryParamsFromQueryTokenDTO').and.returnValue(item.queryToken.input); + spyOn(router, 'navigate'); + spectator.component.itemSelected(item); + expect(router.navigate).toHaveBeenCalledWith(['/filiale/goods/in/results'], { queryParams: item.queryToken.input }); + }); + + it('should emit the navigated event after select item', () => { + const item: MessageBoardItemDTO = { queryToken: { input: { main_qs: 'test' } } }; + spyOn(spectator.component.navigated, 'emit'); + spectator.component.itemSelected(item); + expect(spectator.component.navigated.emit).toHaveBeenCalled(); + }); + }); + + describe('actions CTA', () => { + it('should navigate to reservation page after clicking the CTA', () => { + const cta = spectator.query('.cta-primary'); + expect(cta).toHaveText('Zum Tätigkeitskalender'); + expect(cta).toHaveAttribute('href', '/filiale/task-calendar/calendar'); + }); + + it('should emit the navigated event after clicking the CTA', () => { + const cta = spectator.query('.cta-primary') as HTMLAnchorElement; + spyOn(spectator.component.navigated, 'emit'); + spectator.click(cta); + expect(spectator.component.navigated.emit).toHaveBeenCalled(); + }); + }); +}); diff --git a/apps/modal/notifications/src/lib/notifications-task-calendar-group/notifications-task-calendar-group.component.ts b/apps/modal/notifications/src/lib/notifications-task-calendar-group/notifications-task-calendar-group.component.ts index 59e0e98d6..1a011f2ff 100644 --- a/apps/modal/notifications/src/lib/notifications-task-calendar-group/notifications-task-calendar-group.component.ts +++ b/apps/modal/notifications/src/lib/notifications-task-calendar-group/notifications-task-calendar-group.component.ts @@ -19,7 +19,7 @@ export class ModalNotificationsTaskCalendarGroupComponent { itemSelected(item: MessageBoardItemDTO) { const queryParams = UiFilter.getQueryParamsFromQueryTokenDTO(item.queryToken); - this._router.navigate(['/goods/in/results'], { queryParams }); + this._router.navigate(['/filiale/goods/in/results'], { queryParams }); this.navigated.emit(); } } diff --git a/apps/modal/notifications/src/lib/notifications.component.html b/apps/modal/notifications/src/lib/notifications.component.html index 0dfba4c8c..627a95478 100644 --- a/apps/modal/notifications/src/lib/notifications.component.html +++ b/apps/modal/notifications/src/lib/notifications.component.html @@ -5,7 +5,7 @@ *ngIf="notification.group !== (activeCard$ | async)" type="button" class="notification-card" - (click)="changeActiveCard(notification.group)" + (click)="activeCard = notification.group" > {{ notification.group }} diff --git a/apps/modal/notifications/src/lib/notifications.component.spec.ts b/apps/modal/notifications/src/lib/notifications.component.spec.ts new file mode 100644 index 000000000..611a233c2 --- /dev/null +++ b/apps/modal/notifications/src/lib/notifications.component.spec.ts @@ -0,0 +1,105 @@ +import { createComponentFactory, mockProvider, Spectator, SpyObject } from '@ngneat/spectator'; +import { ModalNotificationsComponent } from './notifications.component'; +import { CommonModule } from '@angular/common'; +import { UiModalRef, UiModalService } from '@ui/modal'; +import { of } from 'rxjs'; + +describe('ModalNotificationsComponent', () => { + let spectator: Spectator; + let modalRefMock: SpyObject; + + const createComponent = createComponentFactory({ + component: ModalNotificationsComponent, + imports: [CommonModule], + providers: [mockProvider(UiModalRef, { data: { product: {} } })], + mocks: [UiModalService], + }); + + beforeEach(() => { + spectator = createComponent(); + modalRefMock = spectator.inject(UiModalRef); + }); + + it('should create', () => { + expect(spectator.component).toBeTruthy(); + }); + + describe('close()', () => { + it('should call _modalRef.close()', () => { + spectator.component.close(); + expect(modalRefMock.close).toHaveBeenCalled(); + }); + }); + + describe('get activeCard', () => { + it('should return the activeCard', () => { + spectator.component.activeCard = 'testCard'; + expect(spectator.component.activeCard).toBe('testCard'); + }); + }); + + describe('get activeCard$', () => { + it('should return activeCard$', () => { + spectator.component.activeCard = 'testCard'; + + spectator.component.activeCard$ + .subscribe((c) => { + expect(c).toBe('testCard'); + }) + .unsubscribe(); + }); + }); + + describe('get groupedNotifications', () => { + it('should return the groupedNotifications', () => { + spectator.component.groupedNotifications = [{ group: 'test', items: [] }]; + expect(spectator.component.groupedNotifications).toEqual([{ group: 'test', items: [] }]); + }); + }); + + describe('get groupedNotifications$', () => { + it('should return the groupedNotifications$', () => { + spectator.component.groupedNotifications = [{ group: 'test', items: [] }]; + + spectator.component.groupedNotifications$ + .subscribe((gn) => { + expect(gn).toEqual([{ group: 'test', items: [] }]); + }) + .unsubscribe(); + }); + }); + + describe('activeNotifications$', () => { + it('should return the item with the activeCard within the group', () => { + spectator.component.groupedNotifications = [{ group: 'testCard', items: [{ text: 'testmessage' }] }]; + spectator.component.activeCard = 'testCard'; + spectator.component.activeNotifications$ + .subscribe((an) => { + expect(an).toEqual([{ text: 'testmessage' }]); + }) + .unsubscribe(); + }); + + it('should not return anything if the item with the activeCard is not within the group', () => { + spectator.component.groupedNotifications = [{ group: 'testCard', items: [{ text: 'testmessage' }] }]; + spectator.component.activeCard = 'testCardtest'; + spectator.component.activeNotifications$ + .subscribe((an) => { + expect(an).toBeUndefined(); + }) + .unsubscribe(); + }); + }); + + describe('ngOnInit()', () => { + it('should call patchState with activeCard', () => { + const activeCard = 'test'; + spectator = createComponent({ props: { activeCard, groupedNotifications: [{ group: activeCard, items: [] }] } }); + spyOn(spectator.component, 'patchState'); + spectator.component.ngOnInit(); + expect(spectator.component.patchState).toHaveBeenCalledWith({ + activeCard: [{ group: activeCard, items: [] }].find((_) => true)?.group, + }); + }); + }); +}); diff --git a/apps/modal/notifications/src/lib/notifications.component.ts b/apps/modal/notifications/src/lib/notifications.component.ts index 07fc51adc..20797b398 100644 --- a/apps/modal/notifications/src/lib/notifications.component.ts +++ b/apps/modal/notifications/src/lib/notifications.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, ViewEncapsulation } from '@angular/core'; +import { ChangeDetectionStrategy, Component, OnInit, ViewEncapsulation } from '@angular/core'; import { ComponentStore } from '@ngrx/component-store'; import { Group, groupBy } from '@ui/common'; import { UiModalRef } from '@ui/modal'; @@ -18,19 +18,28 @@ interface ModalNotificationComponentState { changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, }) -export class ModalNotificationsComponent extends ComponentStore { +export class ModalNotificationsComponent extends ComponentStore implements OnInit { set activeCard(activeCard: string) { if (this.activeCard !== activeCard) { this.patchState({ activeCard }); } } + activeCard$ = this.select((s) => s.activeCard); + get activeCard() { + return this.get((s) => s.activeCard); + } + get groupedNotifications() { return this.get((s) => s.groupedNotifications); } groupedNotifications$ = this.select((s) => s.groupedNotifications); + set groupedNotifications(groupedNotifications: Group[]) { + this.patchState({ groupedNotifications }); + } + activeNotifications$ = combineLatest([this.activeCard$, this.groupedNotifications$]).pipe( map(([activeCard, notifications]) => notifications.find((n) => n.group === activeCard)?.items) ); @@ -40,14 +49,13 @@ export class ModalNotificationsComponent extends ComponentStore item.category), }); - this.activeCard = this.groupedNotifications?.find((_) => true)?.group; + } + + ngOnInit() { + this.patchState({ activeCard: this.groupedNotifications?.find((_) => true)?.group }); } close() { this._modalRef.close(); } - - changeActiveCard(card: string) { - this.activeCard = card; - } } diff --git a/apps/modal/notifications/src/test.ts b/apps/modal/notifications/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/modal/notifications/src/test.ts +++ b/apps/modal/notifications/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/modal/printer/src/test.ts b/apps/modal/printer/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/modal/printer/src/test.ts +++ b/apps/modal/printer/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/modal/reorder/src/test.ts b/apps/modal/reorder/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/modal/reorder/src/test.ts +++ b/apps/modal/reorder/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/modal/reviews/src/test.ts b/apps/modal/reviews/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/modal/reviews/src/test.ts +++ b/apps/modal/reviews/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/native-container/src/test.ts b/apps/native-container/src/test.ts index dec5191a9..4d5f0245f 100644 --- a/apps/native-container/src/test.ts +++ b/apps/native-container/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/native-container/tsconfig.lib.json b/apps/native-container/tsconfig.lib.json index 0e0b9c45d..92d8245ea 100644 --- a/apps/native-container/tsconfig.lib.json +++ b/apps/native-container/tsconfig.lib.json @@ -2,6 +2,7 @@ "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "../../out-tsc/lib", + "declarationMap": true, "target": "es2015", "declaration": true, "inlineSources": true, diff --git a/apps/native-container/tsconfig.lib.prod.json b/apps/native-container/tsconfig.lib.prod.json index b1d501abb..2617a83e3 100644 --- a/apps/native-container/tsconfig.lib.prod.json +++ b/apps/native-container/tsconfig.lib.prod.json @@ -1,5 +1,8 @@ { "extends": "./tsconfig.lib.json", + "compilerOptions": { + "declarationMap": false + }, "angularCompilerOptions": { "enableIvy": false } diff --git a/apps/page/catalog/src/lib/article-details/article-details.component.html b/apps/page/catalog/src/lib/article-details/article-details.component.html index 9062aadb2..7ef46ecaa 100644 --- a/apps/page/catalog/src/lib/article-details/article-details.component.html +++ b/apps/page/catalog/src/lib/article-details/article-details.component.html @@ -1,265 +1,272 @@ -
- -
-
-
- + +
-
- - Artikel ist ein Fortsetzungsartikel,
- Artikel muss über eine Aboabteilung
- bestellt werden. -
-
-
- -
-
-
- +
+
+ - -
- -
-
- +
+ {{ item.product?.name }}
- -
-
- {{ item.product?.name }} -
- -
-
-
- - {{ item.product?.formatDetail }} -
-
Band/Reihe {{ item?.product?.volume }}
- -
{{ publicationDate$ | async }}
-
- -
-
- {{ item.catalogAvailability?.price?.value?.value | currency: item.catalogAvailability?.price?.value?.currency:'code' }} -
- -
- {{ takeAwayAvailability?.retailPrice?.value?.value | currency: takeAwayAvailability?.retailPrice?.value?.currency:'code' }} +
+
+
+ + {{ item.product?.formatDetail }}
- -
{{ promotionPoints }} Lesepunkte
+
Band/Reihe {{ item?.product?.volume }}
+ +
{{ publicationDate$ | async }}
+
+ +
+
+ {{ item.catalogAvailability?.price?.value?.value | currency: item.catalogAvailability?.price?.value?.currency:'code' }} +
+ +
+ {{ + takeAwayAvailability?.retailPrice?.value?.value | currency: takeAwayAvailability?.retailPrice?.value?.currency:'code' + }} +
+
+
{{ promotionPoints }} Lesepunkte
+
-
-
-
{{ item.product?.manufacturer }}
+
+
{{ item.product?.manufacturer }}
-
-
- - - - {{ takeAwayAvailability.inStock || 0 }}x - - -
-
-
{{ item?.product?.locale }}
- -
-
{{ item.product?.ean }}
-
-
-
- - - - - - - - - Download - +
+
+ + + + {{ takeAwayAvailability.inStock || 0 }}x +
-
+
{{ item?.product?.locale }}
-
- - - {{ (item?.stockInfos)[0]?.compartment }} - - / -
- - {{ (item?.shelfInfos)[0]?.label }} - -
- - - {{ (item?.stockInfos)[0]?.compartment }} +
+
{{ item.product?.ean }}
+
+
+
+ + + + + + + + + Download + + +
+
+
+ +
+ + + {{ (item?.stockInfos)[0]?.compartment }} + + / +
+ + {{ (item?.shelfInfos)[0]?.label }} +
- - - + + + {{ (item?.stockInfos)[0]?.compartment }} + + + + + {{ (item?.shelfInfos)[0]?.label }} + + +
+ +
+
+ + {{ (item?.specs)[0]?.value }} + +
+
+
+ +
+ {{ sscText }} +
+
+
+
+ +
+ + {{ (item?.stockInfos)[0]?.compartment }} + / +
{{ (item?.shelfInfos)[0]?.label }}
- -
- -
-
- - {{ (item?.specs)[0]?.value }} - -
-
-
- -
- {{ sscText }} -
-
+ + + {{ (item?.stockInfos)[0]?.compartment }} + + + + + {{ (item?.shelfInfos)[0]?.label }} + +
-
- - {{ (item?.stockInfos)[0]?.compartment }} - / -
- {{ (item?.shelfInfos)[0]?.label }} -
- - - {{ (item?.stockInfos)[0]?.compartment }} - - - - - {{ (item?.shelfInfos)[0]?.label }} - - +
+
+ +
+
+ + Artikel ist ein Fortsetzungsartikel,
+ Artikel muss über eine Aboabteilung
+ bestellt werden. +
+
+
+ +
-
-
- - -
- -
- -
- Auch verfügbar als - - - - - format icon - {{ format.product?.formatDetail }} - {{ format.catalogAvailability?.price?.value?.value | currency: '€' }} - - - +
+ +

+ + + +
+
+ +
+
+ {{ item.texts[0].value }} +
+ +
+ +

{{ text.label }}

+ {{ text.value }} +
+ +
+
+
-
-
- {{ item.texts[0].value }} -
- -
- -

{{ text.label }}

- {{ text.value }} -
- -
-
-
-
- - + +
- +
diff --git a/apps/page/catalog/src/lib/article-details/article-details.component.scss b/apps/page/catalog/src/lib/article-details/article-details.component.scss index faf52d4e7..6047b0900 100644 --- a/apps/page/catalog/src/lib/article-details/article-details.component.scss +++ b/apps/page/catalog/src/lib/article-details/article-details.component.scss @@ -4,16 +4,15 @@ .product-card { @apply flex flex-col bg-white w-full rounded-card shadow-card; - height: 100%; - min-height: calc(100vh - 345px); .product-details { @apply flex flex-row p-5; .bookmark { - @apply flex absolute; - right: 37px; - top: 57px; + @apply absolute flex; + top: 52px; + right: 25px; + z-index: 100; } .bookmark-badge { @@ -118,7 +117,7 @@ } .availability-icons { - @apply flex flex-row justify-end text-dark-cerulean; + @apply flex flex-row justify-end text-dark-cerulean mt-4; ui-icon { @apply mx-1; @@ -138,10 +137,6 @@ .download-icon { @apply flex flex-row items-center; - - ui-icon { - @apply inline mt-1; - } } } @@ -166,6 +161,7 @@ .product-description { @apply flex flex-col flex-grow px-5 py-5; + min-height: calc(100vh - 769px); .info { @apply whitespace-pre-line; @@ -174,6 +170,10 @@ .product-text { @apply flex flex-col whitespace-pre-line mb-px-100; + h3 { + @apply my-4; + } + .header { @apply text-regular font-bold; } @@ -209,7 +209,7 @@ } .product-formats { - @apply grid whitespace-nowrap items-center px-5 h-px-40 py-4; + @apply grid whitespace-nowrap items-center px-5 py-4; grid-template-rows: auto; grid-template-columns: auto 1fr; max-width: 100%; @@ -237,7 +237,7 @@ } .product-recommendations { - @apply absolute border-none outline-none bottom-0 left-0 right-0 flex items-center px-5 h-16 bg-white w-full; + @apply sticky bottom-0 border-none outline-none left-0 right-0 flex items-center px-5 h-16 bg-white w-full; box-shadow: #dce2e9 0px -2px 18px 0px; .label { @@ -250,7 +250,9 @@ } .recommendations-overlay { - @apply absolute w-full bottom-0 rounded-t-card; + @apply absolute w-full top-0 rounded-t-card; + top: 56px; + max-width: 916px; .product-button { @apply flex flex-row justify-center items-center w-full text-xl bg-white text-ucla-blue font-bold border-none outline-none bg-transparent rounded-t-card; @@ -260,5 +262,5 @@ } .autor { - @apply text-active-customer !important font-bold no-underline; + @apply text-active-customer font-bold no-underline; } diff --git a/apps/page/catalog/src/lib/article-details/article-details.component.ts b/apps/page/catalog/src/lib/article-details/article-details.component.ts index d8aa7aa59..13ae7bdee 100644 --- a/apps/page/catalog/src/lib/article-details/article-details.component.ts +++ b/apps/page/catalog/src/lib/article-details/article-details.component.ts @@ -25,7 +25,7 @@ import { DatePipe } from '@angular/common'; templateUrl: 'article-details.component.html', styleUrls: ['article-details.component.scss'], changeDetection: ChangeDetectionStrategy.Default, - providers: [ArticleDetailsStore], + providers: [ArticleDetailsStore, DatePipe], animations: [slideYAnimation], }) export class ArticleDetailsComponent implements OnInit, OnDestroy { @@ -88,13 +88,12 @@ export class ArticleDetailsComponent implements OnInit, OnDestroy { ); constructor( - private applicationService: ApplicationService, + public readonly applicationService: ApplicationService, private activatedRoute: ActivatedRoute, public readonly store: ArticleDetailsStore, private domainPrinterService: DomainPrinterService, private uiModal: UiModalService, private productImageService: ProductImageService, - private application: ApplicationService, private breadcrumb: BreadcrumbService, private _dateAdapter: DateAdapter, private _datePipe: DatePipe, @@ -123,7 +122,7 @@ export class ArticleDetailsComponent implements OnInit, OnDestroy { async updateBreadcrumb(item: ItemDTO) { const crumbs = await this.breadcrumb - .getBreadcrumbsByKeyAndTags$(this.application.activatedProcessId, ['catalog', 'details', `${item.id}`]) + .getBreadcrumbsByKeyAndTags$(this.applicationService.activatedProcessId, ['catalog', 'details', `${item.id}`]) .pipe(first()) .toPromise(); @@ -132,9 +131,9 @@ export class ArticleDetailsComponent implements OnInit, OnDestroy { } this.breadcrumb.addBreadcrumbIfNotExists({ - key: this.application.activatedProcessId, + key: this.applicationService.activatedProcessId, name: item.product?.name, - path: `/product/details/${item.id}`, + path: `/kunde/${this.applicationService.activatedProcessId}/product/details/${item.id}`, params: this.activatedRoute.snapshot.queryParams, tags: ['catalog', 'details', `${item.id}`], section: 'customer', @@ -264,4 +263,9 @@ export class ArticleDetailsComponent implements OnInit, OnDestroy { } as PurchasingOptionsModalData, }); } + + scrollTop() { + const element = this.elementRef.nativeElement.closest('.main-wrapper'); + element?.scrollTo({ top: 0, behavior: 'smooth' }); + } } diff --git a/apps/page/catalog/src/lib/article-details/recommendations/article-recommendations.component.html b/apps/page/catalog/src/lib/article-details/recommendations/article-recommendations.component.html index c95174562..8277a295c 100644 --- a/apps/page/catalog/src/lib/article-details/recommendations/article-recommendations.component.html +++ b/apps/page/catalog/src/lib/article-details/recommendations/article-recommendations.component.html @@ -17,10 +17,10 @@ - product-image + product-image {{ recommendation.product?.formatDetail }} {{ recommendation.catalogAvailability?.price?.value?.value | currency: ' ' }} EUR diff --git a/apps/page/catalog/src/lib/article-details/recommendations/article-recommendations.component.scss b/apps/page/catalog/src/lib/article-details/recommendations/article-recommendations.component.scss index 0016395b3..84066c8c8 100644 --- a/apps/page/catalog/src/lib/article-details/recommendations/article-recommendations.component.scss +++ b/apps/page/catalog/src/lib/article-details/recommendations/article-recommendations.component.scss @@ -5,7 +5,7 @@ } h1 { - @apply text-center mt-12; + @apply text-center mt-12 font-bold; font-size: 26px; } @@ -18,8 +18,7 @@ p { @apply mt-10; .label { - @apply flex flex-row items-center uppercase text-dark-cerulean font-bold text-sm; - margin-left: 3.25rem; + @apply flex flex-row items-center uppercase text-dark-cerulean font-bold text-sm ml-6; ui-icon { @apply mr-2; @@ -35,8 +34,8 @@ p { img { @apply rounded-xl; - height: 310px; - width: 190px; + height: 315px; + max-width: 195px; box-shadow: 0 0 15px #949393; } @@ -50,3 +49,7 @@ p { } } } + +ui-slider { + @apply mx-4; +} diff --git a/apps/page/catalog/src/lib/article-details/recommendations/article-recommendations.component.ts b/apps/page/catalog/src/lib/article-details/recommendations/article-recommendations.component.ts index afce9100d..828c0c7c3 100644 --- a/apps/page/catalog/src/lib/article-details/recommendations/article-recommendations.component.ts +++ b/apps/page/catalog/src/lib/article-details/recommendations/article-recommendations.component.ts @@ -1,4 +1,5 @@ import { Component, EventEmitter, Output } from '@angular/core'; +import { ApplicationService } from '@core/application'; import { ArticleDetailsStore } from '../article-details.store'; @Component({ @@ -10,5 +11,5 @@ export class ArticleRecommendationsComponent { @Output() close = new EventEmitter(); - constructor(public readonly store: ArticleDetailsStore) {} + constructor(public readonly store: ArticleDetailsStore, public readonly applicationService: ApplicationService) {} } diff --git a/apps/page/catalog/src/lib/article-search/article-search-new.store.ts b/apps/page/catalog/src/lib/article-search/article-search-new.store.ts deleted file mode 100644 index f53029631..000000000 --- a/apps/page/catalog/src/lib/article-search/article-search-new.store.ts +++ /dev/null @@ -1,343 +0,0 @@ -import { Injectable } from '@angular/core'; -import { Router } from '@angular/router'; -import { BreadcrumbService } from '@core/breadcrumb'; -import { ComponentStore, tapResponse } from '@ngrx/component-store'; -import { combineLatest, Observable, Subject } from 'rxjs'; -import { first, map, tap, switchMap, withLatestFrom, debounceTime } from 'rxjs/operators'; -import { DomainCatalogService } from '@domain/catalog'; -import { - fromInputDto, - mapExpandedFilterOptionsToParams, - mapFilterArrayToStringDictionary, - mapSelectedFilterToParams, - mapSelectedParamsToFilter, -} from './article-search.mappings'; -import { Filter } from '@ui/filter'; -import { ItemDTO, QueryTokenDTO, OrderByDTO } from '@swagger/cat'; - -export interface ArticleSearchState { - processId?: number; - searchState: 'empty' | 'fetching' | ''; - params: { - query?: string; - filter?: string; - main?: string; - inputSelector?: string; - orderBy?: string; - desc?: string; - expanded?: string; - [key: string]: string; - }; - - items?: ItemDTO[]; - hits?: number; -} - -/* tslint:disable member-ordering */ -@Injectable() -export class ArticleSearchStore extends ComponentStore { - readonly onSearch = new Subject<{ clear?: boolean; reload?: boolean }>(); - - private processIdSelector = (s: ArticleSearchState) => s.processId; - get processId() { - return this.get(this.processIdSelector); - } - - private queryParamsQuerySelector = (s: ArticleSearchState) => (s.params?.query?.length ? decodeURI(s.params.query) : ''); - readonly queryParamsQuery$ = this.select(this.queryParamsQuerySelector); - get query() { - return this.get(this.queryParamsQuerySelector); - } - - private itemsSelector = (s: ArticleSearchState) => s.items ?? []; - readonly items$ = this.select(this.itemsSelector); - get items() { - return this.get(this.itemsSelector); - } - - private orderBySelector = (s: ArticleSearchState) => (s.params?.orderBy != undefined ? decodeURI(s.params.orderBy) : undefined); - readonly queryParamsOrderBy$ = this.select(this.orderBySelector); - - private descSelector = (s: ArticleSearchState) => s.params.desc; - readonly queryParamsDesc$ = this.select(this.descSelector); - - private hitsSelector = (s: ArticleSearchState) => s.hits; - readonly hits$ = this.select(this.hitsSelector); - get hits() { - return this.get(this.hitsSelector); - } - - private searchStateSelector = (s: ArticleSearchState) => s.searchState; - readonly searchState$ = this.select(this.searchStateSelector); - - private queryParamsSelector = (s: ArticleSearchState) => - Object.keys(s.params).reduce((dic, key) => ({ ...dic, [key]: s.params[key] }), {} as { [key: string]: string }); - readonly queryParams$ = this.select(this.queryParamsSelector); - get queryParams() { - return this.get(this.queryParamsSelector); - } - - private queryParamsExpandedSelector = (s: ArticleSearchState) => (s.params?.expanded != undefined ? s.params?.expanded : undefined); - readonly queryParamsExpanded$ = this.select(this.queryParamsExpandedSelector); - - private queryParamsFilterSelector = (s: ArticleSearchState) => (s.params?.filter != undefined ? decodeURI(s.params?.filter) : undefined); - readonly queryParamsFilter$ = this.select(this.queryParamsFilterSelector); - - private queryParamsMainSelector = (s: ArticleSearchState) => (s.params?.main != undefined ? decodeURI(s.params?.main) : undefined); - readonly queryParamsMain$ = this.select(this.queryParamsMainSelector); - - private queryParamsInputSelector = (s: ArticleSearchState) => - s.params?.inputSelector != undefined ? decodeURI(s.params?.inputSelector) : undefined; - readonly queryParamsInputSelector$ = this.select(this.queryParamsInputSelector); - - readonly defaultFilter$ = this.catalog.getFilters().pipe( - map((ig) => { - return { - filter: ig.find((g) => g.group === 'filter')?.input.map(fromInputDto), - main: ig.find((g) => g.group === 'main')?.input.map(fromInputDto), - inputSelector: ig.find((g) => g.group === 'input_selector')?.input.map(fromInputDto), - }; - }) - ); - - readonly orderByOptions$ = this.catalog.getOrderBy(); - - readonly filter$ = combineLatest([this.queryParamsFilter$, this.queryParamsExpanded$, this.defaultFilter$]).pipe( - map(([selectedFilters, expanded, defaultFilters]) => mapSelectedParamsToFilter(selectedFilters, defaultFilters.filter, expanded)) - ); - - readonly inputSelectorFilter$ = combineLatest([this.queryParamsInputSelector$, this.defaultFilter$]).pipe( - map(([selectedFilters, defaultFilters]) => mapSelectedParamsToFilter(selectedFilters, defaultFilters.inputSelector)) - ); - - readonly mainFilter$ = combineLatest([this.queryParamsMain$, this.defaultFilter$]).pipe( - map(([selectedFilters, defaultFilters]) => mapSelectedParamsToFilter(selectedFilters, defaultFilters.main)) - ); - - readonly queryTokenFilter$ = combineLatest([this.filter$, this.mainFilter$]).pipe( - map(([filter, mainFilter]) => mapFilterArrayToStringDictionary([...filter, ...mainFilter])) - ); - - readonly queryTokenInput$ = combineLatest([this.queryParamsQuery$, this.inputSelectorFilter$]).pipe( - map(([query, inputSelector]) => { - const dic: { [key: string]: string } = {}; - - inputSelector.forEach((filter) => - filter.options.forEach((option) => { - if (option.selected) { - dic[option.id] = query; - } - }) - ); - - if (Object.keys(dic).length === 0) { - dic['qs'] = query; - } - - return dic; - }) - ); - - readonly orderBy$ = combineLatest([this.queryParamsOrderBy$, this.queryParamsDesc$, this.orderByOptions$]).pipe( - map(([orderBy, desc, orderByOptions]) => orderByOptions.find((opt) => opt?.by === orderBy && !!opt?.desc === (desc === 'true'))) - ); - - readonly queryTokenOrderBy$ = this.orderBy$.pipe(map((orderBy) => (orderBy ? [orderBy] : undefined))); - - readonly queryToken$ = combineLatest([ - this.queryTokenInput$, - this.queryTokenFilter$, - this.queryTokenOrderBy$, - this.queryParamsQuery$, - ]).pipe( - map( - ([input, filter, orderBy, friendlyName]) => - ({ - input, - orderBy, - filter, - returnStockData: false, - friendlyName, - } as QueryTokenDTO) - ) - ); - - constructor(private router: Router, private catalog: DomainCatalogService, private breadcrumb: BreadcrumbService) { - super({ - searchState: '', - params: {}, - }); - } - - search = this.effect((options$: Observable<{ clear?: boolean; reload?: boolean }>) => - options$.pipe( - debounceTime(250), - withLatestFrom(this.queryToken$, this.items$), - tap(([options]) => { - this.setSearchState({ searchState: 'fetching' }); - this.onSearch.next(options); - }), - switchMap(([options, queryToken, items]) => - this.catalog - .search({ - queryToken: { - ...queryToken, - skip: options.clear || options.reload ? 0 : items.length, - take: options.reload ? items.length : 25, - }, - }) - .pipe( - tapResponse( - (res) => { - this.setSearchState({ searchState: '' }); - if (options.clear || options.reload) { - this.patchState({ items: res.result, hits: res.hits }); - if (res.hits > 1) { - this.navigateToResults(); - } else if (res.hits === 1) { - this.navigateToDetails(res.result[0]); - } else { - this.setSearchState({ searchState: 'empty' }); - this.navigateToMain(); - } - } else { - this.patchState({ items: [...items, ...res.result], hits: res.hits }); - } - this.updateBreadcrumbs(); - }, - (err) => {} - ) - ) - ) - ) - ); - - async updateBreadcrumbs() { - const params = this.get(this.queryParamsSelector); - const crumbs = await this.breadcrumb.getBreadcrumbsByKeyAndTags$(this.processId, ['catalog', 'filter']).pipe(first()).toPromise(); - - const { hits, query } = this; - - for (const crumb of crumbs) { - if (crumb.tags.includes('results')) { - this.breadcrumb.patchBreadcrumb(crumb.id, { - params: { ...params }, - name: `${query ? query : 'Alle Artikel'}`, - }); - } else { - this.breadcrumb.patchBreadcrumb(crumb.id, { params: { ...params } }); - } - } - } - - async updateQueryParams() { - const queryParams = this.get(this.queryParamsSelector); - await this.updateBreadcrumbs(); - await this.router.navigate([], { - queryParams, - }); - } - - navigateToResults() { - const path = '/product/search/results'; - if (!this.router.isActive(path, false)) { - this.router.navigate([path], { queryParams: this.queryParams }); - } - } - - navigateToDetails(item: ItemDTO) { - const path = '/product/details'; - if (!this.router.isActive(path, false)) { - this.router.navigate([path, item.id]); - } - } - - navigateToMain() { - const path = '/product/search'; - if (!this.router.isActive(path, true)) { - this.router.navigate([path], { queryParams: this.queryParams }); - } - } - - setQueryParams({ params }: { params: { [key: string]: string } }) { - this.patchState({ params }); - } - - setQuery({ query }: { query: string }) { - const queryParams = this.get(this.queryParamsSelector); - this.patchState({ - params: { - ...queryParams, - query, - }, - }); - - this.updateQueryParams(); - } - - setFilter({ filter }: { filter: Filter[] }) { - const queryParams = this.get(this.queryParamsSelector); - - this.patchState({ - params: { - ...queryParams, - filter: mapSelectedFilterToParams(filter), - expanded: mapExpandedFilterOptionsToParams(filter), - }, - }); - - this.updateQueryParams(); - } - - setInputSelectorFilter({ filter }: { filter: Filter[] }) { - const queryParams = this.get(this.queryParamsSelector); - this.patchState({ - params: { - ...queryParams, - inputSelector: mapSelectedFilterToParams(filter), - }, - }); - - this.updateQueryParams(); - } - - setMainFilter({ filter }: { filter: Filter[] }) { - const queryParams = this.get(this.queryParamsSelector); - this.patchState({ - params: { - ...queryParams, - main: mapSelectedFilterToParams(filter), - }, - }); - - this.updateQueryParams(); - } - - setSearchState({ searchState }: { searchState: 'fetching' | 'empty' | '' }) { - this.patchState({ searchState }); - } - - setItems({ items }: { items: ItemDTO[] }) { - this.patchState({ items }); - } - - setOrderBy({ orderBy }: { orderBy: OrderByDTO }) { - const queryParams = this.get(this.queryParamsSelector); - this.patchState({ - params: { - ...queryParams, - orderBy: orderBy?.by, - desc: !!orderBy ? String(orderBy.desc) : undefined, - }, - }); - - this.updateQueryParams(); - } - - reset() { - this.setState({ - params: {}, - searchState: '', - }); - } -} diff --git a/apps/page/catalog/src/lib/article-search/article-search.component.html b/apps/page/catalog/src/lib/article-search/article-search.component.html index 625adebde..f1542cfc1 100644 --- a/apps/page/catalog/src/lib/article-search/article-search.component.html +++ b/apps/page/catalog/src/lib/article-search/article-search.component.html @@ -1,8 +1,13 @@ - - + + + diff --git a/apps/page/catalog/src/lib/article-search/article-search.component.scss b/apps/page/catalog/src/lib/article-search/article-search.component.scss index e5f50f06c..37e1ebaaa 100644 --- a/apps/page/catalog/src/lib/article-search/article-search.component.scss +++ b/apps/page/catalog/src/lib/article-search/article-search.component.scss @@ -1,11 +1,10 @@ :host { - @apply flex flex-col w-full box-content absolute; + @apply flex flex-col w-full box-content relative; } .filter { - @apply absolute font-sans flex items-center font-bold bg-inactive-customer border-0 text-regular py-px-8 px-px-15 rounded-filter justify-center right-0; + @apply absolute font-sans flex items-center font-bold bg-wild-blue-yonder border-0 text-regular py-px-8 px-px-15 rounded-filter justify-center right-0 -top-12 z-sticky; min-width: 106px; - top: -52px; .label { @apply ml-px-5; @@ -15,8 +14,3 @@ @apply bg-active-customer text-white ml-px-5; } } - -page-article-search-filter { - @apply fixed right-0 bottom-0 left-0 z-fixed; - top: 136px; -} diff --git a/apps/page/catalog/src/lib/article-search/article-search.component.ts b/apps/page/catalog/src/lib/article-search/article-search.component.ts index f4e654d0d..cdde5aaea 100644 --- a/apps/page/catalog/src/lib/article-search/article-search.component.ts +++ b/apps/page/catalog/src/lib/article-search/article-search.component.ts @@ -1,12 +1,10 @@ -import { animate, style, transition, trigger } from '@angular/animations'; import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core'; -import { Router } from '@angular/router'; -import { ApplicationService } from '@core/application'; +import { ActivatedRoute, Router } from '@angular/router'; import { BreadcrumbService } from '@core/breadcrumb'; import { UiFilterAutocompleteProvider, UiFilterScanProvider } from '@ui/filter'; -import { UiFilter } from 'apps/ui/filter/src/lib/next'; -import { Observable, Subscription } from 'rxjs'; -import { filter, first, map } from 'rxjs/operators'; +import { isEqual } from 'lodash'; +import { BehaviorSubject, Observable, Subject } from 'rxjs'; +import { filter, first, map, takeUntil, withLatestFrom } from 'rxjs/operators'; import { ArticleSearchService } from './article-search.store'; import { FocusSearchboxEvent } from './focus-searchbox.event'; import { ArticleSearchMainAutocompleteProvider, ArticleSearchMainScanProviderService } from './providers'; @@ -30,82 +28,76 @@ import { ArticleSearchMainAutocompleteProvider, ArticleSearchMainScanProviderSer }, ], changeDetection: ChangeDetectionStrategy.OnPush, - animations: [ - trigger('slideInOut', [ - transition(':enter', [style({ transform: 'translateX(100%)' }), animate('250ms', style({ transform: 'translateX(0%)' }))]), - transition(':leave', [style({ transform: '*' }), animate('250ms', style({ transform: 'translateX(100%)' }))]), - ]), - ], }) export class ArticleSearchComponent implements OnInit, OnDestroy { - showFilterOverlay = false; + private _onDestroy$ = new Subject(); + private _processId$: Observable; - subscriptions = new Subscription(); - - hasFilter$: Observable = this.articleSearch.filter$.pipe( - filter((filter: UiFilter) => !!filter?.getQueryToken()?.filter), - map((filter: UiFilter) => { - return Object.keys(filter?.getQueryToken()?.filter)?.length !== 0; - }) + initialFilter$ = this._articleSearch.filter$.pipe( + filter((filter) => !!filter), + first() ); + hasFilter$ = this._articleSearch.filter$.pipe( + withLatestFrom(this.initialFilter$), + map(([filter, initialFilter]) => !isEqual(filter?.getQueryParams(), initialFilter?.getQueryParams())) + ); + + filterActive$ = new BehaviorSubject(false); constructor( - private breadcrumb: BreadcrumbService, - private application: ApplicationService, - private router: Router, - private articleSearch: ArticleSearchService + private _breadcrumb: BreadcrumbService, + private _router: Router, + private _articleSearch: ArticleSearchService, + private _activatedRoute: ActivatedRoute ) {} ngOnInit() { - this.removeBreadcrumbs(); - this.subscriptions.add( - this.application.activatedProcessId$.subscribe((processId) => { - this.setBreadcrumb(processId); - }) - ); + this.initProcessId(); + this._activatedRoute.url + .pipe(takeUntil(this._onDestroy$), withLatestFrom(this._activatedRoute.queryParams, this._processId$)) + .subscribe(([_, queryParams, processId]) => { + this.removeBreadcrumbs(processId); + this.addOrUpdateBreadcrumbs(processId, queryParams); + }); - this.articleSearch.searchCompleted.subscribe((s) => { - if (s.searchState === '') { - if (s.hits === 1) { - const item = s.items.find((f) => f); - this.router.navigate(['/product', 'details', item.id]); - } else { - const params = s.filter.getQueryParams(); - this.router.navigate(['/product', 'search', 'results'], { - queryParams: params, - }); + this._articleSearch.searchCompleted + .pipe(takeUntil(this._onDestroy$), withLatestFrom(this._processId$)) + .subscribe(([state, processId]) => { + if (state.searchState === '') { + if (state.hits === 1) { + const item = state.items.find((f) => f); + this._router.navigate(['/kunde', processId, 'product', 'details', item.id]); + } else { + const params = state.filter.getQueryParams(); + this._router.navigate(['/kunde', processId, 'product', 'search', 'results'], { + queryParams: params, + }); + } } - } - }); + }); + } + + initProcessId() { + this._processId$ = this._activatedRoute.parent.data.pipe(map((data) => Number(data.processId))); } ngOnDestroy() { - if (!!this.subscriptions) { - this.subscriptions.unsubscribe(); - } + this._onDestroy$.next(); + this._onDestroy$.complete(); } - async removeBreadcrumbs() { - const checkoutCrumbs = await this.breadcrumb - .getBreadcrumbsByKeyAndTags$(this.application.activatedProcessId, ['checkout']) - .pipe(first()) - .toPromise(); - checkoutCrumbs.forEach(async (crumb) => { - await this.breadcrumb.removeBreadcrumb(crumb.id, true); - }); + async removeBreadcrumbs(processId: number) { + this._breadcrumb.removeBreadcrumbsByKeyAndTags(processId, ['checkout']); } - async setBreadcrumb(processId: number) { - await this.breadcrumb.addBreadcrumbIfNotExists({ + async addOrUpdateBreadcrumbs(processId: number, queryParams: Record) { + await this._breadcrumb.addBreadcrumbIfNotExists({ key: processId, name: 'Artikelsuche', - path: '/product', + path: `/kunde/${processId}/product`, + params: queryParams, tags: ['catalog', 'main'], section: 'customer', }); } - - toggleFilterOverlay() { - this.showFilterOverlay = !this.showFilterOverlay; - } } diff --git a/apps/page/catalog/src/lib/article-search/article-search.module.ts b/apps/page/catalog/src/lib/article-search/article-search.module.ts index 86036b751..b0d0113ba 100644 --- a/apps/page/catalog/src/lib/article-search/article-search.module.ts +++ b/apps/page/catalog/src/lib/article-search/article-search.module.ts @@ -3,23 +3,13 @@ import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; import { UiIconModule } from '@ui/icon'; import { ArticleSearchComponent } from './article-search.component'; -import { FilterChipsModule } from './containers/filter-chips/filter-chips.module'; -import { ArticleSearchboxModule } from './containers/article-searchbox/article-searchbox.module'; import { SearchResultsModule } from './search-results/search-results.module'; import { SearchMainModule } from './search-main/search-main.module'; import { SearchFilterModule } from './search-filter/search-filter.module'; +import { ShellFilterOverlayModule } from '@shell/filter-overlay'; @NgModule({ - imports: [ - CommonModule, - RouterModule, - UiIconModule, - FilterChipsModule, - ArticleSearchboxModule, - SearchResultsModule, - SearchMainModule, - SearchFilterModule, - ], + imports: [CommonModule, RouterModule, UiIconModule, SearchResultsModule, SearchMainModule, SearchFilterModule, ShellFilterOverlayModule], exports: [ArticleSearchComponent], declarations: [ArticleSearchComponent], providers: [], diff --git a/apps/page/catalog/src/lib/article-search/containers/article-searchbox/article-searchbox.component.html b/apps/page/catalog/src/lib/article-search/containers/article-searchbox/article-searchbox.component.html deleted file mode 100644 index 2cf628e14..000000000 --- a/apps/page/catalog/src/lib/article-search/containers/article-searchbox/article-searchbox.component.html +++ /dev/null @@ -1,63 +0,0 @@ -
-
- - - - - - - - - - - - - - - Um Begriffe auszuschließen, geben Sie diese
- mit einem ! oder ~ davor in das Suchfeld ein.
- Um exakt einen Begriff zu suchen, wählen
- Sie „Exakt“ aus.
-
-
-
diff --git a/apps/page/catalog/src/lib/article-search/containers/article-searchbox/article-searchbox.component.scss b/apps/page/catalog/src/lib/article-search/containers/article-searchbox/article-searchbox.component.scss deleted file mode 100644 index 309d66bce..000000000 --- a/apps/page/catalog/src/lib/article-search/containers/article-searchbox/article-searchbox.component.scss +++ /dev/null @@ -1,87 +0,0 @@ -.checked { - @apply text-dark-cerulean border border-solid border-inactive-customer; -} - -.wrapper { - @apply flex flex-col; - - .primary-filter-container { - @apply flex flex-row justify-center mb-8; - } - - page-filter-chip { - @apply mb-1; - } -} - -.searchbox-wrapper { - @apply flex flex-row justify-center items-center; - - ui-searchbox { - @apply ml-0 mr-4; - } -} - -ui-searchbox { - @apply max-w-lg mx-auto w-full; - - input { - caret-color: #f70400; - } - - .no-results-button { - @apply border-none outline-none font-bold right-0 px-4 text-warning whitespace-nowrap bg-white; - font-size: 21px; - } - - [uiSearchboxSearchButton] { - @apply bg-white text-brand; - - &:not(.scan) { - @apply pr-px-25; - } - - &.scan { - @apply bg-brand text-white; - } - - &.hide { - @apply bg-white; - } - - .spin { - @apply bg-white text-ucla-blue; - } - } - - [uiSearchboxClearButton] { - @apply text-inactive-customer; - } -} - -.spin { - @apply animate-spin; -} - -@media (min-width: 1025px) { - ui-checkbox { - @apply text-base; - } -} - -ui-searchbox-autocomplete { - @apply z-dropdown; -} - -.info-tooltip-button { - @apply border-font-customer bg-white rounded-md text-base font-bold text-black; - border-style: outset; - width: 31px; - height: 31px; -} - -page-tooltip { - @apply relative; - right: 31px; - bottom: 37px; -} diff --git a/apps/page/catalog/src/lib/article-search/containers/article-searchbox/article-searchbox.component.ts b/apps/page/catalog/src/lib/article-search/containers/article-searchbox/article-searchbox.component.ts deleted file mode 100644 index 88a9d91a0..000000000 --- a/apps/page/catalog/src/lib/article-search/containers/article-searchbox/article-searchbox.component.ts +++ /dev/null @@ -1,187 +0,0 @@ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - EventEmitter, - Input, - OnInit, - Output, - ViewChild, - OnDestroy, -} from '@angular/core'; -import { EnvironmentService } from '@core/environment'; -import { AutocompleteDTO } from '@swagger/cat'; -import { UiSearchboxAutocompleteComponent } from '@ui/searchbox'; -import { BehaviorSubject, combineLatest, concat, Observable } from 'rxjs'; -import { map, shareReplay, tap, filter, switchMap, debounceTime, distinctUntilChanged } from 'rxjs/operators'; -import { NativeContainerService } from 'native-container'; -import { Subscription } from 'rxjs'; -import { ArticleSearchStore } from '../../article-search-new.store'; -import { DomainCatalogService } from '@domain/catalog'; -import { FocusSearchboxEvent } from '../../focus-searchbox.event'; -import { UiAutofocusDirective } from 'apps/ui/common/src/lib/autofocus.directive'; - -@Component({ - selector: 'page-article-searchbox', - templateUrl: 'article-searchbox.component.html', - styleUrls: ['article-searchbox.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class ArticleSearchboxComponent implements OnInit, OnDestroy { - @ViewChild(UiAutofocusDirective, { read: UiAutofocusDirective, static: false }) autofocus: UiAutofocusDirective; - - @ViewChild(UiSearchboxAutocompleteComponent, { - read: UiSearchboxAutocompleteComponent, - static: false, - }) - autocomplete: UiSearchboxAutocompleteComponent; - readonly autocompleteQuery$ = new BehaviorSubject(''); - autocompleteResult$: Observable = combineLatest([ - this.autocompleteQuery$, - this.store.queryTokenFilter$, - this.store.queryParamsInputSelector$, - ]).pipe( - debounceTime(200), - distinctUntilChanged(), - filter(([query]) => query.trim().length >= 3), - switchMap(([query, fil, inputSelector]) => - this.catalog.searchComplete({ - queryToken: { - filter: fil, - input: query, - take: 5, - type: inputSelector || 'qs', - catalogType: undefined, - }, - }) - ), - tap((response) => { - if (response.hits > 0) { - this.autocomplete.open(); - } else { - this.autocomplete.close(); - } - }), - map((response) => response.result) - ); - - readonly query$ = this.store.queryParamsQuery$.pipe(shareReplay()); - readonly searchState$ = this.store.searchState$; - - isMobile: boolean; - showTooltip = false; - subscriptions = new Subscription(); - - @Output() closeFilterOverlay = new EventEmitter(); - - /* @internal */ - mode$ = new BehaviorSubject<'filter' | 'main'>('main'); - - @Input() - get mode() { - return this.mode$.value; - } - - set mode(value) { - if (this.mode !== value) { - this.mode$.next(value); - } - } - - isFilter$ = this.mode$.pipe( - map((type) => type === 'filter'), - shareReplay() - ); - - isFetching$ = this.searchState$.pipe( - map((searchState) => searchState === 'fetching'), - shareReplay() - ); - - isEmpty$ = this.searchState$.pipe( - map((searchState) => searchState === 'empty'), - shareReplay() - ); - - constructor( - private environmentService: EnvironmentService, - private cdr: ChangeDetectorRef, - private store: ArticleSearchStore, - private nativeContainer: NativeContainerService, - private catalog: DomainCatalogService, - private focusSearchbox: FocusSearchboxEvent - ) {} - - ngOnInit() { - this.detectDevice(); - - this.subscriptions.add( - this.focusSearchbox.subscribe((_) => { - this.autofocus?.focus(); - }) - ); - } - - ngOnDestroy() { - this.subscriptions.unsubscribe(); - } - - startSearch() { - const isNative = this.nativeContainer.isUiWebview().isNative; - const query = this.store.query; - - if (isNative && (query?.length ?? 0) === 0) { - return this.scan(); - } else { - this.store.search({ clear: true }); - - this.subscriptions.add( - this.store.searchState$.subscribe((state) => { - if (state !== 'fetching' && state !== 'empty') { - this.closeFilterOverlay.emit(); - } - }) - ); - } - } - - scan() { - const sub = this.nativeContainer - .openScanner('scanBook') - .pipe(filter((message) => message.status !== 'IN_PROGRESS')) - .subscribe((result) => { - this.store.setQuery({ query: result?.data }); - - this.store.search({ clear: true }); - this.subscriptions.add( - this.store.searchState$.subscribe((state) => { - if (state !== 'fetching' && state !== 'empty') { - this.closeFilterOverlay.emit(); - } - }) - ); - sub?.unsubscribe(); - }); - - this.subscriptions.add(sub); - } - - reset() { - this.store.setSearchState({ searchState: '' }); - this.store.setQuery({ query: '' }); - this.autocomplete.close(); - } - - resetSearchState() { - this.store.setSearchState({ searchState: '' }); - } - - updateQuery(query: string) { - this.store.setQuery({ query }); - } - - async detectDevice() { - this.isMobile = await this.environmentService.isMobile(); - this.cdr.detectChanges(); - } -} diff --git a/apps/page/catalog/src/lib/article-search/containers/article-searchbox/article-searchbox.module.ts b/apps/page/catalog/src/lib/article-search/containers/article-searchbox/article-searchbox.module.ts deleted file mode 100644 index 7e67431ac..000000000 --- a/apps/page/catalog/src/lib/article-search/containers/article-searchbox/article-searchbox.module.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { FormsModule } from '@angular/forms'; -import { UiCommonModule } from '@ui/common'; -import { UiIconModule } from '@ui/icon'; -import { UiInputModule } from '@ui/input'; -import { UiSearchboxModule } from '@ui/searchbox'; -import { UiTooltipModule } from '@ui/tooltip'; - -import { ArticleSearchboxComponent } from './article-searchbox.component'; - -@NgModule({ - imports: [CommonModule, FormsModule, UiIconModule, UiSearchboxModule, UiInputModule, UiCommonModule, UiTooltipModule], - exports: [ArticleSearchboxComponent], - declarations: [ArticleSearchboxComponent], - providers: [], -}) -export class ArticleSearchboxModule {} diff --git a/apps/page/catalog/src/lib/article-search/containers/filter-chips/filter-chips.component.html b/apps/page/catalog/src/lib/article-search/containers/filter-chips/filter-chips.component.html deleted file mode 100644 index 5114f0058..000000000 --- a/apps/page/catalog/src/lib/article-search/containers/filter-chips/filter-chips.component.html +++ /dev/null @@ -1,12 +0,0 @@ -
- - {{ chip.name }} - -
diff --git a/apps/page/catalog/src/lib/article-search/containers/filter-chips/filter-chips.component.scss b/apps/page/catalog/src/lib/article-search/containers/filter-chips/filter-chips.component.scss deleted file mode 100644 index ddf6201e1..000000000 --- a/apps/page/catalog/src/lib/article-search/containers/filter-chips/filter-chips.component.scss +++ /dev/null @@ -1,30 +0,0 @@ -:host { - --filter-chips-background-color: #e9f0f8; -} - -:host.filter { - --filter-chips-background-color: #fff; -} - -.filter-chip-background { - background-color: var(--filter-chips-background-color); -} - -.primary-filter-container { - @apply flex flex-row justify-center mb-8; -} - -.checked { - @apply text-dark-cerulean border border-solid border-inactive-customer; -} - -ui-checkbox { - @apply mx-px-10 px-5 py-4 text-inactive-customer font-bold text-sm whitespace-nowrap; - border-radius: 27px; - border: 1px solid white; - background-color: #e9f0f8; -} - -ui-checkbox.filter { - @apply bg-white; -} diff --git a/apps/page/catalog/src/lib/article-search/containers/filter-chips/filter-chips.component.ts b/apps/page/catalog/src/lib/article-search/containers/filter-chips/filter-chips.component.ts deleted file mode 100644 index 4757ed935..000000000 --- a/apps/page/catalog/src/lib/article-search/containers/filter-chips/filter-chips.component.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Component, Input, OnInit, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core'; -import { Filter } from '@ui/filter'; -import { cloneDeep } from 'lodash'; - -@Component({ - selector: 'page-filter-chips', - templateUrl: 'filter-chips.component.html', - styleUrls: ['filter-chips.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class FilterChipsComponent implements OnInit { - @Output() filterChange = new EventEmitter(); - - @Input() filter: Filter[]; - - constructor() {} - - ngOnInit() {} - - checkFilter(checked: boolean = false, changedFilterChip: Filter) { - const filter = cloneDeep(this.filter); - filter.find((f) => f.key === changedFilterChip.key).options[0].selected = checked; - this.filterChange.emit(filter); - } -} diff --git a/apps/page/catalog/src/lib/article-search/containers/filter-chips/filter-chips.module.ts b/apps/page/catalog/src/lib/article-search/containers/filter-chips/filter-chips.module.ts deleted file mode 100644 index e1efe520e..000000000 --- a/apps/page/catalog/src/lib/article-search/containers/filter-chips/filter-chips.module.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { FormsModule } from '@angular/forms'; -import { UiCheckboxModule } from '@ui/checkbox'; - -import { FilterChipsComponent } from './filter-chips.component'; - -@NgModule({ - imports: [CommonModule, FormsModule, UiCheckboxModule], - exports: [FilterChipsComponent], - declarations: [FilterChipsComponent], - providers: [], -}) -export class FilterChipsModule {} diff --git a/apps/page/catalog/src/lib/article-search/search-filter/search-filter.component.html b/apps/page/catalog/src/lib/article-search/search-filter/search-filter.component.html index 267db2a14..0bbb5cf83 100644 --- a/apps/page/catalog/src/lib/article-search/search-filter/search-filter.component.html +++ b/apps/page/catalog/src/lib/article-search/search-filter/search-filter.component.html @@ -5,7 +5,7 @@
-

Filter

+

Filter

, private readonly _router: Router) {} + constructor( + public ref: UiModalRef, + private readonly _router: Router, + private readonly _applicationService: ApplicationService + ) {} continue() { - this._router.navigate(['/product/search']); + this._router.navigate([`/kunde/${this._applicationService.activatedProcessId}/product/search`]); this.ref.close(); } toCart() { - this._router.navigate(['/cart/review']); + this._router.navigate([`/kunde/${this._applicationService.activatedProcessId}/cart/review`]); this.ref.close(); } } diff --git a/apps/page/catalog/src/lib/article-search/search-results/search-result-item.component.html b/apps/page/catalog/src/lib/article-search/search-results/search-result-item.component.html index 58640dc0c..06445c449 100644 --- a/apps/page/catalog/src/lib/article-search/search-results/search-result-item.component.html +++ b/apps/page/catalog/src/lib/article-search/search-results/search-result-item.component.html @@ -1,4 +1,4 @@ -
+
@@ -6,7 +6,7 @@
diff --git a/apps/page/catalog/src/lib/article-search/search-results/search-result-item.component.ts b/apps/page/catalog/src/lib/article-search/search-results/search-result-item.component.ts index 863515631..d02b8bf0e 100644 --- a/apps/page/catalog/src/lib/article-search/search-results/search-result-item.component.ts +++ b/apps/page/catalog/src/lib/article-search/search-results/search-result-item.component.ts @@ -1,5 +1,6 @@ import { DatePipe } from '@angular/common'; import { Component, ChangeDetectionStrategy, Input, EventEmitter, Output } from '@angular/core'; +import { ApplicationService } from '@core/application'; import { ComponentStore } from '@ngrx/component-store'; import { ItemDTO } from '@swagger/cat'; import { DateAdapter } from '@ui/common'; @@ -17,6 +18,7 @@ export interface SearchResultItemComponentState { templateUrl: 'search-result-item.component.html', styleUrls: ['search-result-item.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, + providers: [DatePipe], }) export class SearchResultItemComponent extends ComponentStore { @Input() @@ -72,7 +74,12 @@ export class SearchResultItemComponent extends ComponentStore - -
- -
+ diff --git a/apps/page/catalog/src/lib/page-catalog.component.scss b/apps/page/catalog/src/lib/page-catalog.component.scss index 7d8b7d789..0bca0607a 100644 --- a/apps/page/catalog/src/lib/page-catalog.component.scss +++ b/apps/page/catalog/src/lib/page-catalog.component.scss @@ -1,9 +1,7 @@ -shell-breadcrumb { - margin-top: -5px; - @apply mb-px-10 pb-px-10; +:host { + @apply block relative; } -.content-container { - max-height: calc(100vh - 267px); - overflow: scroll; +shell-breadcrumb { + @apply sticky z-sticky top-0; } diff --git a/apps/page/catalog/src/test.ts b/apps/page/catalog/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/page/catalog/src/test.ts +++ b/apps/page/catalog/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/page/checkout/src/lib/checkout-dummy/checkout-dummy.component.html b/apps/page/checkout/src/lib/checkout-dummy/checkout-dummy.component.html index 403393597..64626e61e 100644 --- a/apps/page/checkout/src/lib/checkout-dummy/checkout-dummy.component.html +++ b/apps/page/checkout/src/lib/checkout-dummy/checkout-dummy.component.html @@ -66,7 +66,7 @@ - + @@ -75,7 +75,7 @@ - + diff --git a/apps/page/checkout/src/lib/checkout-dummy/checkout-dummy.component.scss b/apps/page/checkout/src/lib/checkout-dummy/checkout-dummy.component.scss index 5bd79bb9b..b5bda2876 100644 --- a/apps/page/checkout/src/lib/checkout-dummy/checkout-dummy.component.scss +++ b/apps/page/checkout/src/lib/checkout-dummy/checkout-dummy.component.scss @@ -88,15 +88,15 @@ } ::ng-deep page-checkout-dummy .datepicker .input-wrapper { - flex-grow: 0 !important; + flex-grow: 0; } ::ng-deep page-checkout-dummy ui-searchbox .ui-searchbox-input { - @apply pl-0 !important; + padding-left: 0 !important; } ::ng-deep page-checkout-dummy ui-searchbox .hint { - @apply mr-10 !important; + @apply mr-10; } ::ng-deep page-checkout-dummy .price .suffix { @@ -106,3 +106,11 @@ ::ng-deep page-checkout-dummy .searchbox-control .input-wrapper { @apply block; } + +::ng-deep page-checkout-dummy .mwst-dropdown .ui-select-options { + max-height: 5.5rem !important; +} + +::ng-deep page-checkout-dummy .supplier-dropdown .ui-select-options { + max-height: 10rem !important; +} diff --git a/apps/page/checkout/src/lib/checkout-dummy/checkout-dummy.component.ts b/apps/page/checkout/src/lib/checkout-dummy/checkout-dummy.component.ts index 061b9a93a..1630e78c6 100644 --- a/apps/page/checkout/src/lib/checkout-dummy/checkout-dummy.component.ts +++ b/apps/page/checkout/src/lib/checkout-dummy/checkout-dummy.component.ts @@ -1,6 +1,7 @@ import { ChangeDetectionStrategy, Component, Inject, OnDestroy, OnInit, Optional } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { Router } from '@angular/router'; +import { ApplicationService } from '@core/application'; import { ItemDTO } from '@swagger/cat'; import { DateAdapter } from '@ui/common'; import { UiFilterScanProvider } from '@ui/filter'; @@ -21,6 +22,7 @@ import { CheckoutDummyStore } from './checkout-dummy.store'; useClass: CheckoutDummyScanProvider, multi: true, }, + FormBuilder, ], changeDetection: ChangeDetectionStrategy.OnPush, }) @@ -55,7 +57,8 @@ export class CheckoutDummyComponent implements OnInit, OnDestroy { private _dateAdapter: DateAdapter, private _modal: UiModalService, private _store: CheckoutDummyStore, - private _ref: UiModalRef + private _ref: UiModalRef, + private readonly _applicationService: ApplicationService ) {} ngOnInit() { @@ -212,9 +215,11 @@ export class CheckoutDummyComponent implements OnInit, OnDestroy { let filter: { [key: string]: string }; if (!customer) { filter = customerFilter; - this._router.navigate(['/customer', 'search'], { queryParams: { customertype: filter.customertype } }); + this._router.navigate(['/kunde', this._applicationService.activatedProcessId, 'customer', 'search'], { + queryParams: { customertype: filter.customertype }, + }); } else { - this._router.navigate(['/cart', 'review']); + this._router.navigate(['/kunde', this._applicationService.activatedProcessId, 'cart', 'review']); } this._ref?.close(); }); @@ -227,9 +232,11 @@ export class CheckoutDummyComponent implements OnInit, OnDestroy { let filter: { [key: string]: string }; if (!customer) { filter = customerFilter; - this._router.navigate(['/customer', 'search'], { queryParams: { customertype: filter.customertype } }); + this._router.navigate(['/kunde', this._applicationService.activatedProcessId, 'customer', 'search'], { + queryParams: { customertype: filter.customertype }, + }); } else { - this._router.navigate(['/cart', 'review']); + this._router.navigate(['/kunde', this._applicationService.activatedProcessId, 'cart', 'review']); } this._ref?.close(); }); diff --git a/apps/page/checkout/src/lib/checkout-review/checkout-review.component.html b/apps/page/checkout/src/lib/checkout-review/checkout-review.component.html index 4f7bcb845..ee1a9203c 100644 --- a/apps/page/checkout/src/lib/checkout-review/checkout-review.component.html +++ b/apps/page/checkout/src/lib/checkout-review/checkout-review.component.html @@ -13,7 +13,7 @@

@@ -59,9 +59,15 @@
-
- -
+
+ +

this.domainCheckoutService.getCustomerFeatures({ processId })) ); + control: FormGroup; + showBillingAddress$ = this.shoppingCartItems$.pipe( takeUntil(this._orderCompleted), withLatestFrom(this.customerFeatures$), @@ -172,6 +176,19 @@ export class CheckoutReviewComponent extends ComponentStore this.domainCheckoutService.getNotificationChannels({ processId })) + ); + + communicationDetails$ = this.applicationService.activatedProcessId$.pipe( + takeUntil(this._orderCompleted), + switchMap((processId) => this.domainCheckoutService.getBuyerCommunicationDetails({ processId })) + ); + + notificationChannelLoading$ = new Subject(); + + showQuantityControlSpinnerItemId: number; quantityError$ = new BehaviorSubject<{ [key: string]: string }>({}); primaryCtaLabel$ = combineLatest([this.payer$, this.shoppingCartItemsWithoutOrderType$]).pipe( @@ -203,15 +220,16 @@ export class CheckoutReviewComponent extends ComponentStore @@ -264,7 +283,7 @@ export class CheckoutReviewComponent extends ComponentStore val | current, 0) as NotificationChannel; + const processId = await this.applicationService.activatedProcessId$.pipe(first()).toPromise(); + const email = control?.notificationChannel?.email; + const mobile = control?.notificationChannel?.mobile; + this.domainCheckoutService.setBuyerCommunicationDetails({ processId, email, mobile }); + this.domainCheckoutService.setNotificationChannels({ + processId, + notificationChannels: (notificationChannel as NotificationChannel) || 0, + }); + } catch (error) { + this.uiModal.open({ content: UiErrorModalComponent, data: error, title: 'Fehler beim setzen des Benachrichtigungskanals' }); + } + + this.notificationChannelLoading$.next(false); + } + openDummyModal(data?: any) { this.uiModal.open({ content: CheckoutDummyComponent, @@ -307,7 +360,7 @@ export class CheckoutReviewComponent extends ComponentStore -
- Benachrichtigung -
-
-
- -
-
-
-
-
- -
diff --git a/apps/page/checkout/src/lib/checkout-review/notification-channels/notification-channels.component.scss b/apps/page/checkout/src/lib/checkout-review/notification-channels/notification-channels.component.scss deleted file mode 100644 index f46a55417..000000000 --- a/apps/page/checkout/src/lib/checkout-review/notification-channels/notification-channels.component.scss +++ /dev/null @@ -1,40 +0,0 @@ -:host { - width: stretch; -} - -.row { - @apply flex flex-row items-center w-full; -} - -.wrapper { - width: inherit; -} - -.label { - @apply font-bold; - min-width: 200px; -} - -.notification-wrapper { - @apply flex flex-row items-center; -} - -.notification { - @apply flex flex-row items-center; -} - -.notification-label { - @apply mx-4; -} - -.icon-notification { - @apply text-font-customer; -} - -.grow { - @apply flex-grow; -} - -.cta-edit { - @apply bg-transparent text-brand font-bold text-lg outline-none border-none; -} diff --git a/apps/page/checkout/src/lib/checkout-review/notification-channels/notification-channels.component.ts b/apps/page/checkout/src/lib/checkout-review/notification-channels/notification-channels.component.ts deleted file mode 100644 index 17e6b0776..000000000 --- a/apps/page/checkout/src/lib/checkout-review/notification-channels/notification-channels.component.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; -import { ApplicationService } from '@core/application'; -import { DomainCheckoutService } from '@domain/checkout'; -import { CrmCustomerService } from '@domain/crm'; -import { CustomerDTO } from '@swagger/crm'; -import { Observable } from 'rxjs'; -import { switchMap, map, tap, filter } from 'rxjs/operators'; -import { NotificationChannelsService } from './notification-channels.service'; - -@Component({ - selector: 'page-notification-channels', - templateUrl: 'notification-channels.component.html', - styleUrls: ['notification-channels.component.scss'], - providers: [NotificationChannelsService], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class NotificationChannelsComponent { - customer$: Observable = this.applicationService.activatedProcessId$.pipe( - switchMap((processId) => this.domainCheckoutService.getBuyer({ processId })), - tap((buyer) => (this.ncs.buyer = buyer)), - filter((buyer) => !!buyer?.source), - switchMap((buyer) => this.customerService.getCustomer(buyer?.source)), - tap((response) => (this.ncs.customer = response.result)), - map((response) => response.result) - ); - - constructor( - private applicationService: ApplicationService, - private domainCheckoutService: DomainCheckoutService, - private customerService: CrmCustomerService, - public ncs: NotificationChannelsService - ) {} -} diff --git a/apps/page/checkout/src/lib/checkout-review/notification-channels/notification-channels.service.ts b/apps/page/checkout/src/lib/checkout-review/notification-channels/notification-channels.service.ts deleted file mode 100644 index e51c73faf..000000000 --- a/apps/page/checkout/src/lib/checkout-review/notification-channels/notification-channels.service.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Injectable } from '@angular/core'; -import { ApplicationService } from '@core/application'; -import { DomainCheckoutService } from '@domain/checkout'; -import { BuyerDTO, NotificationChannel } from '@swagger/checkout'; -import { CustomerDTO } from '@swagger/crm'; -import { BehaviorSubject } from 'rxjs'; -import { switchMap, map, first } from 'rxjs/operators'; - -@Injectable() -export class NotificationChannelsService { - // Display form and checkbox components - checkboxFormActive = new BehaviorSubject(false); - editFormObject: { email: true | undefined; sms: true | undefined }; - - checkboxes = new BehaviorSubject<{ email: true | undefined; sms: true | undefined }>(undefined); - - customer: CustomerDTO; - buyer: BuyerDTO; - hasEmail: true | undefined; - hasMobile: true | undefined; - emailNotification: NotificationChannel; - smsNotification: NotificationChannel; - - notificationChannels$ = this.applicationService.activatedProcessId$.pipe( - switchMap((processId) => this.domainCheckoutService.getNotificationChannels({ processId })) - ); - - notificationChannelEmail$ = this.notificationChannels$.pipe(map((notificationChannels) => (notificationChannels & 1) === 1)); - - notificationChannelMobile$ = this.notificationChannels$.pipe(map((notificationChannels) => (notificationChannels & 2) === 2)); - - constructor(private domainCheckoutService: DomainCheckoutService, private applicationService: ApplicationService) {} - - async checkEmailOrMobileExist() { - this.hasEmail = !!this.customer?.communicationDetails?.email || !!this.buyer?.communicationDetails?.email || undefined; - this.hasMobile = !!this.customer?.communicationDetails?.mobile || !!this.buyer?.communicationDetails?.mobile || undefined; - - if ((await this.notificationChannelEmail$.pipe(first()).toPromise()) && !!this.hasEmail) { - this.emailNotification = 1; - } - if ((await this.notificationChannelMobile$.pipe(first()).toPromise()) && !!this.hasMobile) { - this.smsNotification = 2; - } - - this.editFormObject = { email: this.hasEmail, sms: this.hasMobile }; - } -} diff --git a/apps/page/checkout/src/lib/checkout-review/notification-channels/notification-edit/notification-checkbox.component.html b/apps/page/checkout/src/lib/checkout-review/notification-channels/notification-edit/notification-checkbox.component.html deleted file mode 100644 index c74133991..000000000 --- a/apps/page/checkout/src/lib/checkout-review/notification-channels/notification-edit/notification-checkbox.component.html +++ /dev/null @@ -1,9 +0,0 @@ -
- - E-Mail - - - SMS - -
-
diff --git a/apps/page/checkout/src/lib/checkout-review/notification-channels/notification-edit/notification-checkbox.component.scss b/apps/page/checkout/src/lib/checkout-review/notification-channels/notification-edit/notification-checkbox.component.scss deleted file mode 100644 index 065c602d7..000000000 --- a/apps/page/checkout/src/lib/checkout-review/notification-channels/notification-edit/notification-checkbox.component.scss +++ /dev/null @@ -1,36 +0,0 @@ -:host { - @apply flex flex-row items-center w-full; -} - -form { - @apply flex flex-row; -} - -.checkbox-wrapper { - @apply flex flex-row; -} - -ui-checkbox { - @apply mr-8; -} - -.communication-details { - @apply flex flex-col relative top-6; -} - -.grow { - @apply flex-grow; -} - -.cta-submit { - @apply bg-transparent text-brand font-bold text-lg outline-none border-none; -} - -hr { - height: 2px; - @apply bg-disabled-customer; -} - -.cta-submit:disabled { - @apply text-disabled-branch; -} diff --git a/apps/page/checkout/src/lib/checkout-review/notification-channels/notification-edit/notification-checkbox.component.ts b/apps/page/checkout/src/lib/checkout-review/notification-channels/notification-edit/notification-checkbox.component.ts deleted file mode 100644 index 8c17d65eb..000000000 --- a/apps/page/checkout/src/lib/checkout-review/notification-channels/notification-edit/notification-checkbox.component.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core'; -import { ApplicationService } from '@core/application'; -import { DomainCheckoutService } from '@domain/checkout'; -import { NotificationChannel } from '@swagger/checkout'; -import { first } from 'rxjs/operators'; -import { NotificationChannelsService } from '../notification-channels.service'; - -@Component({ - selector: 'page-notification-checkbox', - templateUrl: 'notification-checkbox.component.html', - styleUrls: ['notification-checkbox.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class NotificationCheckboxComponent implements OnInit { - constructor( - private applicationService: ApplicationService, - private domainCheckoutService: DomainCheckoutService, - public ncs: NotificationChannelsService - ) {} - - ngOnInit() { - this.ncs.checkEmailOrMobileExist(); - this.setInitialNotificationChannel(); - } - - setInitialNotificationChannel() { - if (this.ncs.hasEmail && this.ncs.hasMobile) { - this.ncs.smsNotification = undefined; - this.ncs.emailNotification = 1; - } else if (this.ncs.hasEmail && !this.ncs.hasMobile) { - this.ncs.smsNotification = undefined; - this.ncs.emailNotification = 1; - } else if (!this.ncs.hasEmail && this.ncs.hasMobile) { - this.ncs.emailNotification = undefined; - this.ncs.smsNotification = 2; - } else { - this.ncs.emailNotification = undefined; - this.ncs.smsNotification = undefined; - } - this.onChangeNotificationChannel(); - } - - check(event: true | undefined, option: 'email' | 'sms') { - if (option === 'email') { - this.ncs.editFormObject.email = event; - } - - if (option === 'sms') { - this.ncs.editFormObject.sms = event; - } - - if (event && !this.ncs.hasEmail && option === 'email') { - this.ncs.emailNotification = 1; - this.ncs.checkboxFormActive.next(true); - this.ncs.checkboxes.next(this.ncs.editFormObject); - } else if (event && this.ncs.hasEmail && option === 'email') { - this.ncs.emailNotification = 1; - this.onChangeNotificationChannel(); - } else if (!event && this.ncs.hasEmail && option === 'email') { - this.ncs.emailNotification = undefined; - this.onChangeNotificationChannel(); - } else if (!event && !this.ncs.hasEmail && option === 'email') { - this.ncs.smsNotification = undefined; - this.ncs.checkboxFormActive.next(false); - } - if (event && !this.ncs.hasMobile && option === 'sms') { - this.ncs.smsNotification = 2; - this.ncs.checkboxFormActive.next(true); - this.ncs.checkboxes.next(this.ncs.editFormObject); - } else if (event && this.ncs.hasMobile && option === 'sms') { - this.ncs.smsNotification = 2; - this.onChangeNotificationChannel(); - } else if (!event && this.ncs.hasMobile && option === 'sms') { - this.ncs.smsNotification = undefined; - this.onChangeNotificationChannel(); - } else if (!event && !this.ncs.hasMobile && option === 'sms') { - this.ncs.smsNotification = undefined; - this.ncs.checkboxFormActive.next(false); - } - } - - async onChangeNotificationChannel() { - try { - const processId = await this.applicationService.activatedProcessId$.pipe(first()).toPromise(); - const notificationChannels = this.ncs.emailNotification | this.ncs.smsNotification; - await this.domainCheckoutService.setNotificationChannels({ - processId, - notificationChannels: notificationChannels as NotificationChannel, - }); - } catch (err) { - console.error(err); - } - } -} diff --git a/apps/page/checkout/src/lib/checkout-review/notification-channels/notification-edit/notification-edit.component.html b/apps/page/checkout/src/lib/checkout-review/notification-channels/notification-edit/notification-edit.component.html deleted file mode 100644 index a3e4186da..000000000 --- a/apps/page/checkout/src/lib/checkout-review/notification-channels/notification-edit/notification-edit.component.html +++ /dev/null @@ -1,21 +0,0 @@ -
- -
- - - - -
-
- - - - -
-
- -
diff --git a/apps/page/checkout/src/lib/checkout-review/notification-channels/notification-edit/notification-edit.component.scss b/apps/page/checkout/src/lib/checkout-review/notification-channels/notification-edit/notification-edit.component.scss deleted file mode 100644 index 5b1cfa887..000000000 --- a/apps/page/checkout/src/lib/checkout-review/notification-channels/notification-edit/notification-edit.component.scss +++ /dev/null @@ -1,46 +0,0 @@ -:host { - width: inherit; -} - -form { - @apply flex flex-col items-center; - width: inherit; -} - -hr { - height: 2px; - @apply bg-disabled-customer; - width: inherit; -} - -label { - @apply font-bold mt-2; - min-width: 200px; -} - -.communication-details { - @apply flex flex-col; - width: inherit; -} - -.controls { - @apply flex flex-row items-center mt-5; - width: inherit; -} - -ui-form-control { - width: inherit; - margin-top: 0 !important; -} - -input { - font-size: 16px !important; -} - -.cta-submit { - @apply bg-transparent text-brand font-bold text-lg outline-none border-none self-end mt-6; -} - -.cta-submit:disabled { - @apply text-disabled-branch; -} diff --git a/apps/page/checkout/src/lib/checkout-review/notification-channels/notification-edit/notification-edit.component.ts b/apps/page/checkout/src/lib/checkout-review/notification-channels/notification-edit/notification-edit.component.ts deleted file mode 100644 index 58e807f67..000000000 --- a/apps/page/checkout/src/lib/checkout-review/notification-channels/notification-edit/notification-edit.component.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { ChangeDetectionStrategy, Component, OnInit, ChangeDetectorRef, OnDestroy } from '@angular/core'; -import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { ApplicationService } from '@core/application'; -import { DomainCheckoutService } from '@domain/checkout'; -import { NotificationChannel } from '@swagger/checkout'; -import { validateEmail } from 'apps/page/customer/src/lib/validators/email-validator'; -import { Subscription } from 'rxjs'; -import { first } from 'rxjs/operators'; -import { NotificationChannelsService } from '../notification-channels.service'; - -@Component({ - selector: 'page-notification-edit', - templateUrl: 'notification-edit.component.html', - styleUrls: ['notification-edit.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class NotificationEditComponent implements OnInit, OnDestroy { - checkboxSubscription: Subscription; - control: FormGroup; - constructor( - private applicationService: ApplicationService, - private fb: FormBuilder, - private cdr: ChangeDetectorRef, - private domainCheckoutService: DomainCheckoutService, - public ncs: NotificationChannelsService - ) {} - - ngOnInit() { - this.createForm(); - this.checkboxSubscription = this.ncs.checkboxes.subscribe((checkboxes) => { - if (!!checkboxes.email && !this.ncs.hasEmail) { - this.control?.get('communicationDetails')?.get('email')?.enable(); - } else { - this.control?.get('communicationDetails')?.get('email')?.disable(); - } - if (!!checkboxes.sms && !this.ncs.hasMobile) { - this.control?.get('communicationDetails')?.get('mobile')?.enable(); - } else { - this.control?.get('communicationDetails')?.get('mobile')?.disable(); - } - }); - } - - ngOnDestroy() { - if (!!this.checkboxSubscription) { - this.checkboxSubscription.unsubscribe(); - } - } - - createForm() { - const fb = this.fb; - this.control = fb.group({ - communicationDetails: fb.group({ - email: fb.control(this.ncs.customer?.communicationDetails?.email, [Validators.required, validateEmail]), - mobile: fb.control(this.ncs.customer?.communicationDetails?.mobile, [Validators.required]), - }), - }); - this.control?.get('communicationDetails')?.get('email')?.disable(); - this.control?.get('communicationDetails')?.get('mobile')?.disable(); - this.cdr.markForCheck(); - } - - async submit() { - if (!this.control.valid || !this.control.enabled) { - return; - } - - this.control.disable(); - - try { - const processId = await this.applicationService.activatedProcessId$.pipe(first()).toPromise(); - const email = this.control?.value?.communicationDetails?.email; - const mobile = this.control?.value?.communicationDetails?.mobile; - - if (!this.ncs.hasEmail && !!email) { - this.domainCheckoutService.setBuyerCommunicationDetails({ processId, email }); - this.ncs.hasEmail = true; - } - if (!this.ncs.hasMobile && !!mobile) { - this.domainCheckoutService.setBuyerCommunicationDetails({ processId, mobile }); - this.ncs.hasMobile = true; - } - const notificationChannel = this.ncs.emailNotification | this.ncs.smsNotification; - await this.domainCheckoutService.setNotificationChannels({ - processId, - notificationChannels: notificationChannel as NotificationChannel, - }); - this.control.enable(); - } catch (err) { - this.control.enable(); - console.error(err); - } - this.checkboxSubscription.unsubscribe(); - this.ncs.checkboxFormActive.next(false); - } -} diff --git a/apps/page/checkout/src/lib/checkout-review/shopping-cart-item/shopping-cart-item.component.html b/apps/page/checkout/src/lib/checkout-review/shopping-cart-item/shopping-cart-item.component.html index bfc536b35..28b98c14a 100644 --- a/apps/page/checkout/src/lib/checkout-review/shopping-cart-item/shopping-cart-item.component.html +++ b/apps/page/checkout/src/lib/checkout-review/shopping-cart-item/shopping-cart-item.component.html @@ -1,5 +1,5 @@ @@ -7,7 +7,7 @@
diff --git a/apps/page/checkout/src/lib/checkout-review/shopping-cart-item/shopping-cart-item.component.ts b/apps/page/checkout/src/lib/checkout-review/shopping-cart-item/shopping-cart-item.component.ts index a67141c85..83c786c04 100644 --- a/apps/page/checkout/src/lib/checkout-review/shopping-cart-item/shopping-cart-item.component.ts +++ b/apps/page/checkout/src/lib/checkout-review/shopping-cart-item/shopping-cart-item.component.ts @@ -104,7 +104,7 @@ export class ShoppingCartItemComponent extends ComponentStore
diff --git a/apps/page/checkout/src/lib/checkout-summary/checkout-summary.component.scss b/apps/page/checkout/src/lib/checkout-summary/checkout-summary.component.scss index 5461cfd4b..24bf33c76 100644 --- a/apps/page/checkout/src/lib/checkout-summary/checkout-summary.component.scss +++ b/apps/page/checkout/src/lib/checkout-summary/checkout-summary.component.scss @@ -8,7 +8,7 @@ } .order-header { - @apply text-center text-3xl my-2 mb-2; + @apply text-center text-3xl my-2 mb-2 font-bold; } .order-info { @@ -99,7 +99,7 @@ width: 320px; .info-row { - @apply whitespace-nowrap overflow-ellipsis overflow-hidden; + @apply flex items-center whitespace-nowrap overflow-ellipsis overflow-hidden; } .order-icon { diff --git a/apps/page/checkout/src/lib/checkout-summary/checkout-summary.component.ts b/apps/page/checkout/src/lib/checkout-summary/checkout-summary.component.ts index 46e4634de..9eff6ba1a 100644 --- a/apps/page/checkout/src/lib/checkout-summary/checkout-summary.component.ts +++ b/apps/page/checkout/src/lib/checkout-summary/checkout-summary.component.ts @@ -103,7 +103,7 @@ export class CheckoutSummaryComponent { private omsService: DomainOmsService, private uiModal: UiModalService, private breadcrumb: BreadcrumbService, - private applicationService: ApplicationService, + public applicationService: ApplicationService, private domainPrinterService: DomainPrinterService ) { this.breadcrumb @@ -116,7 +116,7 @@ export class CheckoutSummaryComponent { this.breadcrumb.addBreadcrumbIfNotExists({ key: this.applicationService.activatedProcessId, name: 'Bestellbestätigung', - path: '/cart/review', + path: `/kunde/${this.applicationService.activatedProcessId}/cart/review`, tags: ['checkout', 'cart'], section: 'customer', }); @@ -152,7 +152,7 @@ export class CheckoutSummaryComponent { } } - this.router.navigate(['goods', 'out', 'details', 'order', takeNowOrders[0].orderNumber, 128]); + this.router.navigate(['/kunde', 'goods', 'out', 'details', 'order', takeNowOrders[0].orderNumber, 128]); } catch (e) { console.error(e); } diff --git a/apps/page/checkout/src/lib/modals/purchasing-options-list-modal/list-options.scss b/apps/page/checkout/src/lib/modals/purchasing-options-list-modal/list-options.scss index 22aebfe6c..fa42b0963 100644 --- a/apps/page/checkout/src/lib/modals/purchasing-options-list-modal/list-options.scss +++ b/apps/page/checkout/src/lib/modals/purchasing-options-list-modal/list-options.scss @@ -29,6 +29,10 @@ @apply mt-4 mb-4 border-2 border-solid border-brand text-brand text-cta-l font-bold bg-white rounded-full py-3 px-6; } +p { + @apply my-4; +} + ::ng-deep page-purchasing-options-list-modal ui-branch-dropdown .wrapper { @apply mx-auto; width: 80%; diff --git a/apps/page/checkout/src/lib/modals/purchasing-options-list-modal/purchasing-options-list-item/purchasing-options-list-item.component.scss b/apps/page/checkout/src/lib/modals/purchasing-options-list-modal/purchasing-options-list-item/purchasing-options-list-item.component.scss index 15a91d508..6a1ae6594 100644 --- a/apps/page/checkout/src/lib/modals/purchasing-options-list-modal/purchasing-options-list-item/purchasing-options-list-item.component.scss +++ b/apps/page/checkout/src/lib/modals/purchasing-options-list-modal/purchasing-options-list-item/purchasing-options-list-item.component.scss @@ -78,7 +78,7 @@ } .info-tooltip-button { - @apply border-font-customer bg-white rounded-full text-base font-bold mr-3; + @apply border-font-customer border-solid border-2 bg-white rounded-full text-base font-bold mr-3; border-style: outset; width: 31px; height: 31px; diff --git a/apps/page/checkout/src/lib/modals/purchasing-options-modal/b2b-delivery-option/b2b-delivery-option.component.scss b/apps/page/checkout/src/lib/modals/purchasing-options-modal/b2b-delivery-option/b2b-delivery-option.component.scss index cd07a8cc8..12edba0f5 100644 --- a/apps/page/checkout/src/lib/modals/purchasing-options-modal/b2b-delivery-option/b2b-delivery-option.component.scss +++ b/apps/page/checkout/src/lib/modals/purchasing-options-modal/b2b-delivery-option/b2b-delivery-option.component.scss @@ -4,5 +4,6 @@ } h4 { + @apply font-bold; margin-top: -2px; } diff --git a/apps/page/checkout/src/lib/modals/purchasing-options-modal/delivery-option/delivery-option.component.scss b/apps/page/checkout/src/lib/modals/purchasing-options-modal/delivery-option/delivery-option.component.scss index af61d1d7c..66b02bbf6 100644 --- a/apps/page/checkout/src/lib/modals/purchasing-options-modal/delivery-option/delivery-option.component.scss +++ b/apps/page/checkout/src/lib/modals/purchasing-options-modal/delivery-option/delivery-option.component.scss @@ -3,9 +3,13 @@ } .info-tooltip-button { - @apply border-font-customer bg-white rounded-full text-base font-bold; + @apply border-font-customer border-solid border-2 bg-white rounded-full text-base font-bold; border-style: outset; width: 31px; height: 31px; margin-left: 10px; } + +h4 { + @apply font-bold; +} diff --git a/apps/page/checkout/src/lib/modals/purchasing-options-modal/dig-delivery-option/dig-delivery-option.component.scss b/apps/page/checkout/src/lib/modals/purchasing-options-modal/dig-delivery-option/dig-delivery-option.component.scss index af61d1d7c..a1016c46f 100644 --- a/apps/page/checkout/src/lib/modals/purchasing-options-modal/dig-delivery-option/dig-delivery-option.component.scss +++ b/apps/page/checkout/src/lib/modals/purchasing-options-modal/dig-delivery-option/dig-delivery-option.component.scss @@ -9,3 +9,7 @@ height: 31px; margin-left: 10px; } + +h4 { + @apply font-bold; +} diff --git a/apps/page/checkout/src/lib/modals/purchasing-options-modal/pick-up-option/pick-up-option.component.scss b/apps/page/checkout/src/lib/modals/purchasing-options-modal/pick-up-option/pick-up-option.component.scss index e69de29bb..f06ca392f 100644 --- a/apps/page/checkout/src/lib/modals/purchasing-options-modal/pick-up-option/pick-up-option.component.scss +++ b/apps/page/checkout/src/lib/modals/purchasing-options-modal/pick-up-option/pick-up-option.component.scss @@ -0,0 +1,3 @@ +h4 { + @apply font-bold; +} diff --git a/apps/page/checkout/src/lib/modals/purchasing-options-modal/purchasing-options-modal.component.scss b/apps/page/checkout/src/lib/modals/purchasing-options-modal/purchasing-options-modal.component.scss index 89914bf2a..9c053e9f1 100644 --- a/apps/page/checkout/src/lib/modals/purchasing-options-modal/purchasing-options-modal.component.scss +++ b/apps/page/checkout/src/lib/modals/purchasing-options-modal/purchasing-options-modal.component.scss @@ -3,11 +3,11 @@ } .modal-title { - @apply text-center mt-2 text-xl; + @apply text-center mt-2 text-xl font-bold; } .cta-modify { - @apply self-end bg-transparent text-brand font-bold text-lg outline-none border-none; + @apply self-end bg-transparent text-brand font-bold text-lg outline-none border-none ml-4; } .options-wrapper { diff --git a/apps/page/checkout/src/lib/modals/purchasing-options-modal/purchasing-options-modal.component.ts b/apps/page/checkout/src/lib/modals/purchasing-options-modal/purchasing-options-modal.component.ts index 82051c337..591e179ca 100644 --- a/apps/page/checkout/src/lib/modals/purchasing-options-modal/purchasing-options-modal.component.ts +++ b/apps/page/checkout/src/lib/modals/purchasing-options-modal/purchasing-options-modal.component.ts @@ -332,10 +332,10 @@ export class PurchasingOptionsModalComponent { .toPromise(); if (!!crumbs && crumbs.length > 0) { const queryParams = crumbs[0].params; - this.router.navigate(['/product', 'search', 'results'], { queryParams }); + this.router.navigate(['/kunde', this.application.activatedProcessId, 'product', 'search', 'results'], { queryParams }); } else { // Route back to search if no result page was loaded (f.e. When searching Article and landing directly on details page) - this.router.navigate(['/product', 'search']); + this.router.navigate(['/kunde', this.application.activatedProcessId, 'product', 'search']); } } else if (navigate === 'continue') { // Set filter for navigation to customer search if customer is not set @@ -350,13 +350,15 @@ export class PurchasingOptionsModalComponent { map((res) => res.filter) ) .toPromise(); - this.router.navigate(['/customer', 'search'], { queryParams: { customertype: filter.customertype } }); + this.router.navigate(['/kunde', this.application.activatedProcessId, 'customer', 'search'], { + queryParams: { customertype: filter.customertype }, + }); } else { - this.router.navigate(['/cart', 'review']); + this.router.navigate(['/kunde', this.application.activatedProcessId, 'cart', 'review']); } } else if (navigate === 'add-customer-data') { const upgradeCustomerId = await this.customerId$.pipe(first()).toPromise(); - this.router.navigate(['/customer', 'create', 'webshop'], { + this.router.navigate(['/kunde', this.application.activatedProcessId, 'customer', 'create', 'webshop'], { queryParams: { upgradeCustomerId }, }); } diff --git a/apps/page/checkout/src/lib/modals/purchasing-options-modal/take-away-option/take-away-option.component.scss b/apps/page/checkout/src/lib/modals/purchasing-options-modal/take-away-option/take-away-option.component.scss index e69de29bb..f06ca392f 100644 --- a/apps/page/checkout/src/lib/modals/purchasing-options-modal/take-away-option/take-away-option.component.scss +++ b/apps/page/checkout/src/lib/modals/purchasing-options-modal/take-away-option/take-away-option.component.scss @@ -0,0 +1,3 @@ +h4 { + @apply font-bold; +} diff --git a/apps/page/checkout/src/lib/page-checkout.component.scss b/apps/page/checkout/src/lib/page-checkout.component.scss index 1a76fb77d..56e454d46 100644 --- a/apps/page/checkout/src/lib/page-checkout.component.scss +++ b/apps/page/checkout/src/lib/page-checkout.component.scss @@ -1,4 +1,3 @@ shell-breadcrumb { - margin-top: -5px; - @apply mb-px-10 pb-px-10; + @apply sticky z-sticky top-0; } diff --git a/apps/page/checkout/src/test.ts b/apps/page/checkout/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/page/checkout/src/test.ts +++ b/apps/page/checkout/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/page/customer/karma.conf.js b/apps/page/customer/karma.conf.js index 42d3e1247..1397f6477 100644 --- a/apps/page/customer/karma.conf.js +++ b/apps/page/customer/karma.conf.js @@ -1,5 +1,8 @@ // Karma configuration file, see link for more information // https://karma-runner.github.io/1.0/config/configuration-file.html +const customLaunchers = require('../../../karma/custom-launchers'); +const junitReporter = require('../../../karma/junit-reporter')('page-customer'); +const coverageReporter = require('../../../karma/coverage-reporter')('page-customer'); module.exports = function (config) { config.set({ @@ -9,23 +12,22 @@ module.exports = function (config) { require('karma-jasmine'), require('karma-chrome-launcher'), require('karma-jasmine-html-reporter'), - require('karma-coverage-istanbul-reporter'), + require('karma-coverage'), + require('karma-junit-reporter'), require('@angular-devkit/build-angular/plugins/karma'), ], client: { clearContext: false, // leave Jasmine Spec Runner output visible in browser }, - coverageIstanbulReporter: { - dir: require('path').join(__dirname, '../../../coverage/page/customer'), - reports: ['html', 'lcovonly', 'text-summary'], - fixWebpackSourcePaths: true, - }, + coverageReporter, + junitReporter, reporters: ['progress', 'kjhtml'], port: 9876, colors: true, logLevel: config.LOG_INFO, autoWatch: true, browsers: ['Chrome'], + customLaunchers, singleRun: false, restartOnFileChange: true, }); diff --git a/apps/page/customer/src/lib/customer-create/customer-create-b2b.component.ts b/apps/page/customer/src/lib/customer-create/customer-create-b2b.component.ts index 6c0927bbe..34da1af5a 100644 --- a/apps/page/customer/src/lib/customer-create/customer-create-b2b.component.ts +++ b/apps/page/customer/src/lib/customer-create/customer-create-b2b.component.ts @@ -209,7 +209,7 @@ export class CustomerCreateB2BComponent extends CustomerCreateComponentBase impl throw new Error(response.message); } else { this.removeBreadcrumb(); - this.router.navigate(['/customer', response.result.id]); + this.router.navigate(['/kunde', this.application.activatedProcessId, 'customer', response.result.id]); } } catch (error) { this.setValidationError(error.error?.invalidProperties, this.control); diff --git a/apps/page/customer/src/lib/customer-create/customer-create-branch.component.ts b/apps/page/customer/src/lib/customer-create/customer-create-branch.component.ts index 6d9724c07..8c7133a45 100644 --- a/apps/page/customer/src/lib/customer-create/customer-create-branch.component.ts +++ b/apps/page/customer/src/lib/customer-create/customer-create-branch.component.ts @@ -254,7 +254,7 @@ export class CustomerCreateBranchComponent extends CustomerCreateComponentBase i throw new Error(response.message); } else { this.removeBreadcrumb(); - this.router.navigate(['/customer', response.result.id]); + this.router.navigate(['/kunde', this.application.activatedProcessId, 'customer', response.result.id]); } } catch (error) { this.setValidationError(error.error?.invalidProperties, this.control); diff --git a/apps/page/customer/src/lib/customer-create/customer-create-guest.component.ts b/apps/page/customer/src/lib/customer-create/customer-create-guest.component.ts index 5044b8a8a..5d0b45635 100644 --- a/apps/page/customer/src/lib/customer-create/customer-create-guest.component.ts +++ b/apps/page/customer/src/lib/customer-create/customer-create-guest.component.ts @@ -225,7 +225,7 @@ export class CustomerCreateGuestComponent extends CustomerCreateComponentBase im throw new Error(response.message); } else { this.removeBreadcrumb(); - this.router.navigate(['/customer', response.result.id]); + this.router.navigate(['/kunde', this.application.activatedProcessId, 'customer', response.result.id]); } } catch (error) { this.setValidationError(error.error?.invalidProperties, this.control); diff --git a/apps/page/customer/src/lib/customer-create/customer-create-online.component.ts b/apps/page/customer/src/lib/customer-create/customer-create-online.component.ts index c918320ac..9e0fb0e93 100644 --- a/apps/page/customer/src/lib/customer-create/customer-create-online.component.ts +++ b/apps/page/customer/src/lib/customer-create/customer-create-online.component.ts @@ -260,7 +260,7 @@ export class CustomerCreateOnlineComponent extends CustomerCreateComponentBase i throw new Error(response.message); } else { this.removeBreadcrumb(); - this.router.navigate(['/customer', response.result.id]); + this.router.navigate(['/kunde', this.application.activatedProcessId, 'customer', response.result.id]); } } catch (error) { if (error?.error?.invalidProperties?.Email || error?.error?.invalidProperties?.email) { diff --git a/apps/page/customer/src/lib/customer-create/customer-create.component.scss b/apps/page/customer/src/lib/customer-create/customer-create.component.scss index 641dce937..d721e4592 100644 --- a/apps/page/customer/src/lib/customer-create/customer-create.component.scss +++ b/apps/page/customer/src/lib/customer-create/customer-create.component.scss @@ -7,7 +7,7 @@ } h1 { - @apply m-0 text-center; + @apply m-0 text-center font-bold; font-size: 26px; margin-top: 27px; } diff --git a/apps/page/customer/src/lib/customer-create/customer-create.component.ts b/apps/page/customer/src/lib/customer-create/customer-create.component.ts index 554e87394..cd6f524e7 100644 --- a/apps/page/customer/src/lib/customer-create/customer-create.component.ts +++ b/apps/page/customer/src/lib/customer-create/customer-create.component.ts @@ -74,7 +74,7 @@ export abstract class CustomerCreateComponentBase { this.breadcrumb.addBreadcrumbIfNotExists({ key: this.application.activatedProcessId, name: 'Kundendaten erfassen', - path: `/customer/create/${this.type}`, + path: `/kunde/${this.application.activatedProcessId}/customer/create/${this.type}`, tags: ['customer', 'create'], section: 'customer', params: {}, diff --git a/apps/page/customer/src/lib/customer-create/customer-create.module.ts b/apps/page/customer/src/lib/customer-create/customer-create.module.ts index f3834f61b..287890f14 100644 --- a/apps/page/customer/src/lib/customer-create/customer-create.module.ts +++ b/apps/page/customer/src/lib/customer-create/customer-create.module.ts @@ -15,7 +15,7 @@ import { AddressSelectionModalModule } from '../modals/address-selection-modal/a import { CantAddCustomerToCartModalModule } from '../modals/cant-add-customer-to-cart-modal/cant-add-customer-to-cart.module'; import { UiIconModule } from '@ui/icon'; import { UiCheckboxModule } from '@ui/checkbox'; -import { UiSpinnerModule } from 'apps/ui/spinner/src/lib/ui-spinner.module'; +import { UiSpinnerModule } from '@ui/spinner'; @NgModule({ imports: [ diff --git a/apps/page/customer/src/lib/customer-details/address/address.component.scss b/apps/page/customer/src/lib/customer-details/address/address.component.scss index 8845feae0..7a181f318 100644 --- a/apps/page/customer/src/lib/customer-details/address/address.component.scss +++ b/apps/page/customer/src/lib/customer-details/address/address.component.scss @@ -13,7 +13,7 @@ form { } h1 { - @apply m-0 text-center; + @apply m-0 text-center font-bold; font-size: 26px; margin-top: 27px; } diff --git a/apps/page/customer/src/lib/customer-details/address/payer-create/payer-create.component.ts b/apps/page/customer/src/lib/customer-details/address/payer-create/payer-create.component.ts index 474d3752f..72b66c81f 100644 --- a/apps/page/customer/src/lib/customer-details/address/payer-create/payer-create.component.ts +++ b/apps/page/customer/src/lib/customer-details/address/payer-create/payer-create.component.ts @@ -69,7 +69,7 @@ export abstract class PayerCreateComponent implements OnInit { await this.breadcrumb.addBreadcrumbIfNotExists({ key: this.application.activatedProcessId, name: 'Rechnungsadresse hinzufügen', - path: `customer/${this.customerId}/payer/create/${customerFeature}`, + path: `/kunde/${this.application.activatedProcessId}/customer/${this.customerId}/payer/create/${customerFeature}`, tags: ['customer', 'payer', 'create'], section: 'customer', params: {}, diff --git a/apps/page/customer/src/lib/customer-details/address/payer-edit/payer-edit.component.ts b/apps/page/customer/src/lib/customer-details/address/payer-edit/payer-edit.component.ts index edd6e7bd4..df58a830d 100644 --- a/apps/page/customer/src/lib/customer-details/address/payer-edit/payer-edit.component.ts +++ b/apps/page/customer/src/lib/customer-details/address/payer-edit/payer-edit.component.ts @@ -86,7 +86,7 @@ export abstract class PayerEditComponent implements OnInit { await this.breadcrumb.addBreadcrumbIfNotExists({ key: this.application.activatedProcessId, name: 'Rechnungsadresse bearbeiten', - path: `customer/${this.customerId}/payer/${this.payerId}/edit/${customerFeature}`, + path: `/kunde/${this.application.activatedProcessId}/customer/${this.customerId}/payer/${this.payerId}/edit/${customerFeature}`, tags: ['customer', 'payer', 'edit'], section: 'customer', params: {}, diff --git a/apps/page/customer/src/lib/customer-details/address/shipping-create/shipping-create.component.ts b/apps/page/customer/src/lib/customer-details/address/shipping-create/shipping-create.component.ts index c9100c09e..cee52cf69 100644 --- a/apps/page/customer/src/lib/customer-details/address/shipping-create/shipping-create.component.ts +++ b/apps/page/customer/src/lib/customer-details/address/shipping-create/shipping-create.component.ts @@ -70,7 +70,7 @@ export abstract class ShippingCreateComponent implements OnInit { await this.breadcrumb.addBreadcrumbIfNotExists({ key: this.application.activatedProcessId, name: 'Lieferadresse hinzufügen', - path: `customer/${this.customerId}/shipping/create/${customerFeature}`, + path: `/kunde/${this.application.activatedProcessId}/customer/${this.customerId}/shipping/create/${customerFeature}`, tags: ['customer', 'shipping', 'create'], section: 'customer', params: {}, diff --git a/apps/page/customer/src/lib/customer-details/address/shipping-edit/shipping-edit.component.ts b/apps/page/customer/src/lib/customer-details/address/shipping-edit/shipping-edit.component.ts index 464286f98..1a18b9802 100644 --- a/apps/page/customer/src/lib/customer-details/address/shipping-edit/shipping-edit.component.ts +++ b/apps/page/customer/src/lib/customer-details/address/shipping-edit/shipping-edit.component.ts @@ -88,7 +88,7 @@ export abstract class ShippingEditComponent implements OnInit { await this.breadcrumb.addBreadcrumbIfNotExists({ key: this.application.activatedProcessId, name: 'Lieferadresse bearbeiten', - path: `customer/${this.customerId}/shipping/${this.shippingAddressId}/edit/${customerFeature}`, + path: `/kunde/${this.application.activatedProcessId}/customer/${this.customerId}/shipping/${this.shippingAddressId}/edit/${customerFeature}`, tags: ['customer', 'shipping', 'edit'], section: 'customer', params: {}, diff --git a/apps/page/customer/src/lib/customer-details/customer-card/customer-card.component.html b/apps/page/customer/src/lib/customer-details/customer-card/customer-card.component.html index 33ceb2c6c..28d9533d3 100644 --- a/apps/page/customer/src/lib/customer-details/customer-card/customer-card.component.html +++ b/apps/page/customer/src/lib/customer-details/customer-card/customer-card.component.html @@ -1,8 +1,8 @@ - + Kundendetails - + Bestellungen
diff --git a/apps/page/customer/src/lib/customer-details/customer-card/customer-card.component.ts b/apps/page/customer/src/lib/customer-details/customer-card/customer-card.component.ts index bc4bbcf05..aa36c7d63 100644 --- a/apps/page/customer/src/lib/customer-details/customer-card/customer-card.component.ts +++ b/apps/page/customer/src/lib/customer-details/customer-card/customer-card.component.ts @@ -1,5 +1,6 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; +import { ApplicationService } from '@core/application'; import { CrmCustomerService } from '@domain/crm'; import { BonusCardInfoDTO, CustomerDTO } from '@swagger/crm'; import { Observable } from 'rxjs'; @@ -18,7 +19,11 @@ export class CustomerCardComponent implements OnInit { customerCard$: Observable; partnerCards$: Observable; - constructor(private route: ActivatedRoute, private customerService: CrmCustomerService) {} + constructor( + private route: ActivatedRoute, + private customerService: CrmCustomerService, + public readonly applicationService: ApplicationService + ) {} ngOnInit() { this.customerId$ = this.route.params.pipe(map((p) => Number(p['customerId']))); diff --git a/apps/page/customer/src/lib/customer-details/customer-data-edit/customer-data-edit.component.scss b/apps/page/customer/src/lib/customer-details/customer-data-edit/customer-data-edit.component.scss index 22c4bcc1d..b948301fd 100644 --- a/apps/page/customer/src/lib/customer-details/customer-data-edit/customer-data-edit.component.scss +++ b/apps/page/customer/src/lib/customer-details/customer-data-edit/customer-data-edit.component.scss @@ -6,7 +6,7 @@ @apply bg-white; h1 { - @apply text-center text-card-heading my-16; + @apply text-center text-card-heading my-16 font-bold; } } @@ -29,7 +29,7 @@ form { @apply text-center my-8; button { - @apply rounded-full outline-none p-4 text-cta-l border-brand border-solid px-px-25 py-px-15 mx-4 font-bold; + @apply rounded-full outline-none p-4 text-cta-l border-brand border-solid border-2 px-px-25 py-px-15 mx-4 font-bold; &.btn-cancel { @apply text-brand bg-white; diff --git a/apps/page/customer/src/lib/customer-details/customer-data-edit/customer-data-edit.component.ts b/apps/page/customer/src/lib/customer-details/customer-data-edit/customer-data-edit.component.ts index 945142fc6..b8d97e4b9 100644 --- a/apps/page/customer/src/lib/customer-details/customer-data-edit/customer-data-edit.component.ts +++ b/apps/page/customer/src/lib/customer-details/customer-data-edit/customer-data-edit.component.ts @@ -79,7 +79,7 @@ export abstract class CustomerDataEditComponent implements OnInit { await this.breadcrumb.addBreadcrumbIfNotExists({ key: this.application.activatedProcessId, name: 'Bearbeiten', - path: `customer/${this.customerId}/edit/${customerFeature}`, + path: `/kunde/${this.application.activatedProcessId}/customer/${this.customerId}/edit/${customerFeature}`, tags: ['customer', 'edit'], section: 'customer', params: {}, diff --git a/apps/page/customer/src/lib/customer-details/customer-details.component.html b/apps/page/customer/src/lib/customer-details/customer-details.component.html index 22e1e70b9..13e720362 100644 --- a/apps/page/customer/src/lib/customer-details/customer-details.component.html +++ b/apps/page/customer/src/lib/customer-details/customer-details.component.html @@ -1,8 +1,12 @@ - + Bestellungen - + Kundenkarte
@@ -29,7 +33,7 @@ {{ customer.customerNumber }}
Bearbeiten{{ records?.number }}
BearbeitenHinzufügen
{{ customer | address }} - Bearbeiten + Bearbeiten
{{ defaultPayerAddress }} - Bearbeiten
@@ -85,7 +104,16 @@ Bearbeiten
@@ -99,7 +127,15 @@ Hinzufügen
@@ -114,7 +150,7 @@ Bearbeiten
@@ -125,7 +161,16 @@ Bearbeiten
diff --git a/apps/page/customer/src/lib/customer-details/customer-details.component.scss b/apps/page/customer/src/lib/customer-details/customer-details.component.scss index 83440e09e..94195fa6c 100644 --- a/apps/page/customer/src/lib/customer-details/customer-details.component.scss +++ b/apps/page/customer/src/lib/customer-details/customer-details.component.scss @@ -84,7 +84,7 @@ ui-inline-input { @apply bg-white flex flex-row justify-between p-4 items-center pt-px-40 pb-px-20; h3 { - @apply m-0 text-card-sub; + @apply m-0 text-card-sub font-bold; } } diff --git a/apps/page/customer/src/lib/customer-details/customer-details.component.ts b/apps/page/customer/src/lib/customer-details/customer-details.component.ts index b5fcfbe57..39267222b 100644 --- a/apps/page/customer/src/lib/customer-details/customer-details.component.ts +++ b/apps/page/customer/src/lib/customer-details/customer-details.component.ts @@ -1,11 +1,17 @@ import { HttpErrorResponse } from '@angular/common/http'; import { Component, ChangeDetectionStrategy, OnInit, ChangeDetectorRef } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { ApplicationService, ProcessService } from '@core/application'; +import { ApplicationService } from '@core/application'; import { Breadcrumb, BreadcrumbService } from '@core/breadcrumb'; import { DomainCheckoutService } from '@domain/checkout'; -import { AddressHelper, AssignedPayerHelper, CrmCustomerService } from '@domain/crm'; -import { BuyerDTO, PayerDTO as CheckoutPayerDTO, ShippingAddressDTO as CheckoutShippingAddressDTO } from '@swagger/checkout'; +import { AddressHelper, AssignedPayerHelper, CrmCustomerService, ShippingAddressHelper } from '@domain/crm'; +import { HistoryComponent } from '@modal/history'; +import { + BuyerDTO, + NotificationChannel, + PayerDTO as CheckoutPayerDTO, + ShippingAddressDTO as CheckoutShippingAddressDTO, +} from '@swagger/checkout'; import { AssignedPayerDTO, CustomerDTO, @@ -16,8 +22,6 @@ import { } from '@swagger/crm'; import { UiMessageModalComponent, UiModalService } from '@ui/modal'; import { isResponseArgs } from '@utils/object'; -import { ShippingAddressHelper } from 'apps/domain/crm/src/lib/helpers/shipping-address.helper'; -import { HistoryComponent } from 'apps/modal/history/src/lib/history.component'; import { isBoolean } from 'lodash'; import { Observable, of } from 'rxjs'; import { catchError, first, map, shareReplay, switchMap } from 'rxjs/operators'; @@ -60,8 +64,7 @@ export class CustomerDetailsComponent implements OnInit { private activatedRoute: ActivatedRoute, private customerDetailsService: CrmCustomerService, private breadcrumb: BreadcrumbService, - private application: ApplicationService, - private process: ProcessService, + public application: ApplicationService, private checkoutService: DomainCheckoutService, private router: Router, private modal: UiModalService, @@ -168,7 +171,7 @@ export class CustomerDetailsComponent implements OnInit { this.currentBreadcrumb = await this.breadcrumb.addBreadcrumbIfNotExists({ key: this.application.activatedProcessId, name: 'Kundendetails', - path: `/customer/${customerId}`, + path: `/kunde/${this.application.activatedProcessId}/customer/${customerId}`, tags: ['customer', 'details'], section: 'customer', }); @@ -253,8 +256,8 @@ export class CustomerDetailsComponent implements OnInit { if (!isBoolean(canAddDestination)) { this.modal.open({ content: UiMessageModalComponent, + title: 'Warenkorb kann dem Kunden nicht zugewiesen werden', data: { - title: 'Warenkorb kann dem Kunden nicht zugewiesen werden', message: canAddDestination, }, }); @@ -302,10 +305,9 @@ export class CustomerDetailsComponent implements OnInit { // Set Process Name const isB2b = customer.features.some((s) => s.key === 'b2b'); - this.process.updateName( - this.application.activatedProcessId, - isB2b ? (customer.organisation?.name ? customer.organisation?.name : customer.lastName) : customer.lastName - ); + this.application.patchProcess(this.application.activatedProcessId, { + name: isB2b ? (customer.organisation?.name ? customer.organisation?.name : customer.lastName) : customer.lastName, + }); // Set Buyer For Process this.checkoutService.setBuyer({ @@ -320,7 +322,7 @@ export class CustomerDetailsComponent implements OnInit { this.checkoutService.setNotificationChannels({ processId: this.application.activatedProcessId, - notificationChannels: customer.notificationChannels, + notificationChannels: customer.notificationChannels === (3 as NotificationChannel) ? 1 : customer.notificationChannels, }); // Set Invoice Address If Selected @@ -342,9 +344,9 @@ export class CustomerDetailsComponent implements OnInit { // Navigate To Catalog Or Cart if (await this.cartExists$.pipe(first()).toPromise()) { - this.router.navigate(['/cart/review']); + this.router.navigate(['/kunde', this.application.activatedProcessId, 'cart', 'review']); } else { - this.router.navigate(['/product/search']); + this.router.navigate(['/kunde', this.application.activatedProcessId, 'product', 'search']); } this.showSpinner = false; diff --git a/apps/page/customer/src/lib/customer-details/customer-details.module.ts b/apps/page/customer/src/lib/customer-details/customer-details.module.ts index a263cf6ff..22517f8bd 100644 --- a/apps/page/customer/src/lib/customer-details/customer-details.module.ts +++ b/apps/page/customer/src/lib/customer-details/customer-details.module.ts @@ -28,11 +28,11 @@ import { CustomerOrdersComponent } from './customer-orders/customer-orders.compo import { UiCommonModule } from '@ui/common'; import { CustomerOrderDetailsComponent } from './customer-order-details/customer-order-details.component'; import { CustomerOrderItemCardComponent } from './customer-order-details/order-item-card/customer-order-item-card.component'; -import { UiSpinnerModule } from 'apps/ui/spinner/src/lib/ui-spinner.module'; import { HistoryModule } from '@modal/history'; import { CantSelectGuestModalModule } from '../modals/cant-select-guest/cant-select-guest-modal.module'; import { UiScrollContainerModule } from '@ui/scroll-container'; import { UiTooltipModule } from '@ui/tooltip'; +import { UiSpinnerModule } from '@ui/spinner'; @NgModule({ imports: [ CommonModule, @@ -51,7 +51,6 @@ import { UiTooltipModule } from '@ui/tooltip'; UiSpinnerModule, HistoryModule, CantSelectGuestModalModule, - UiSpinnerModule, ], exports: [CustomerDetailsComponent], declarations: [ diff --git a/apps/page/customer/src/lib/customer-details/customer-order-details/customer-order-details.component.scss b/apps/page/customer/src/lib/customer-details/customer-order-details/customer-order-details.component.scss index 5b45703eb..dc434f017 100644 --- a/apps/page/customer/src/lib/customer-details/customer-order-details/customer-order-details.component.scss +++ b/apps/page/customer/src/lib/customer-details/customer-order-details/customer-order-details.component.scss @@ -74,3 +74,7 @@ .line { @apply bg-customer h-px-2; } + +h2 { + @apply font-bold; +} diff --git a/apps/page/customer/src/lib/customer-details/customer-order-details/customer-order-details.component.ts b/apps/page/customer/src/lib/customer-details/customer-order-details/customer-order-details.component.ts index 499ed86e2..9abf366cd 100644 --- a/apps/page/customer/src/lib/customer-details/customer-order-details/customer-order-details.component.ts +++ b/apps/page/customer/src/lib/customer-details/customer-order-details/customer-order-details.component.ts @@ -42,7 +42,7 @@ export class CustomerOrderDetailsComponent implements OnInit { this.breadcrumb.addBreadcrumbIfNotExists({ key: this.application.activatedProcessId, name: 'Bestelldetails', - path: `/customer/${this.customerId}/order/${this.orderId}`, + path: `/kunde/${this.application.activatedProcessId}/customer/${this.customerId}/order/${this.orderId}`, tags: ['customer', 'order'], section: 'customer', params: {}, diff --git a/apps/page/customer/src/lib/customer-details/customer-orders/customer-orders.component.html b/apps/page/customer/src/lib/customer-details/customer-orders/customer-orders.component.html index fd5139d6a..23d30932f 100644 --- a/apps/page/customer/src/lib/customer-details/customer-orders/customer-orders.component.html +++ b/apps/page/customer/src/lib/customer-details/customer-orders/customer-orders.component.html @@ -1,8 +1,12 @@ - + Kundendetails - + Kundenkarte @@ -28,7 +32,11 @@ {{ order | orderTarget }}
{{ order.orderValue | currency: order.currency:'code' }} | {{ order.itemsCount }} Artikel
- Details + Details
diff --git a/apps/page/customer/src/lib/customer-details/customer-orders/customer-orders.component.scss b/apps/page/customer/src/lib/customer-details/customer-orders/customer-orders.component.scss index 685f053ca..81ed524a7 100644 --- a/apps/page/customer/src/lib/customer-details/customer-orders/customer-orders.component.scss +++ b/apps/page/customer/src/lib/customer-details/customer-orders/customer-orders.component.scss @@ -107,3 +107,7 @@ h1 { top: 66%; left: 42%; } + +h1 { + @apply font-bold; +} diff --git a/apps/page/customer/src/lib/customer-details/customer-orders/customer-orders.component.ts b/apps/page/customer/src/lib/customer-details/customer-orders/customer-orders.component.ts index 69254c20d..fbed3c78d 100644 --- a/apps/page/customer/src/lib/customer-details/customer-orders/customer-orders.component.ts +++ b/apps/page/customer/src/lib/customer-details/customer-orders/customer-orders.component.ts @@ -32,7 +32,7 @@ export class CustomerOrdersComponent implements OnInit, OnDestroy { private activatedRoute: ActivatedRoute, private customerDetailsService: CrmCustomerService, private omsService: DomainOmsService, - private application: ApplicationService, + public application: ApplicationService, private breadcrumb: BreadcrumbService ) {} @@ -70,7 +70,7 @@ export class CustomerOrdersComponent implements OnInit, OnDestroy { this.breadcrumb.addBreadcrumbIfNotExists({ key: this.application.activatedProcessId, name: 'Bestellungen', - path: `/customer/${customer?.id}/orders`, + path: `/kunde/${this.application.activatedProcessId}/customer/${customer?.id}/orders`, tags: ['customer', 'orders'], section: 'customer', params: {}, diff --git a/apps/page/customer/src/lib/customer-details/guards/customer-card.guard.ts b/apps/page/customer/src/lib/customer-details/guards/customer-card.guard.ts index 710c6256c..3b1cee494 100644 --- a/apps/page/customer/src/lib/customer-details/guards/customer-card.guard.ts +++ b/apps/page/customer/src/lib/customer-details/guards/customer-card.guard.ts @@ -1,11 +1,16 @@ import { Injectable } from '@angular/core'; import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router'; +import { ApplicationService } from '@core/application'; import { CrmCustomerService } from '@domain/crm'; import { map, take } from 'rxjs/operators'; @Injectable({ providedIn: 'root' }) export class CustomerCardGuard implements CanActivate { - constructor(private router: Router, private customerService: CrmCustomerService) {} + constructor( + private router: Router, + private customerService: CrmCustomerService, + private readonly _applicationService: ApplicationService + ) {} canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { const id = Number(route.params?.customerId); @@ -14,7 +19,7 @@ export class CustomerCardGuard implements CanActivate { take(1), map((customer) => { if (!customer.result.features.find((feature) => feature.description === 'Kundenkarte')) { - this.router.navigate(['/customer', id]); + this.router.navigate(['/kunde', this._applicationService.activatedProcessId, 'customer', id]); } return true; }) diff --git a/apps/page/customer/src/lib/customer-search/customer-search.component.html b/apps/page/customer/src/lib/customer-search/customer-search.component.html index 410890635..dc7c2a1fd 100644 --- a/apps/page/customer/src/lib/customer-search/customer-search.component.html +++ b/apps/page/customer/src/lib/customer-search/customer-search.component.html @@ -1,22 +1,15 @@ - - + -
-
-
- -
-

- Filter -

- -
-
+ + + diff --git a/apps/page/customer/src/lib/customer-search/customer-search.component.scss b/apps/page/customer/src/lib/customer-search/customer-search.component.scss index 266460b8a..fea19223d 100644 --- a/apps/page/customer/src/lib/customer-search/customer-search.component.scss +++ b/apps/page/customer/src/lib/customer-search/customer-search.component.scss @@ -1,12 +1,10 @@ :host { - @apply flex flex-col w-full box-content absolute; + @apply flex flex-col w-full box-content relative; } .filter { - @apply absolute font-sans flex items-center font-bold bg-wild-blue-yonder border-0 text-regular py-px-8 px-px-15 rounded-filter justify-center; + @apply absolute font-sans flex items-center font-bold bg-wild-blue-yonder border-0 text-regular -top-12 right-0 py-px-8 px-px-15 rounded-filter justify-center z-sticky; - right: 0; - top: -52px; min-width: 106px; .label { @@ -17,34 +15,3 @@ @apply bg-dark-cerulean text-white ml-px-5; } } - -.filter-content { - @apply max-w-content mx-auto mt-px-25 px-px-15; - - .filter-close-right { - @apply pr-px-10 text-right; - } - - .filter-header { - @apply text-center text-page-heading mt-0; - } - - button.filter-close { - @apply border-0 bg-transparent text-ucla-blue; - } -} - -.filter-overlay { - @apply fixed bg-glitter z-fixed; - - transform: translatex(100%); - transition: transform 0.5s ease-out; - top: 135px; - right: 0; - bottom: 0; - left: 0; - - &.active { - transform: translatex(0%); - } -} diff --git a/apps/page/customer/src/lib/customer-search/customer-search.component.ts b/apps/page/customer/src/lib/customer-search/customer-search.component.ts index 3f8b5426d..db39af23f 100644 --- a/apps/page/customer/src/lib/customer-search/customer-search.component.ts +++ b/apps/page/customer/src/lib/customer-search/customer-search.component.ts @@ -1,15 +1,21 @@ -import { Component, ChangeDetectionStrategy, forwardRef, NgZone, OnInit, OnDestroy } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; -import { ApplicationService } from '@core/application'; +import { Component, ChangeDetectionStrategy, OnInit, OnDestroy } from '@angular/core'; import { BreadcrumbService } from '@core/breadcrumb'; -import { EnvironmentService } from '@core/environment'; -import { CrmCustomerService } from '@domain/crm'; -import { FilterOption, UiFilterMappingService } from '@ui/filter'; -import { NativeContainerService } from 'native-container'; -import { BehaviorSubject, Observable, of, Subscription } from 'rxjs'; -import { delay, map, switchMap } from 'rxjs/operators'; -import { CustomerSearch } from './customer-search.service'; -import { isSelectFilterOption } from 'apps/ui/filter/src/lib/type-guards'; +import { BehaviorSubject, Observable, Subject } from 'rxjs'; +import { + SearchComponentStoreService, + SearchState, + SEARCH_STATE_SEARCH_SERVICE, + SEARCH_STATE_SETTINGS_LOADER, +} from '@store/search-component-store'; +import { CustomerSearchStateSettingsLoader } from './services/customer.search-state-settings-loader'; +import { CustomerSearchStateSearchService } from './services/customer.search-state-search-service'; +import { ActivatedRoute, Router } from '@angular/router'; +import { isEqual } from 'lodash'; +import { filter, first, map, takeUntil, withLatestFrom } from 'rxjs/operators'; +import { CustomerInfoDTO } from '@swagger/crm'; +import { UiFilterAutocompleteProvider } from '@ui/filter'; +import { CustomerSearchMainAutocompleteProvider } from './providers/customer-search-main-autocomplete.provider'; +import { UiErrorModalComponent, UiModalService } from '@ui/modal'; @Component({ selector: 'page-customer-search', @@ -17,83 +23,160 @@ import { isSelectFilterOption } from 'apps/ui/filter/src/lib/type-guards'; styleUrls: ['customer-search.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, providers: [ + SearchComponentStoreService, { - provide: CustomerSearch, - useExisting: forwardRef(() => CustomerSearchComponent), + provide: SEARCH_STATE_SETTINGS_LOADER, + useClass: CustomerSearchStateSettingsLoader, + }, + { + provide: SEARCH_STATE_SEARCH_SERVICE, + useClass: CustomerSearchStateSearchService, + }, + { + provide: UiFilterAutocompleteProvider, + useClass: CustomerSearchMainAutocompleteProvider, + multi: true, }, ], }) -export class CustomerSearchComponent extends CustomerSearch implements OnInit, OnDestroy { - filterActive$ = new BehaviorSubject(false); - showMainContent$ = this.getShowMainContent(); +export class CustomerSearchComponent implements OnInit, OnDestroy { + private _onDestroy$ = new Subject(); - subscriptions = new Subscription(); + private _processId$: Observable; - queryFilterHasSelectedOptions$ = this.queryFilter$.pipe( - map((qf) => qf.filters.some(({ options }: { options: FilterOption[] }) => this.hasSelectedOptions(options))) + initialFilter$ = this._store.filter$.pipe( + filter((filter) => !!filter), + first() ); + hasFilter$ = this._store.filter$.pipe( + withLatestFrom(this.initialFilter$), + map(([filter, initialFilter]) => !isEqual(filter?.getQueryParams(), initialFilter?.getQueryParams())) + ); + + filterActive$ = new BehaviorSubject(false); + constructor( - protected customerSearch: CrmCustomerService, - zone: NgZone, - router: Router, - route: ActivatedRoute, - env: EnvironmentService, - filterMapping: UiFilterMappingService, - nativeContainer: NativeContainerService, - application: ApplicationService, - breadcrumb: BreadcrumbService - ) { - super(zone, router, route, application, breadcrumb, env, filterMapping, nativeContainer); - } + private _breadcrumb: BreadcrumbService, + private _store: SearchComponentStoreService, + private _router: Router, + private _activatedRoute: ActivatedRoute, + private _modal: UiModalService + ) {} ngOnInit() { - super.ngOnInit(); - this.subscriptions.add( - this.application.activatedProcessId$.subscribe((processId) => { - this.setBreadcrumb(processId); - }) - ); + this.initProcessId(); + this.initFilter(); } ngOnDestroy() { - this.subscriptions.unsubscribe(); + this._onDestroy$.next(); + this._onDestroy$.complete(); } - async setBreadcrumb(processId: number) { - const crumb = await this.breadcrumb.addBreadcrumbIfNotExists({ - key: processId, - name: 'Kundensuche', - path: '/customer/search', - tags: ['customer', 'search', 'main', 'filter'], - section: 'customer', + initFilter() { + this._activatedRoute.url + .pipe(takeUntil(this._onDestroy$), withLatestFrom(this._activatedRoute.queryParams, this._processId$)) + .subscribe(([_, queryParams, processId]) => { + this.resetFilter(queryParams); + this.addOrUpdateBreadcrumb(processId, queryParams); + }); + + this._store.searchCompleted + .pipe(takeUntil(this._onDestroy$), withLatestFrom(this._processId$)) + .subscribe(([state, processId]) => this.handleSearchCompleted(state, processId)); + } + + async initProcessId() { + this._processId$ = this._activatedRoute.parent.data.pipe(map((data) => Number(data.processId))); + } + + handleSearchCompleted(state: SearchState, processId: number) { + const queryParams = state.filter?.getQueryParams(); + + if (state.hits === 1) { + const customer = state.items[0]; + if (!customer) { + this._modal.open({ content: UiErrorModalComponent, title: 'Kundendatensatz nicht vorhanden' }); + return; + } + + const id = Number(customer?.id); + if (id < 0) { + this.navigateToCreateCustomer(processId, state.items[0]); + } else { + this.navigateToDetails(processId, id); + } + } else if (state.hits > 0) { + this.navigateToResults(processId, queryParams); + } else { + this.updateQueryParamsInCurrentUrl(queryParams); + } + this.patchBradcrumb(processId, queryParams); + } + + resetFilter(queryParams: Record) { + const currentQueryParams = this._store.filter?.getQueryParams(); + if (!isEqual(currentQueryParams, queryParams)) { + this._store.resetFilter(queryParams); + } + } + + navigateToResults(processId: number, queryParams: Record) { + this._router.navigate(['/kunde', processId, 'customer', 'search', 'result'], { + queryParams, }); } - getShowMainContent(animationDelayInMs: number = 500): Observable { - return this.filterActive$.pipe( - switchMap((filterActive) => { - const onExitMainContent = filterActive; - if (onExitMainContent) { - return of(!filterActive).pipe(delay(animationDelayInMs)); - } - - return of(!filterActive); - }) + navigateToCreateCustomer(processId: number, customer: CustomerInfoDTO) { + const customerCreateQueryParams = { + gender: customer?.gender, + title: customer?.title, + firstName: customer?.firstName, + lastName: customer?.lastName, + email: customer?.communicationDetails?.email, + phone: customer?.communicationDetails?.phone, + mobile: customer?.communicationDetails?.mobile, + street: customer?.address?.street, + streetNumber: customer?.address?.streetNumber, + zipCode: customer?.address?.zipCode, + city: customer?.address?.city, + country: customer?.address?.country, + info: customer?.address?.info, + name: customer?.organisation?.name, + department: customer?.organisation?.department, + vatId: customer?.organisation?.vatId, + dateOfBirth: customer?.dateOfBirth, + card: encodeURIComponent(JSON.stringify(customer?.features[0])), + }; + this._router.navigate( + ['/kunde', processId, 'customer', 'create', customer?.features?.find((feature) => feature.key === 'webshop') ? 'webshop' : 'store'], + { + queryParams: customerCreateQueryParams, + } ); } - hasSelectedOptions(options: FilterOption[] = []) { - return options.some((option) => { - if (option.selected) { - return true; - } + navigateToDetails(processId: number, customerId: number) { + this._router.navigate(['/kunde', processId, 'customer', customerId]); + } - if (isSelectFilterOption(option)) { - return this.hasSelectedOptions(option.options); - } - - return false; + addOrUpdateBreadcrumb(processId: number, queryParams: Record) { + this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({ + key: processId, + name: 'Kundensuche', + path: `/kunde/${processId}/customer/search`, + params: queryParams, + section: 'customer', + tags: ['customer', 'filter', 'main'], }); } + + patchBradcrumb(processId: number, queryParams: Record) { + this._breadcrumb.patchBreadcrumbByKeyAndTags(processId, ['customer', 'filter'], { params: queryParams }); + } + + updateQueryParamsInCurrentUrl(queryParams: Record) { + this._router.navigate([], { queryParams }); + } } diff --git a/apps/page/customer/src/lib/customer-search/customer-search.module.ts b/apps/page/customer/src/lib/customer-search/customer-search.module.ts index 62490e80a..55edec42f 100644 --- a/apps/page/customer/src/lib/customer-search/customer-search.module.ts +++ b/apps/page/customer/src/lib/customer-search/customer-search.module.ts @@ -10,11 +10,22 @@ import { CustomerSearchResultComponent } from './search-results'; import { UiCommonModule } from '@ui/common'; import { CustomerResultCardComponent } from './search-results/customer-result-card/customer-result-card.component'; import { CustomerSearchFilterComponent } from './search-filter/search-filter.component'; -import { CustomerSearchboxComponent } from './shared/customer-searchbox'; -import { UiFilterModule } from '@ui/filter'; +import { UiFilterNextModule } from '@ui/filter'; +import { ShellFilterOverlayModule } from '@shell/filter-overlay'; +import { UiScrollContainerModule } from '@ui/scroll-container'; @NgModule({ - imports: [CommonModule, RouterModule, UiSearchboxModule, UiCommonModule, UiIconModule, UiFilterModule, ReactiveFormsModule], + imports: [ + CommonModule, + RouterModule, + UiSearchboxModule, + UiCommonModule, + UiIconModule, + ReactiveFormsModule, + ShellFilterOverlayModule, + UiFilterNextModule, + UiScrollContainerModule, + ], exports: [CustomerSearchComponent, CustomerSearchMainComponent, CustomerSearchResultComponent], declarations: [ CustomerSearchComponent, @@ -22,7 +33,6 @@ import { UiFilterModule } from '@ui/filter'; CustomerSearchResultComponent, CustomerResultCardComponent, CustomerSearchFilterComponent, - CustomerSearchboxComponent, ], }) export class CustomerSearchModule {} diff --git a/apps/page/customer/src/lib/customer-search/customer-search.service.spec.ts b/apps/page/customer/src/lib/customer-search/customer-search.service.spec.ts deleted file mode 100644 index fdefc1ced..000000000 --- a/apps/page/customer/src/lib/customer-search/customer-search.service.spec.ts +++ /dev/null @@ -1,233 +0,0 @@ -import { forwardRef, NgZone } from '@angular/core'; -import { fakeAsync, TestBed, tick } from '@angular/core/testing'; -import { Router } from '@angular/router'; -import { RouterTestingModule } from '@angular/router/testing'; -import { ApplicationService } from '@core/application'; -import { BreadcrumbService } from '@core/breadcrumb'; -import { EnvironmentService } from '@core/environment'; -import { CrmCustomerService } from '@domain/crm'; -import { PagedResult } from '@domain/defs'; -import { UiFilterMappingService } from '@ui/filter'; -import { NativeContainerService } from 'native-container'; -import { BehaviorSubject, of } from 'rxjs'; -import { filter, take } from 'rxjs/operators'; -import { CustomerSearchComponent } from './customer-search.component'; -import { CustomerSearch } from './customer-search.service'; -import { CustomerSearchType } from './defs'; - -function customerSearchCompFn(crmService, zone, router, env, filterMapping, nativeContainer, application, breadcrumb) { - return new CustomerSearchComponent(crmService, zone, router, env, filterMapping, nativeContainer, application, breadcrumb); -} - -class MockCrmCustomerService { - getCustomers() {} - complete() {} -} - -class MockNativeContainer { - openScanner() {} -} - -class MockApplicationService { - get activatedProcessId() { - return 123; - } -} - -class MockBreadcrumbService { - getBreadcrumbsByKeyAndTags$() { - return of([]); - } - patchBreadcrumb() {} -} - -describe('CustomerSearch', () => { - let service: CustomerSearch; - let customerSearch: CrmCustomerService; - let nativeService: NativeContainerService; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [RouterTestingModule], - providers: [ - RouterTestingModule, - { provide: CrmCustomerService, useClass: MockCrmCustomerService }, - { provide: NativeContainerService, useClass: MockNativeContainer }, - { provide: ApplicationService, useClass: MockApplicationService }, - { provide: BreadcrumbService, useClass: MockBreadcrumbService }, - { - provide: CustomerSearchComponent, - useFactory: customerSearchCompFn, - deps: [ - CrmCustomerService, - NgZone, - Router, - EnvironmentService, - UiFilterMappingService, - NativeContainerService, - ApplicationService, - BreadcrumbService, - ], - }, - { - provide: CustomerSearch, - useExisting: forwardRef(() => CustomerSearchComponent), - }, - ], - }).compileComponents(); - - service = TestBed.inject(CustomerSearch); - customerSearch = TestBed.inject(CrmCustomerService); - nativeService = TestBed.inject(NativeContainerService); - - service['customerSearch'] = customerSearch; - service.autocompleteResult$ = new BehaviorSubject([]); - }); - - it('should be created', () => { - expect(service instanceof CustomerSearch).toBeTruthy(); - }); - - describe('addLoadingProducts', () => { - it('should add ten products with property loading', () => { - const mockPagesResult: PagedResult = { result: [] }; - const result = service['addLoadingProducts'](mockPagesResult, 10); - - expect(result.result.length).toEqual(10); - }); - }); - describe('removeLoadingProducts', () => { - it('should remove all products withload loaded set to true', () => { - const mockPagesResult: PagedResult = { - result: [{ loaded: true }, { loaded: true }, { loaded: false }, { loaded: true }], - }; - const result = service['removeLoadingProducts'](mockPagesResult); - - expect(result.result.length).toEqual(3); - }); - }); - - describe('shouldFetchNewProducts', () => { - it('should return if it is a new search', () => { - const result = service['shouldFetchNewProducts']({ isNewSearch: true }); - - expect(result).toBeTruthy(); - }); - - it('should return true if total results is zero', () => { - spyOnProperty(service as any, 'totalResults', 'get').and.returnValue(0); - const result = service['shouldFetchNewProducts']({ isNewSearch: true }); - - expect(result).toBeTruthy(); - }); - - it('should return true if the number of results fetched is smaller than the totalResults', () => { - spyOnProperty(service as any, 'totalResults', 'get').and.returnValue(60); - spyOnProperty(service as any, 'numberOfResultsFetched', 'get').and.returnValue(30); - - const result = service['shouldFetchNewProducts']({ - isNewSearch: false, - take: 10, - }); - - expect(result).toBeTruthy(); - }); - - it('should return false if the number of results fetched plus take a larger than the totalResults', () => { - spyOnProperty(service as any, 'totalResults', 'get').and.returnValue(40); - spyOnProperty(service as any, 'numberOfResultsFetched', 'get').and.returnValue(41); - - const result = service['shouldFetchNewProducts']({ - isNewSearch: false, - take: 10, - }); - - expect(result).toBeFalsy(); - }); - }); - - describe('startSearch', () => { - beforeEach(() => { - spyOn(service, 'search').and.callFake(() => {}); - spyOn(service, 'scan').and.callFake(() => {}); - }); - it('should call search on desktop device', () => { - spyOn(service['environmentService'], 'isMobile').and.returnValue(false); - service.queryFilter$.next({ query: 'Unit Test', filters: [] }); - - service.startSearch(); - expect(service.scan).not.toHaveBeenCalled(); - expect(service.search).toHaveBeenCalled(); - }); - - it('should call scan on mobile device with empty input', () => { - service.queryFilter$.next({ query: '', filters: [] }); - spyOn(service['environmentService'], 'isMobile').and.returnValue(true); - - service.startSearch(); - expect(service.scan).toHaveBeenCalled(); - expect(service.search).not.toHaveBeenCalled(); - }); - - it('should call search on mobile device with non-empty input', () => { - service.queryFilter$.next({ query: 'Unit Test', filters: [] }); - spyOn(service['environmentService'], 'isMobile').and.returnValue(true); - - service.startSearch(); - expect(service.scan).not.toHaveBeenCalled(); - expect(service.search).toHaveBeenCalled(); - }); - }); - - describe('search', () => { - beforeEach(() => { - spyOn(customerSearch, 'getCustomers').and.callFake(() => of({ result: [] })); - spyOn(service, 'navigateToDetails').and.callFake(() => {}); - spyOn(service, 'navigateToResults').and.callFake(() => {}); - }); - it('should return immediately if no new products need to be fetched', fakeAsync(async () => { - spyOn(service as any, 'shouldFetchNewProducts').and.returnValue(false); - - service.search(); - service.searchResult$.next({ hits: 100, result: [] }); - await service.searchResult$.pipe(take(1)).toPromise(); - - tick(10); - - expect(customerSearch.getCustomers).not.toHaveBeenCalled(); - })); - it('should call getCustomers', fakeAsync(async () => { - spyOn(service as any, 'shouldFetchNewProducts').and.returnValue(true); - - service.search(); - service.searchResult$.next({ hits: 100, result: [] }); - await service.searchResult$.pipe(take(1)).toPromise(); - - tick(10); - - expect(customerSearch.getCustomers).toHaveBeenCalled(); - })); - }); - describe('scan', () => { - it('it should call the open method on the native container service', async () => { - spyOn(service, 'search').and.callFake(() => {}); - spyOn(service, 'setQuery').and.callFake(() => {}); - spyOn(service, 'scan').and.callThrough(); - spyOn(nativeService, 'openScanner').and.callFake(() => of({ data: 'Test Scan String' })); - - service.scan(); - await nativeService - .openScanner('scanCustomer') - .pipe( - filter((message) => message.status !== 'IN_PROGRESS'), - take(1) - ) - .toPromise(); - - expect(nativeService.openScanner).toHaveBeenCalled(); - - expect(service.setQuery).toHaveBeenCalledWith('Test Scan String'); - expect(service.search).toHaveBeenCalled(); - }); - }); -}); diff --git a/apps/page/customer/src/lib/customer-search/customer-search.service.ts b/apps/page/customer/src/lib/customer-search/customer-search.service.ts deleted file mode 100644 index a9ea19833..000000000 --- a/apps/page/customer/src/lib/customer-search/customer-search.service.ts +++ /dev/null @@ -1,529 +0,0 @@ -import { HttpErrorResponse } from '@angular/common/http'; -import { Injectable, NgZone, OnDestroy, OnInit } from '@angular/core'; -import { Router, ActivatedRoute } from '@angular/router'; -import { EnvironmentService } from '@core/environment'; -import { CrmCustomerService } from '@domain/crm'; -import { PagedResult } from '@domain/defs'; -import { AutocompleteDTO, CustomerInfoDTO, ListResponseArgsOfCustomerInfoDTO } from '@swagger/crm'; -import { BehaviorSubject, Observable, of, Subject } from 'rxjs'; -import { catchError, debounceTime, distinctUntilChanged, filter, map, shareReplay, switchMap, take, takeUntil, tap } from 'rxjs/operators'; -import { CustomerSearchType, QueryFilter, ResultState } from './defs'; -import { cloneFilter, Filter, FilterOption, UiFilterMappingService } from '@ui/filter'; -import { NativeContainerService } from 'native-container'; -import { ApplicationService } from '@core/application'; -import { BreadcrumbService } from '@core/breadcrumb'; - -@Injectable() -export abstract class CustomerSearch implements OnInit, OnDestroy { - protected abstract customerSearch: CrmCustomerService; - - private queryParams: { [key: string]: string }; - - private destroy$ = new Subject(); - - private _processId: number; - - get processId(): number { - return this._processId; - } - - set processId(processId: number) { - this._processId = processId; - } - - filtersLoaded$ = new BehaviorSubject(false); - - initQueryFilter$ = new BehaviorSubject<{ initialFilter: Filter[] }>({ - initialFilter: [], - }); - - queryFilter$ = new BehaviorSubject({ - query: '', - filters: [], - }); - - queryFilterEmpty$ = this.queryFilter$.pipe( - map((qf) => { - return !qf.query && Object.entries(this.getSelecteFiltersAsDictionary()).length === 0; - }) - ); - - get initQueryFilter() { - return this.initQueryFilter$.value; - } - - get queryFilter() { - return this.queryFilter$.value; - } - - protected filterActive$ = new BehaviorSubject(false); - - autocompleteResult$: Observable; - - inputChange$ = new Subject(); - - public searchResult$ = new BehaviorSubject>({ - result: [], - }); - - public searchState$ = new BehaviorSubject('init'); - - get searchState(): ResultState { - return this.searchState$.value; - } - - public hits$ = new BehaviorSubject(undefined); - - public get hits(): number { - return this.hits$.value; - } - - public set hits(hits: number) { - this.hits$.next(hits); - } - - get searchResult(): PagedResult { - return this.searchResult$.value; - } - - private get numberOfResultsFetched(): number { - if (!this.searchResult$ || !this.searchResult$.value || !Array.isArray(this.searchResult$.value.result)) { - return 0; - } - - return this.searchResult$.value.result.length; - } - - constructor( - private zone: NgZone, - private router: Router, - private route: ActivatedRoute, - public application: ApplicationService, - public breadcrumb: BreadcrumbService, - private environmentService: EnvironmentService, - private filterMapping: UiFilterMappingService, - private nativeContainer: NativeContainerService - ) {} - - ngOnInit() { - this.initAvailableFilters(); - this.initAutocomplete(); - this.initStatusRefreshOnReset(); - } - - ngOnDestroy() { - this.destroy$.next(); - this.destroy$.complete(); - } - - setQueryParams({ params }: { params: { [key: string]: string } }): void { - this.queryParams = params; - this.parseQueryParams(); - } - - getQueryParams(): { [key: string]: string } { - return this.queryParams || {}; - } - - parseQueryParams() { - const params = this.getQueryParams(); - - this.setQuery(params.query); - - const filters = this.queryFilter.filters.map(cloneFilter); - filters.forEach((f) => { - const values = params[f.key]?.split(';'); - - if (values?.length > 0) { - f.options.forEach((o) => { - if (values?.includes(o.id)) { - o.selected = true; - } else { - o.selected = false; - } - }); - } - }); - this.setFilter(filters); - } - - private initAvailableFilters() { - this.customerSearch - .getFilters() - .pipe( - map((result) => { - if (result.error) { - } else { - const filters = result.result.map((input) => this.filterMapping.fromInputDto(input)); - this.initQueryFilter$.next({ initialFilter: filters }); - this.setFilter(filters, { updateQuery: false }); - this.parseQueryParams(); - return filters; - } - }) - ) - .subscribe((_) => this.filtersLoaded$.next(true)); - } - - private initAutocomplete() { - this.autocompleteResult$ = this.inputChange$.pipe( - takeUntil(this.destroy$), - debounceTime(300), - distinctUntilChanged(), - switchMap((queryString) => { - if (queryString.length >= 3) { - return this.customerSearch.complete(queryString, this.getSelecteFiltersAsDictionary()).pipe( - map((response) => response.result), - catchError(() => { - // TODO Dialog Service impl. fur Anzeige der Fehlermeldung - return []; - }) - ); - } else { - return of([]); - } - }), - shareReplay() - ); - } - - private initStatusRefreshOnReset() { - this.queryFilter$ - .pipe( - map((fltr) => fltr.query), - distinctUntilChanged(), - takeUntil(this.destroy$), - filter((qs) => !qs || !qs.length) - ) - .subscribe(() => this.searchState$.next('init')); - } - - createQueryParams(): { [key: string]: string } { - return { - query: this.queryFilter.query, - ...this.getSelecteFiltersAsDictionary(), - }; - } - - async updateBreadcrumbParams() { - const queryParams = this.createQueryParams(); - - const crumbs = await this.breadcrumb - .getBreadcrumbsByKeyAndTags$(this.application.activatedProcessId, ['customer', 'filter']) - .pipe(take(1)) - .toPromise(); - - crumbs.forEach((crumb) => { - this.breadcrumb.patchBreadcrumb(crumb.id, { params: queryParams }); - }); - } - - updateUrlQueryParams() { - const queryParams = this.createQueryParams(); - - this.zone.run(() => { - this.router.navigate([], { - queryParams, - queryParamsHandling: 'merge', - }); - }); - } - - async startSearch() { - if (!this.queryFilter.query && (await this.environmentService.isMobile())) { - return this.scan(); - } - - return this.search(); - } - - getSelecteFiltersAsDictionary(): { [key: string]: string } { - const dict: { [key: string]: string } = {}; - - for (const filter of this.queryFilter.filters) { - const options: FilterOption[] = filter.options; - const selected = options.filter((o) => o.selected); - if (selected.length > 0) { - dict[filter.key] = selected.map((o) => o.id).join(';'); - } - } - return dict; - } - - search( - options: { isNewSearch: boolean; take?: number } = { - isNewSearch: true, - take: 10, - } - ): void { - if (!this.shouldFetchNewProducts(options)) { - return; // early exit because no new products need to be fetched - } - if (this.searchState !== 'fetching') { - this.searchState$.next('fetching'); - this.searchResult$ - .pipe( - take(1), - map((result) => { - const hitsTake = options.take ?? 10; - const effectiveTake = options.isNewSearch - ? hitsTake - : this.numberOfResultsFetched + hitsTake > result.hits - ? result.hits - this.numberOfResultsFetched - : hitsTake; - - this.searchResult$.next(this.addLoadingProducts(this.searchResult, effectiveTake)); - return [effectiveTake, hitsTake]; - }), - switchMap(([effectiveTake, hitsTake]) => { - return this.customerSearch - .getCustomers(this.queryFilter.query, { - skip: options.isNewSearch ? 0 : this.numberOfResultsFetched - effectiveTake, - take: hitsTake, - filter: this.getSelecteFiltersAsDictionary(), - }) - .pipe( - takeUntil(this.destroy$), - catchError((err: HttpErrorResponse) => { - return of({ - result: [], - error: true, - hits: 0, - message: err.message, - }); - }), - tap((result) => { - if (result.error) { - this.searchState$.next('error'); - } else if (result.hits === 0) { - this.searchState$.next('empty'); - } else { - this.searchState$.next('result'); - } - }) - ); - }) - ) - .subscribe((r) => { - const hits = r.hits || r.result.length; - this.hits = hits; - this.searchResult$.next(this.removeLoadingProducts(this.searchResult)); - if (options.isNewSearch) { - this.searchResult$.next({ - ...r, - hits, - result: r.result.map((a) => ({ ...a, loaded: true })), - }); - - if (this.searchState === 'result' && !r.dialog) { - if (Number(r?.result[0]?.id) < 0) { - this.navgateToCreateCustomerWithCard(r.result[0]); - } else if (hits === 1) { - this.navigateToDetails(r.result[0].id); - } else { - this.navigateToResults(); - } - } - } else { - this.searchResult$.next({ - ...r, - hits, - result: [...this.searchResult$.value.result, ...r.result.map((a) => ({ ...a, loaded: true }))], - }); - } - - if (hits > 0) { - this.searchState$.next('result'); - this.filterActive$.next(false); - } - }); - } - } - - scan() { - this.nativeContainer - .openScanner('scanCustomer') - .pipe( - takeUntil(this.destroy$), - filter((message) => message.status !== 'IN_PROGRESS') - ) - .subscribe((result) => { - this.setQuery(result.data); - this.searchCustomerCard(); - }); - } - - searchCustomerCard( - options: { isNewSearch: boolean; take?: number } = { - isNewSearch: true, - take: 10, - } - ): void { - if (!this.shouldFetchNewProducts(options)) { - return; // early exit because no new products need to be fetched - } - if (this.searchState !== 'fetching') { - this.searchState$.next('fetching'); - - this.searchResult$ - .pipe( - take(1), - switchMap(() => { - return this.customerSearch.getCustomersByCustomerCardNumber(this.queryFilter.query).pipe( - takeUntil(this.destroy$), - catchError((err: HttpErrorResponse) => { - return of>({ - result: [], - error: true, - hits: 0, - message: err.message, - }); - }), - tap((result) => { - if (result.error) { - this.searchState$.next('error'); - } else if (result.hits === 0) { - this.searchState$.next('empty'); - } else { - this.searchState$.next('result'); - } - }) - ); - }) - ) - .subscribe((r) => { - const hits = r.hits || r.result.length; - this.hits = hits; - this.searchResult$.next(this.removeLoadingProducts(this.searchResult)); - if (options.isNewSearch) { - this.searchResult$.next({ - ...r, - hits, - result: r.result.map((a) => ({ ...a, loaded: true })), - }); - if (this.searchState === 'result') { - if (hits === 1) { - this.navigateToDetails(r.result[0].id); - } else { - this.navigateToResults(); - } - } - } else { - this.searchResult$.next({ - ...r, - hits, - result: [...this.searchResult$.value.result, ...r.result.map((a) => ({ ...a, loaded: true }))], - }); - } - - if (hits > 0) { - this.searchState$.next('result'); - this.filterActive$.next(false); - } - }); - } - } - - navgateToCreateCustomerWithCard(customer: CustomerInfoDTO) { - const feature = customer?.features?.find((feature) => feature.key === 'webshop') ? 'webshop' : 'store'; - const customerCreateQueryParams = { - gender: customer?.gender, - title: customer?.title, - firstName: customer?.firstName, - lastName: customer?.lastName, - email: customer?.communicationDetails?.email, - phone: customer?.communicationDetails?.phone, - mobile: customer?.communicationDetails?.mobile, - street: customer?.address?.street, - streetNumber: customer?.address?.streetNumber, - zipCode: customer?.address?.zipCode, - city: customer?.address?.city, - country: customer?.address?.country, - info: customer?.address?.info, - name: customer?.organisation?.name, - department: customer?.organisation?.department, - vatId: customer?.organisation?.vatId, - dateOfBirth: customer?.dateOfBirth, - card: encodeURIComponent(JSON.stringify(customer?.features[0])), - cardFeature: feature, - }; - this.router.navigate(['customer', 'create', feature], { - queryParams: customerCreateQueryParams, - }); - } - - navigateToDetails(customerId: number) { - this.router.navigate(['customer', customerId]); - } - - navigateToResults() { - this.router.navigate(['customer', 'search', 'result'], { - queryParams: this.createQueryParams(), - }); - } - - setQuery(query: string) { - this.patchQueryFilter({ query }); - } - - setFilter(filters: Filter[], options: { updateQuery?: boolean } = { updateQuery: true }) { - this.patchQueryFilter({ filters }, options); - } - - patchQueryFilter(filter: Partial, options: { updateQuery?: boolean } = { updateQuery: true }) { - const current = this.queryFilter; - - const next = { ...current, ...filter }; - // TODO Check ob QueryFilter geändert wurde, falls gleich kein next Aufruf - let hasChanges = false; - - if (current.query !== next.query) { - hasChanges = true; - } - - if (!hasChanges) { - if (current.filters !== next.filters) { - hasChanges = true; - } - } - - if (hasChanges) { - this.queryFilter$.next(next); - - if (options?.updateQuery) { - this.updateUrlQueryParams(); - this.updateBreadcrumbParams(); - } - } - } - - private addLoadingProducts( - currentResult: PagedResult, - numberOfLoadingProducts: number = 10 - ): PagedResult { - const res = currentResult.result ? currentResult.result : []; - return { - ...currentResult, - result: [...res, ...new Array(numberOfLoadingProducts).fill({ loaded: false })], - }; - } - - private removeLoadingProducts(currentResult: PagedResult): PagedResult { - return { - ...currentResult, - result: currentResult.result.filter((r) => !!r.loaded), - }; - } - - private shouldFetchNewProducts(options: { isNewSearch: boolean; take?: number }): boolean { - if (options.isNewSearch) { - return true; - } - - if (this.hits > 0) { - return !(this.numberOfResultsFetched >= this.hits); - } - - return true; - } -} diff --git a/apps/page/customer/src/lib/customer-search/providers/customer-search-main-autocomplete.provider.spec.ts b/apps/page/customer/src/lib/customer-search/providers/customer-search-main-autocomplete.provider.spec.ts new file mode 100644 index 000000000..6d864d3a7 --- /dev/null +++ b/apps/page/customer/src/lib/customer-search/providers/customer-search-main-autocomplete.provider.spec.ts @@ -0,0 +1,45 @@ +import { CrmCustomerService } from '@domain/crm'; +import { createServiceFactory, SpectatorService, SpyObject } from '@ngneat/spectator'; +import { UiInput } from '@ui/filter'; +import { of } from 'rxjs'; +import { CustomerSearchMainAutocompleteProvider } from './customer-search-main-autocomplete.provider'; + +describe('CustomerSearchMainAutocompleteProvider', () => { + let spectator: SpectatorService; + let customerService: SpyObject; + + const createService = createServiceFactory({ + service: CustomerSearchMainAutocompleteProvider, + mocks: [CrmCustomerService], + }); + + beforeEach(() => { + spectator = createService(); + customerService = spectator.inject(CrmCustomerService); + }); + + it('should create the service', () => { + expect(spectator.service).toBeTruthy(); + }); + + describe('complete()', () => { + it('should call _customerService.complete() with input value when input length is > 2', async () => { + customerService.complete.and.returnValue(of({ result: {} })); + + let input = new UiInput(); + spyOnProperty(input, 'value', 'get').and.returnValue('test'); + const result = await spectator.service.complete(input).toPromise(); + expect(customerService.complete).toHaveBeenCalledWith('test', undefined); + expect(result).toBeTruthy(); + }); + + it('should not call _customerService.complete() when input length is < 2', () => { + customerService.complete.and.returnValue(of({})); + + let input = new UiInput(); + spyOnProperty(input, 'value', 'get').and.returnValue('te'); + spectator.service.complete(input); + expect(customerService.complete).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/apps/page/customer/src/lib/customer-search/providers/customer-search-main-autocomplete.provider.ts b/apps/page/customer/src/lib/customer-search/providers/customer-search-main-autocomplete.provider.ts new file mode 100644 index 000000000..dd089e5b8 --- /dev/null +++ b/apps/page/customer/src/lib/customer-search/providers/customer-search-main-autocomplete.provider.ts @@ -0,0 +1,24 @@ +import { Injectable } from '@angular/core'; +import { CrmCustomerService } from '@domain/crm'; +import { UiFilterAutocomplete, UiFilterAutocompleteProvider, UiInput } from '@ui/filter'; +import { Observable, of } from 'rxjs'; +import { catchError, map } from 'rxjs/operators'; + +@Injectable() +export class CustomerSearchMainAutocompleteProvider extends UiFilterAutocompleteProvider { + for = 'customer'; + + constructor(private _customerSearch: CrmCustomerService) { + super(); + } + + complete(input: UiInput): Observable { + if (input.value?.length > 2) { + return this._customerSearch.complete(input.value, input?.parent?.parent?.getFilterAsStringDictionary()).pipe( + map((response) => response.result), + catchError(() => []) + ); + } + return of([]); + } +} diff --git a/apps/page/customer/src/lib/customer-search/search-filter/search-filter.component.html b/apps/page/customer/src/lib/customer-search/search-filter/search-filter.component.html index 7aafff983..d73afd75a 100644 --- a/apps/page/customer/src/lib/customer-search/search-filter/search-filter.component.html +++ b/apps/page/customer/src/lib/customer-search/search-filter/search-filter.component.html @@ -1,15 +1,26 @@ - - - - - - - -
- + +

Filter

+ + + + +
+ +
+
diff --git a/apps/page/customer/src/lib/customer-search/search-filter/search-filter.component.scss b/apps/page/customer/src/lib/customer-search/search-filter/search-filter.component.scss index ab02338f0..f06543cbb 100644 --- a/apps/page/customer/src/lib/customer-search/search-filter/search-filter.component.scss +++ b/apps/page/customer/src/lib/customer-search/search-filter/search-filter.component.scss @@ -1,5 +1,14 @@ :host { - @apply flex flex-col box-border w-full; + @apply block box-border w-full; +} + +.customer-search-filter-content { + @apply relative mx-auto p-4; + max-width: 916px; +} + +.btn-close { + @apply absolute text-cool-grey top-3 p-4 right-4 outline-none border-none bg-transparent; } .sticky-cta-wrapper { diff --git a/apps/page/customer/src/lib/customer-search/search-filter/search-filter.component.ts b/apps/page/customer/src/lib/customer-search/search-filter/search-filter.component.ts index dd179c18e..3314b49a2 100644 --- a/apps/page/customer/src/lib/customer-search/search-filter/search-filter.component.ts +++ b/apps/page/customer/src/lib/customer-search/search-filter/search-filter.component.ts @@ -1,7 +1,9 @@ -import { Component, OnInit, ChangeDetectionStrategy, ChangeDetectorRef, OnDestroy } from '@angular/core'; -import { Filter } from '@ui/filter'; +import { Component, OnInit, ChangeDetectionStrategy, ChangeDetectorRef, OnDestroy, Output, EventEmitter } from '@angular/core'; +import { SearchComponentStoreService } from '@store/search-component-store'; +import { Filter, UiFilter } from '@ui/filter'; import { isEqual } from 'lodash'; -import { CustomerSearch } from '../customer-search.service'; +import { Subject } from 'rxjs'; +import { map, takeUntil } from 'rxjs/operators'; @Component({ selector: 'page-customer-search-filter', @@ -10,44 +12,33 @@ import { CustomerSearch } from '../customer-search.service'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class CustomerSearchFilterComponent implements OnInit, OnDestroy { - selectedFilters: Filter[]; - initialFilter: Filter[]; - initialSelectedFilters: Filter[]; + @Output() + close = new EventEmitter(); + + private _onDestroy$ = new Subject(); + + filter$ = this.store.filter$.pipe(map((filter) => UiFilter.create(filter))); filterValuesChanged = false; - constructor(public search: CustomerSearch, private cdr: ChangeDetectorRef) {} + constructor(public store: SearchComponentStoreService, private cdr: ChangeDetectorRef) {} - ngOnInit() { - this.selectedFilters = this.search.queryFilter.filters; - this.initialFilter = this.search.initQueryFilter.initialFilter; - if (!isEqual(this.selectedFilters, this.initialFilter)) { - this.initialSelectedFilters = this.selectedFilters; - } - } + ngOnInit() {} ngOnDestroy() { - if (!!this.initialSelectedFilters && this.filterValuesChanged) { - this.search.setFilter(this.selectedFilters); - } else if (!!this.initialSelectedFilters && !this.filterValuesChanged) { - this.search.setFilter(this.initialSelectedFilters); - } else { - this.search.setFilter(this.initialFilter); - } + this._onDestroy$.next(); + this._onDestroy$.complete(); } - applyFilters(search: boolean = true) { - this.search.setFilter(this.selectedFilters); - this.initialFilter = this.selectedFilters; + applyFilter(filter: UiFilter) { + this.store.setFilter(filter); + this.store.search({ clear: true }); - if (search) { - this.search.search({ isNewSearch: true }); - } - this.filterValuesChanged = true; - } - - updateFilter(filters: Filter[]) { - this.search.setFilter(filters); - this.cdr.markForCheck(); + this.store.searchCompleted.pipe(takeUntil(this._onDestroy$)).subscribe((state) => { + console.log('completed', state); + if (state.hits > 0) { + this.close.emit(); + } + }); } } diff --git a/apps/page/customer/src/lib/customer-search/search-main/search-main.component.html b/apps/page/customer/src/lib/customer-search/search-main/search-main.component.html index 6c3c66748..1872f15d9 100644 --- a/apps/page/customer/src/lib/customer-search/search-main/search-main.component.html +++ b/apps/page/customer/src/lib/customer-search/search-main/search-main.component.html @@ -1,11 +1,24 @@ - + Kundendaten erfassen -
+

Kundensuche

Wie lautet Ihr Name oder
Ihre E-Mail-Adresse?

- + + + + +
diff --git a/apps/page/customer/src/lib/customer-search/search-main/search-main.component.scss b/apps/page/customer/src/lib/customer-search/search-main/search-main.component.scss index 717336811..fd7365ffb 100644 --- a/apps/page/customer/src/lib/customer-search/search-main/search-main.component.scss +++ b/apps/page/customer/src/lib/customer-search/search-main/search-main.component.scss @@ -7,7 +7,7 @@ } .info { - @apply text-2xl mt-1 mb-px-50; + @apply text-2xl mt-5 mb-px-50; } .card-create-customer, @@ -22,9 +22,14 @@ } .card-search-customer { - height: calc(100vh - 380px); + height: calc(100vh - 380px - 60px); } a { @apply no-underline; } + +ui-filter-input-group-main { + @apply block mx-auto; + max-width: 600px; +} diff --git a/apps/page/customer/src/lib/customer-search/search-main/search-main.component.spec.ts b/apps/page/customer/src/lib/customer-search/search-main/search-main.component.spec.ts deleted file mode 100644 index 9a96004bb..000000000 --- a/apps/page/customer/src/lib/customer-search/search-main/search-main.component.spec.ts +++ /dev/null @@ -1,125 +0,0 @@ -import { HttpClientTestingModule } from '@angular/common/http/testing'; -import { forwardRef } from '@angular/core'; -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ReactiveFormsModule } from '@angular/forms'; -import { RouterTestingModule } from '@angular/router/testing'; -import { ApplicationService } from '@core/application'; -import { BreadcrumbService } from '@core/breadcrumb'; -import { EnvironmentService } from '@core/environment'; -import { UiIconModule } from '@ui/icon'; -import { UiSearchboxModule } from '@ui/searchbox'; -import { BehaviorSubject, of } from 'rxjs'; -import { take } from 'rxjs/operators'; -import { CustomerSearchComponent } from '../customer-search.component'; -import { CustomerSearch } from '../customer-search.service'; -import { CustomerSearchMainComponent } from './search-main.component'; - -class MockApplicationService { - get activatedProcessId$() { - return of(123); - } -} - -describe('CustomerSearchMainComponent', () => { - let fixture: ComponentFixture; - let component: CustomerSearchMainComponent; - - let searchService: jasmine.SpyObj; - let envService: jasmine.SpyObj; - let applicationService: jasmine.SpyObj; - let breadcrumbService: jasmine.SpyObj; - - const result = [{ id: 'test 1' }, { id: 'test 2' }]; - - beforeEach(() => { - TestBed.configureTestingModule({ - declarations: [CustomerSearchMainComponent, CustomerSearchComponent], - imports: [RouterTestingModule, ReactiveFormsModule, UiSearchboxModule, UiIconModule, HttpClientTestingModule], - providers: [ - CustomerSearchComponent, - { - provide: EnvironmentService, - useValue: jasmine.createSpyObj('environmentService', ['isMobile']), - }, - { - provide: ApplicationService, - useClass: MockApplicationService, - }, - { - provide: BreadcrumbService, - useValue: jasmine.createSpyObj('breadcrumb', ['addBreadcrumbIfNotExists']), - }, - { - provide: CustomerSearch, - useExisting: forwardRef(() => CustomerSearchComponent), - }, - ], - }).compileComponents(); - - fixture = TestBed.createComponent(CustomerSearchMainComponent); - component = fixture.componentInstance; - - searchService = TestBed.inject(CustomerSearch) as jasmine.SpyObj; - - searchService.autocompleteResult$ = of(result); - searchService.searchState$ = new BehaviorSubject('init'); - searchService.filtersLoaded$ = new BehaviorSubject(true); - - envService = TestBed.inject(EnvironmentService) as jasmine.SpyObj; - envService.isMobile.and.returnValue(Promise.resolve(true)); - - applicationService = TestBed.inject(ApplicationService) as jasmine.SpyObj; - - breadcrumbService = TestBed.inject(BreadcrumbService) as jasmine.SpyObj; - breadcrumbService.addBreadcrumbIfNotExists.and.callFake(() => {}); - }); - - it('should be created', () => { - fixture.detectChanges(); - expect(component instanceof CustomerSearchMainComponent).toBeTruthy(); - }); - - describe('detectDevice', () => { - beforeEach(() => { - spyOn(component, 'detectDevice').and.callThrough(); - fixture.detectChanges(); - }); - - it('should be called OnInit', async () => { - expect(component.detectDevice).toHaveBeenCalledTimes(1); - }); - - it('should set isMobile', async () => { - expect(envService.isMobile).toHaveBeenCalled(); - - await envService.isMobile(); - expect(component['isMobile']).toBeTruthy(); - }); - }); - - describe('InitBreadcrumb', () => { - it('should be called onInit', () => { - spyOn(component, 'initBreadcrumb').and.callFake(() => {}); - fixture.detectChanges(); - - expect(component.initBreadcrumb).toHaveBeenCalledTimes(1); - }); - it('should add a breadcrumb if the activated process id changes', async () => { - const params = 'I am a query param'; - spyOn(searchService, 'createQueryParams').and.returnValue(params); - spyOn(component, 'initBreadcrumb').and.callThrough(); - fixture.detectChanges(); - - const key = await applicationService.activatedProcessId$.pipe(take(1)).toPromise(); - const expectedParams = { - key, - params, - name: 'Kundensuche', - path: '/customer/search', - tags: ['customer', 'search', 'main', 'filter'], - }; - - expect(breadcrumbService.addBreadcrumbIfNotExists).toHaveBeenCalledWith(expectedParams); - }); - }); -}); diff --git a/apps/page/customer/src/lib/customer-search/search-main/search-main.component.ts b/apps/page/customer/src/lib/customer-search/search-main/search-main.component.ts index bc8a444ab..efb1c6571 100644 --- a/apps/page/customer/src/lib/customer-search/search-main/search-main.component.ts +++ b/apps/page/customer/src/lib/customer-search/search-main/search-main.component.ts @@ -1,12 +1,10 @@ import { Component, ChangeDetectionStrategy, ChangeDetectorRef, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { ApplicationService } from '@core/application'; import { BreadcrumbService } from '@core/breadcrumb'; -import { EnvironmentService } from '@core/environment'; -import { isEqual } from 'lodash'; -import { combineLatest, Subject, Subscription } from 'rxjs'; -import { debounceTime, first } from 'rxjs/operators'; -import { CustomerSearch } from '../customer-search.service'; +import { SearchComponentStoreService } from '@store/search-component-store'; +import { UiFilter } from '@ui/filter'; +import { NEVER, Observable, Subject } from 'rxjs'; +import { filter, map, switchMap, takeUntil, withLatestFrom } from 'rxjs/operators'; @Component({ selector: 'page-customer-search-main', @@ -15,62 +13,49 @@ import { CustomerSearch } from '../customer-search.service'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class CustomerSearchMainComponent implements OnInit, OnDestroy { - private destroy$ = new Subject(); - - protected isMobile: boolean; - - subscriptions = new Subscription(); + private _onDestroy$ = new Subject(); customerCreateQueryParams?: Record; + processId$: Observable; + + filter$ = this._store.filter$; + + fetching$ = this._store.fetching$; + + message$ = this._store.message$; + constructor( - public search: CustomerSearch, public cdr: ChangeDetectorRef, - public environmentService: EnvironmentService, - public application: ApplicationService, - private breadcrumb: BreadcrumbService, - private route: ActivatedRoute + private _breadcrumb: BreadcrumbService, + private _activatedRoute: ActivatedRoute, + private _store: SearchComponentStoreService ) {} ngOnInit() { - this.detectDevice(); - this.subscriptions.add( - combineLatest([this.application.activatedProcessId$, this.route.queryParams]) - .pipe(debounceTime(0)) - .subscribe(async ([processId, queryParams]) => { - // Setzen des aktuellen Prozesses - if (this.search?.processId !== processId) { - this.search.processId = processId; - } + this.processId$ = this._activatedRoute.parent.parent.data.pipe(map((d) => +d.processId)); + this._activatedRoute.url + .pipe(takeUntil(this._onDestroy$), withLatestFrom(this.processId$)) + .subscribe(([_, processId]) => this.removeBreadcrumbs(processId)); - // Updaten der QueryParams wenn diese sich ändern - if (!isEqual(this.search.getQueryParams(), queryParams)) { - const params = { ...queryParams }; - delete params?.scrollPos; - this.search.setQueryParams({ params }); - } - - const crumbs = await this.breadcrumb - .getBreadcrumbsByKeyAndTags$(processId, ['customer', 'search', 'main', 'filter']) - .pipe(first()) - .toPromise(); - - for (const crumb of crumbs) { - this.breadcrumb.removeBreadcrumbsAfter(crumb.id, ['customer']); - } - }) - ); + this.filter$ + .pipe( + takeUntil(this._onDestroy$), + switchMap((filter) => filter?.input?.find((i) => i.group === 'main')?.input.find((i) => i.key === 'qs')?.changes ?? NEVER), + filter((change) => change.keys.includes('value')) + ) + .subscribe((input) => this.updateCustomerCreateQueryParams(input?.target?.value)); } ngOnDestroy() { - this.destroy$.next(); - this.destroy$.complete(); - this.subscriptions.unsubscribe(); + this._onDestroy$.next(); + this._onDestroy$.complete(); } - async detectDevice() { - this.isMobile = await this.environmentService.isMobile(); - this.cdr.detectChanges(); + async removeBreadcrumbs(processId: number) { + this._breadcrumb.removeBreadcrumbsByKeyAndTags(processId, ['customer', 'result']); + this._breadcrumb.removeBreadcrumbsByKeyAndTags(processId, ['customer', 'create']); + this._breadcrumb.removeBreadcrumbsByKeyAndTags(processId, ['customer', 'details']); } updateCustomerCreateQueryParams(value: string) { @@ -84,4 +69,9 @@ export class CustomerSearchMainComponent implements OnInit, OnDestroy { isValidEmail(email: string) { return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); } + + search(filter: UiFilter) { + this._store.setFilter(filter); + this._store.search({ clear: true }); + } } diff --git a/apps/page/customer/src/lib/customer-search/search-results/customer-result-card/customer-result-card.component.html b/apps/page/customer/src/lib/customer-search/search-results/customer-result-card/customer-result-card.component.html index 3bc070c9e..f92605bf9 100644 --- a/apps/page/customer/src/lib/customer-search/search-results/customer-result-card/customer-result-card.component.html +++ b/apps/page/customer/src/lib/customer-search/search-results/customer-result-card/customer-result-card.component.html @@ -1,5 +1,5 @@
- +

{{ customer?.organisation?.name }} {{ customer?.lastName }} {{ customer?.firstName }} @@ -8,7 +8,7 @@

- +
PLZ und Ort
{{ customer?.address?.zipCode }} {{ customer?.address?.city }}
@@ -29,7 +29,7 @@
- +
@@ -38,14 +38,3 @@
- - - - - - - - - - - diff --git a/apps/page/customer/src/lib/customer-search/search-results/customer-result-card/customer-result-card.component.scss b/apps/page/customer/src/lib/customer-search/search-results/customer-result-card/customer-result-card.component.scss index 8b86a9769..4e03a4f57 100644 --- a/apps/page/customer/src/lib/customer-search/search-results/customer-result-card/customer-result-card.component.scss +++ b/apps/page/customer/src/lib/customer-search/search-results/customer-result-card/customer-result-card.component.scss @@ -6,7 +6,7 @@ @apply flex justify-between items-center; .heading { - @apply text-card-heading p-0 m-0; + @apply text-card-heading font-bold p-0 m-0; } .date { diff --git a/apps/page/customer/src/lib/customer-search/search-results/search-result.component.spec.ts b/apps/page/customer/src/lib/customer-search/search-results/search-result.component.spec.ts deleted file mode 100644 index bf77d0441..000000000 --- a/apps/page/customer/src/lib/customer-search/search-results/search-result.component.spec.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { By } from '@angular/platform-browser'; -import { ApplicationService } from '@core/application'; -import { BreadcrumbService } from '@core/breadcrumb'; -import { PagedResult } from 'apps/domain/defs/src/lib/models'; -import { BehaviorSubject, of } from 'rxjs'; -import { CustomerSearchModule } from '../customer-search.module'; -import { CustomerSearch } from '../customer-search.service'; -import { CustomerSearchType, ResultState } from '../defs'; -import { CustomerSearchResultComponent } from './search-results.component'; - -class MockCustomerSearch { - searchState$ = new BehaviorSubject('init'); - searchResult$ = new BehaviorSubject>({ - result: [{} as CustomerSearchType, {} as CustomerSearchType], - }); - - search(options?: { isNewSearch: boolean; take?: number }) { - return options; - } -} - -describe('CustomerSearchResultComponent', () => { - let fixture: ComponentFixture; - let component: CustomerSearchResultComponent; - - let searchService: CustomerSearch; - let breadcrumb: jasmine.SpyObj; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [CustomerSearchModule], - providers: [ - { - provide: BreadcrumbService, - useValue: jasmine.createSpyObj('breadcrumb', ['addBreadcrumbIfNotExists', 'patchBreadcrumb']), - }, - { - provide: ApplicationService, - useValue: jasmine.createSpyObj('application', { - activatedProcessId: of(123), - }), - }, - { - provide: CustomerSearch, - useClass: MockCustomerSearch, - }, - ], - }).compileComponents(); - - fixture = TestBed.createComponent(CustomerSearchResultComponent); - component = fixture.componentInstance; - searchService = TestBed.inject(CustomerSearch); - breadcrumb = TestBed.inject(BreadcrumbService) as jasmine.SpyObj; - }); - - it('should be created', () => { - expect(component instanceof CustomerSearchResultComponent).toBeTruthy(); - }); - - describe('checkIfReload', () => { - beforeEach(() => { - spyOn(component, 'triggerSearch').and.callThrough(); - spyOn(component, 'checkIfReload').and.callThrough(); - }); - it('should be called on viewportEntered Event', () => { - fixture.detectChanges(); - const resultCard = fixture.debugElement.query(By.css('a')); - const target: HTMLAnchorElement = resultCard.nativeElement; - - target.classList.add('last'); - - resultCard.triggerEventHandler('viewportEntered', target); - - expect(component.checkIfReload).toHaveBeenCalledWith(target); - expect(component.triggerSearch).toHaveBeenCalled(); - }); - - it('should not call triggerSearch if the target element does not include the last class', () => { - fixture.detectChanges(); - const resultCard = fixture.debugElement.query(By.css('a')); - const target: HTMLAnchorElement = resultCard.nativeElement; - - resultCard.triggerEventHandler('viewportEntered', target); - - expect(component.triggerSearch).not.toHaveBeenCalled(); - }); - }); - - describe('triggerSearch', () => { - it('should call search on the SearchService', () => { - spyOn(searchService, 'search').and.callThrough(); - - component.triggerSearch(); - - expect(searchService.search).toHaveBeenCalledWith({ - isNewSearch: false, - take: 10, - }); - }); - }); -}); diff --git a/apps/page/customer/src/lib/customer-search/search-results/search-results.component.html b/apps/page/customer/src/lib/customer-search/search-results/search-results.component.html index 705b41c6d..5b2530167 100644 --- a/apps/page/customer/src/lib/customer-search/search-results/search-results.component.html +++ b/apps/page/customer/src/lib/customer-search/search-results/search-results.component.html @@ -1,17 +1,22 @@ -
{{ hits }} Treffer
-
+
{{ hits }} Treffer
+ + - -
+ diff --git a/apps/page/customer/src/lib/customer-search/search-results/search-results.component.scss b/apps/page/customer/src/lib/customer-search/search-results/search-results.component.scss index 988d0accb..72052a26e 100644 --- a/apps/page/customer/src/lib/customer-search/search-results/search-results.component.scss +++ b/apps/page/customer/src/lib/customer-search/search-results/search-results.component.scss @@ -1,53 +1,18 @@ :host { @apply block box-border overflow-y-scroll; - height: calc(100vh - 293px); } .hits { @apply text-right bg-transparent text-regular font-semibold text-inactive-customer m-3; } -.scroll-container { - @apply h-full overflow-y-scroll relative; -} - -page-customer-result-card { - @apply mb-px-10; -} - a { @apply no-underline; - - &.last { - // page-customer-result-card { - // @apply mb-px-100; - // } - - &.load { - page-customer-result-card { - @apply mb-px-150; - } - } - } -} - -.loader { - @apply sticky w-full flex items-center justify-center text-font-customer; - - bottom: 110px; - height: 32px; - - .hint { - @apply font-light whitespace-nowrap ml-5; - } - - .spin { - @apply animate-spin; - } } .scroll-container-footer { - @apply text-sm mx-auto my-8 w-1/3 text-center text-ucla-blue font-semibold; + @apply text-sm mx-auto mt-8 w-1/3 text-center text-ucla-blue font-semibold; + min-height: 200px; a { @apply text-lg text-brand font-bold; diff --git a/apps/page/customer/src/lib/customer-search/search-results/search-results.component.ts b/apps/page/customer/src/lib/customer-search/search-results/search-results.component.ts index 8252a1da1..99344b3fc 100644 --- a/apps/page/customer/src/lib/customer-search/search-results/search-results.component.ts +++ b/apps/page/customer/src/lib/customer-search/search-results/search-results.component.ts @@ -1,14 +1,10 @@ -import { ChangeDetectionStrategy, Component, ElementRef, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { ChangeDetectionStrategy, Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { ApplicationService } from '@core/application'; import { BreadcrumbService } from '@core/breadcrumb'; -import { PagedResult } from '@domain/defs'; -import { CacheService } from 'apps/core/cache/src/public-api'; -import { debounce, isEqual } from 'lodash'; -import { combineLatest, Observable, Subscription } from 'rxjs'; -import { debounceTime, first, map } from 'rxjs/operators'; -import { CustomerSearch } from '../customer-search.service'; -import { CustomerSearchType } from '../defs'; +import { SearchComponentStoreService } from '@store/search-component-store'; +import { UiScrollContainerComponent } from '@ui/scroll-container'; +import { Observable, Subject } from 'rxjs'; +import { bufferCount, debounceTime, filter, first, map, switchMap, takeUntil, withLatestFrom } from 'rxjs/operators'; @Component({ selector: 'page-customer-search-result', @@ -17,126 +13,90 @@ import { CustomerSearchType } from '../defs'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class CustomerSearchResultComponent implements OnInit, OnDestroy { - @ViewChild('scrollContainer', { static: false }) scrollContainer: ElementRef; - customers$: Observable; - hits$ = this.search.hits$; + @ViewChild('scrollContainer', { static: false }) + scrollContainer: UiScrollContainerComponent; - subscriptions = new Subscription(); + private _onDestroy$ = new Subject(); - protected readonly viewportEnterOptions: IntersectionObserverInit = { - threshold: 0.75, - }; + processId$: Observable; - triggerSearchDebounce = debounce(() => this.triggerSearch(), 1000); - - constructor( - private application: ApplicationService, - private breadcrumb: BreadcrumbService, - public search: CustomerSearch, - private route: ActivatedRoute, - private cache: CacheService - ) {} + constructor(public store: SearchComponentStoreService, private _breadcrumb: BreadcrumbService, private _activatedRoute: ActivatedRoute) {} ngOnInit() { - this.customers$ = this.search.searchResult$.pipe(map((response) => response.result)); - this.subscriptions.add( - combineLatest([this.application.activatedProcessId$, this.route.queryParams]) - .pipe(debounceTime(0)) - .subscribe(async ([processId, queryParams]) => { - // Wenn ein Prozess bereits zugewiesen ist und der Prozess sich ändert - // Speicher Ergebnisse in den Cache und Update Breadcrumb Params - if (this.search.processId !== processId) { - this.cacheCurrentItems(); - await this.updateBreadcrumbs(); - } - - // Setzen des aktuellen Prozesses - this.search.processId = processId; - - // Updaten der QueryParams wenn diese sich ändern - // scrollPos muss entfernt werden um die items anhand der QueryParams zu cachen - if (!isEqual(this.search.getQueryParams(), queryParams)) { - const params = { ...queryParams }; - delete params?.scrollPos; - const items = this.cache?.get>(params)?.result; - const hits = this.cache?.get>(params)?.hits; - this.search.setQueryParams({ params }); - this.search.hits = hits; - this.setItems(items); - this.triggerSearchDebounce(); - } - - // Nach dem setzen der Items im store an die letzte Position scrollen - setTimeout(() => this.scrollContainer.nativeElement.scrollTo(0, Number(queryParams?.scrollPos ?? 0)), 0); - - // Fügt Breadcrumb hinzu falls dieser noch nicht vorhanden ist - await this.breadcrumb.addBreadcrumbIfNotExists({ - key: processId, - name: `${this.search.queryFilter.query ? this.search.queryFilter.query : 'Alle Kunden'}`, - path: '/customer/search/result', - params: queryParams, - tags: ['customer', 'search', 'results', 'filter'], - section: 'customer', - }); - - await this.removeDetailBreadcrumb(); - await this.updateBreadcrumbs(); - }) - ); - } - - async removeDetailBreadcrumb() { - const processId = this.search.processId; - const crumbs = await this.breadcrumb.getBreadcrumbsByKeyAndTags$(processId, ['customer', 'details']).pipe(first()).toPromise(); - - for (const crumb of crumbs) { - this.breadcrumb.removeBreadcrumb(crumb.id); - } - } - - async updateBreadcrumbs() { - const scrollPos = this.scrollContainer.nativeElement.scrollTop; - const processId = this.search.processId; - const queryParams = { ...this.search.getQueryParams() }; - - const crumbs = await this.breadcrumb - .getBreadcrumbsByKeyAndTags$(processId, ['customer', 'search', 'results', 'filter']) - .pipe(first()) - .toPromise(); - - const params = { ...queryParams, scrollPos }; - - for (const crumb of crumbs) { - this.breadcrumb.patchBreadcrumb(crumb.id, { - params, - name: `${this.search.queryFilter.query ? this.search.queryFilter.query : 'Alle Kunden'}`, + this.processId$ = this._activatedRoute.parent.parent.data.pipe(map((data) => +data.processId)); + this._activatedRoute.queryParams + .pipe(takeUntil(this._onDestroy$), withLatestFrom(this.processId$)) + .subscribe(([queryParams, processId]) => this.handleBreadcrumbs(processId, queryParams)); + this.store.filter$ + .pipe( + takeUntil(this._onDestroy$), + filter((filter) => !!filter), + debounceTime(10), + withLatestFrom(this.store.items$) + ) + .subscribe(([_, items]) => { + if (items?.length === 0) { + this.store.search({ clear: true }); + } }); - } + + this.processId$ + .pipe( + takeUntil(this._onDestroy$), + switchMap((_) => + this.store.items$.pipe( + filter((items) => items?.length > 0), + debounceTime(10), + first() + ) + ), + withLatestFrom(this._activatedRoute.queryParams) + ) + .subscribe(([_, queryParams]) => { + setTimeout(() => this.scrollContainer.scrollTo(Number(queryParams.scrollPos ?? 0))); + }); + + this.processId$ + .pipe(takeUntil(this._onDestroy$), bufferCount(2, 1)) + .subscribe((processIds) => this.updateScrollPosition(processIds[0])); } - ngOnDestroy() { - this.subscriptions.unsubscribe(); - this.cacheCurrentItems(); - this.updateBreadcrumbs(); - } - - checkIfReload(target: HTMLElement): void { - if (target.classList.contains('last')) { - this.triggerSearch(); - } - } - - triggerSearch() { - this.search.search({ isNewSearch: false, take: 10 }); - } - - setItems(items: CustomerSearchType[]) { - this.search.searchResult$.next({ - result: items, + async addOrUpdateBreadcrumb(processId: number, queryParams: Record) { + await this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({ + key: processId, + name: queryParams.main_qs ?? 'Alle Kunden', + path: `/kunde/${processId}/customer/search/result`, + params: queryParams, + tags: ['customer', 'search', 'result', 'filter'], + section: 'customer', }); } - cacheCurrentItems() { - this.cache.set(this.search.getQueryParams(), this.search.searchResult); + removeDetailBreadcrumbs(processId: number) { + this._breadcrumb.removeBreadcrumbsByKeyAndTags(processId, ['customer', 'details']); + } + + handleBreadcrumbs(processId: number, queryParams: Record) { + this.addOrUpdateBreadcrumb(processId, queryParams); + this.removeDetailBreadcrumbs(processId); + } + + async ngOnDestroy() { + const processId = await this.processId$.pipe(first()).toPromise(); + this.updateScrollPosition(processId); + + this._onDestroy$.next(); + this._onDestroy$.complete(); + } + + updateScrollPosition(processId: number) { + const scrollPos = `${this.scrollContainer.scrollPos}`; + this.addOrUpdateBreadcrumb(processId, { ...this.store.filter.getQueryParams(), scrollPos }); + } + + triggerSearch() { + if (this.store?.hits > this.store?.items?.length && !this.store?.fetching) { + this.store.search({}); + } } } diff --git a/apps/page/customer/src/lib/customer-search/services/customer.search-state-search-service.spec.ts b/apps/page/customer/src/lib/customer-search/services/customer.search-state-search-service.spec.ts new file mode 100644 index 000000000..b3914d0e6 --- /dev/null +++ b/apps/page/customer/src/lib/customer-search/services/customer.search-state-search-service.spec.ts @@ -0,0 +1,85 @@ +import { createServiceFactory, SpectatorService, SpyObject } from '@ngneat/spectator'; +import { CustomerService, ListResponseArgsOfCustomerInfoDTO } from '@swagger/crm'; +import { UiFilter } from '@ui/filter'; +import { UiErrorModalComponent, UiModalService } from '@ui/modal'; +import { cold, hot } from 'jasmine-marbles'; +import { CustomerSearchStateSearchService } from './customer.search-state-search-service'; + +describe('CustomerSearchStateSearchService', () => { + let spectator: SpectatorService; + let customerServiceMock: SpyObject; + let modalServiceMock: SpyObject; + const createService = createServiceFactory({ + service: CustomerSearchStateSearchService, + mocks: [CustomerService, UiModalService], + }); + + beforeEach(() => { + spectator = createService(); + customerServiceMock = spectator.inject(CustomerService); + modalServiceMock = spectator.inject(UiModalService); + }); + + it('should create the service', () => { + expect(spectator.service).toBeTruthy(); + }); + + describe('search()', () => { + it('should call _customerService.CustomerListCustomers with the query token and options', () => { + customerServiceMock.CustomerListCustomers.and.returnValue(cold('-(a|)', { a: { hits: 5 } })); + + const filter = new UiFilter(); + spyOn(filter, 'getQueryToken').and.returnValue({ input: { qs: 'test' } }); + expect(spectator.service.search(filter, { skip: 10 })).toBeObservable( + hot('-(a|)', { a: { hits: 5, error: false, items: [], message: undefined } }) + ); + expect(customerServiceMock.CustomerListCustomers).toHaveBeenCalledWith({ + input: { qs: 'test' }, + skip: 10, + take: 25, + }); + }); + + it('should catch an Error and show UiErrorModalComponent', () => { + customerServiceMock.CustomerListCustomers.and.returnValue(cold('-#', undefined, { message: 'error' })); + + const filter = new UiFilter(); + spyOn(filter, 'getQueryToken').and.returnValue({ input: { qs: 'test' } }); + expect(spectator.service.search(filter, { skip: 10 })).toBeObservable(hot('-(a|)', { a: {} })); + expect(modalServiceMock.open).toHaveBeenCalledWith({ + content: UiErrorModalComponent, + title: 'Fehler beim Laden der Kunden Suchergebnisse', + data: { message: 'error' }, + }); + }); + }); + + describe('mapResponseToSearchStateSearchResult()', () => { + it('should return SearchStateSearchResult object with the params from ListResponseArgsOfCustomerInfoDTO', () => { + const response: ListResponseArgsOfCustomerInfoDTO = { + result: [], + message: 'Test', + hits: 30, + error: true, + }; + + expect(spectator.service.mapResponseToSearchStateSearchResult(response)).toEqual({ + items: response.result, + message: response.message, + hits: response.hits, + error: response.error, + }); + }); + + it('should return SearchStateSearchResult object with default values when the params from ListResponseArgsOfCustomerInfoDTO are not set', () => { + const response: ListResponseArgsOfCustomerInfoDTO = { error: false }; + + expect(spectator.service.mapResponseToSearchStateSearchResult(response)).toEqual({ + items: [], + message: undefined, + hits: 0, + error: false, + }); + }); + }); +}); diff --git a/apps/page/customer/src/lib/customer-search/services/customer.search-state-search-service.ts b/apps/page/customer/src/lib/customer-search/services/customer.search-state-search-service.ts new file mode 100644 index 000000000..9192a5c49 --- /dev/null +++ b/apps/page/customer/src/lib/customer-search/services/customer.search-state-search-service.ts @@ -0,0 +1,41 @@ +import { Injectable } from '@angular/core'; +import { SearchStateSearchResult, SearchStateSearchService } from '@store/search-component-store'; +import { CustomerInfoDTO, CustomerService, ListResponseArgsOfCustomerInfoDTO } from '@swagger/crm'; +import { UiFilter } from '@ui/filter'; +import { UiErrorModalComponent, UiModalService } from '@ui/modal'; +import { of } from 'rxjs'; +import { catchError, map } from 'rxjs/operators'; + +@Injectable() +export class CustomerSearchStateSearchService implements SearchStateSearchService { + constructor(private _customerService: CustomerService, private _modal: UiModalService) {} + + search(filter: UiFilter, options: { skip: number }) { + return this._customerService + .CustomerListCustomers({ + ...filter.getQueryToken(), + skip: options.skip, + take: 25, + }) + .pipe( + map((res) => this.mapResponseToSearchStateSearchResult(res)), + catchError((error) => { + this._modal.open({ + content: UiErrorModalComponent, + title: 'Fehler beim Laden der Kunden Suchergebnisse', + data: error, + }); + return of({}); + }) + ); + } + + mapResponseToSearchStateSearchResult(response: ListResponseArgsOfCustomerInfoDTO): SearchStateSearchResult { + return { + items: response.result ?? [], + message: response.message, + hits: response.hits ?? 0, + error: response.error ?? false, + }; + } +} diff --git a/apps/page/customer/src/lib/customer-search/services/customer.search-state-settings-loader.spec.ts b/apps/page/customer/src/lib/customer-search/services/customer.search-state-settings-loader.spec.ts new file mode 100644 index 000000000..f7b3d21b4 --- /dev/null +++ b/apps/page/customer/src/lib/customer-search/services/customer.search-state-settings-loader.spec.ts @@ -0,0 +1,43 @@ +import { createServiceFactory, SpectatorService, SpyObject } from '@ngneat/spectator'; +import { CustomerService } from '@swagger/crm'; +import { UiErrorModalComponent, UiModalService } from '@ui/modal'; +import { cold, hot } from 'jasmine-marbles'; +import { CustomerSearchStateSettingsLoader } from './customer.search-state-settings-loader'; + +describe('CustomerSearchStateSettingsLoader', () => { + let spectator: SpectatorService; + let customerServiceMock: SpyObject; + let modalServiceMock: SpyObject; + const createService = createServiceFactory({ + service: CustomerSearchStateSettingsLoader, + mocks: [CustomerService, UiModalService], + }); + + beforeEach(() => { + spectator = createService(); + customerServiceMock = spectator.inject(CustomerService); + modalServiceMock = spectator.inject(UiModalService); + }); + + it('should create the service', () => { + expect(spectator.service).toBeTruthy(); + }); + + describe('load()', () => { + it('should call _customerService.CustomerCustomerQuerySettings() and map the result to UISettingsDTO', () => { + customerServiceMock.CustomerCustomerQuerySettings.and.returnValue(cold('-(a|)', { a: { result: { filter: [{ label: 'test' }] } } })); + expect(spectator.service.load()).toBeObservable(hot('-(a|)', { a: { filter: [{ label: 'test' }] } })); + expect(customerServiceMock.CustomerCustomerQuerySettings).toHaveBeenCalled(); + }); + + it('should catch an Error and show UiErrorModalComponent', () => { + customerServiceMock.CustomerCustomerQuerySettings.and.returnValue(cold('-#', undefined, { message: 'error' })); + expect(spectator.service.load()).toBeObservable(hot('-(a|)', { a: {} })); + expect(modalServiceMock.open).toHaveBeenCalledWith({ + content: UiErrorModalComponent, + title: 'Fehler beim Laden des Kundenfilters', + data: { message: 'error' }, + }); + }); + }); +}); diff --git a/apps/page/customer/src/lib/customer-search/services/customer.search-state-settings-loader.ts b/apps/page/customer/src/lib/customer-search/services/customer.search-state-settings-loader.ts new file mode 100644 index 000000000..45cdeee61 --- /dev/null +++ b/apps/page/customer/src/lib/customer-search/services/customer.search-state-settings-loader.ts @@ -0,0 +1,26 @@ +import { Injectable } from '@angular/core'; +import { SearchStateSettingsLoader } from '@store/search-component-store'; +import { UISettingsDTO } from '@swagger/cat'; +import { CustomerService } from '@swagger/crm'; +import { UiErrorModalComponent, UiModalService } from '@ui/modal'; +import { Observable, of } from 'rxjs'; +import { catchError, map } from 'rxjs/operators'; + +@Injectable() +export class CustomerSearchStateSettingsLoader implements SearchStateSettingsLoader { + constructor(private _customerService: CustomerService, private _modal: UiModalService) {} + + load(): UISettingsDTO | Promise | Observable { + return this._customerService.CustomerCustomerQuerySettings().pipe( + map((res) => res.result), + catchError((error) => { + this._modal.open({ + content: UiErrorModalComponent, + title: 'Fehler beim Laden des Kundenfilters', + data: error, + }); + return of({}); + }) + ); + } +} diff --git a/apps/page/customer/src/lib/customer-search/shared/customer-searchbox/customer-searchbox.component.html b/apps/page/customer/src/lib/customer-search/shared/customer-searchbox/customer-searchbox.component.html deleted file mode 100644 index fb5ee7775..000000000 --- a/apps/page/customer/src/lib/customer-search/shared/customer-searchbox/customer-searchbox.component.html +++ /dev/null @@ -1,53 +0,0 @@ -
- - - - Keine Suchergebnisse - - - - - - - - -
diff --git a/apps/page/customer/src/lib/customer-search/shared/customer-searchbox/customer-searchbox.component.scss b/apps/page/customer/src/lib/customer-search/shared/customer-searchbox/customer-searchbox.component.scss deleted file mode 100644 index 41267614c..000000000 --- a/apps/page/customer/src/lib/customer-search/shared/customer-searchbox/customer-searchbox.component.scss +++ /dev/null @@ -1,39 +0,0 @@ -ui-searchbox { - @apply max-w-lg mx-auto; - - input { - caret-color: #f70400; - } - - [uiSearchboxSearchButton] { - @apply bg-white text-brand; - - &:not(.scan) { - @apply pr-px-25; - } - - &.scan { - @apply bg-brand text-white; - } - - &.hide { - @apply bg-white; - } - - .spin { - @apply bg-white text-ucla-blue; - } - } - - [uiSearchboxClearButton] { - @apply text-inactive-customer; - } -} - -.spin { - @apply animate-spin; -} - -::ng-deep page-customer-searchbox ui-searchbox input::placeholder { - font-size: 16px; -} diff --git a/apps/page/customer/src/lib/customer-search/shared/customer-searchbox/customer-searchbox.component.spec.ts b/apps/page/customer/src/lib/customer-search/shared/customer-searchbox/customer-searchbox.component.spec.ts deleted file mode 100644 index a4a05e335..000000000 --- a/apps/page/customer/src/lib/customer-search/shared/customer-searchbox/customer-searchbox.component.spec.ts +++ /dev/null @@ -1,168 +0,0 @@ -import { ComponentFixture, fakeAsync, flush, TestBed } from '@angular/core/testing'; -import { By } from '@angular/platform-browser'; -import { EnvironmentService } from '@core/environment'; -import { PageCustomerModule } from '@page/customer'; -import { AutocompleteDTO } from '@swagger/crm'; -import { UiSearchboxAutocompleteComponent } from '@ui/searchbox'; -import { BehaviorSubject } from 'rxjs'; -import { CustomerSearch } from '../../customer-search.service'; -import { QueryFilter, ResultState } from '../../defs'; -import { CustomerSearchboxComponent } from './customer-searchbox.component'; - -class MockCustomerSearch { - get queryFilter() { - return { query: 'unit test', filters: [] }; - } - - autocompleteResult$ = new BehaviorSubject([ - { - id: '123', - display: 'query', - query: 'query', - }, - { - id: '456', - display: 'query2', - query: 'query2', - }, - ]); - - queryFilter$ = new BehaviorSubject({ - query: 'unit test', - filters: [], - }); - - inputChange$ = new BehaviorSubject(''); - - searchState$ = new BehaviorSubject('init'); - - setQuery() {} - - startSearch() {} - - search() {} -} - -describe('CustomerSearchboxComponent', () => { - let fixture: ComponentFixture; - let component: CustomerSearchboxComponent; - - let customerSearch: CustomerSearch; - let environmentService: jasmine.SpyObj; - - const getInput = () => { - return fixture.debugElement.query(By.css('input')); - }; - const getResetButton = () => { - return fixture.debugElement.query(By.css('button[type="reset"]')); - }; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [PageCustomerModule], - providers: [ - { - provide: CustomerSearch, - useClass: MockCustomerSearch, - }, - { - provide: EnvironmentService, - useValue: jasmine.createSpyObj('environmentService', ['isMobile']), - }, - ], - }).compileComponents(); - - customerSearch = TestBed.inject(CustomerSearch) as jasmine.SpyObj; - environmentService = TestBed.inject(EnvironmentService) as jasmine.SpyObj; - - fixture = TestBed.createComponent(CustomerSearchboxComponent); - component = fixture.componentInstance; - - component.autocomplete = { - open: () => {}, - close: () => {}, - } as UiSearchboxAutocompleteComponent; - }); - - it('should be created', () => { - expect(component instanceof CustomerSearchboxComponent).toBeTruthy(); - }); - - describe('initQueryControl', () => { - beforeEach(() => { - spyOn(component, 'initQueryControl').and.callThrough(); - }); - it('should initialize the query control with the query from the customer service', () => { - spyOn(customerSearch, 'queryFilter').and.callThrough(); - - fixture.detectChanges(); - - expect(component.initQueryControl).toHaveBeenCalled(); - expect(component.queryControl.value).toEqual('unit test'); - }); - }); - - describe('focus', () => { - beforeEach(() => { - spyOn(component, 'focus').and.callThrough(); - }); - it('should set focus on the provided input element', fakeAsync(() => { - fixture.detectChanges(); - const mockInput = { focus: () => {} } as HTMLInputElement; - spyOn(mockInput, 'focus').and.callThrough(); - - const input = getInput(); - input.triggerEventHandler('viewportEntered', mockInput); - - flush(); - - expect(component.focus).toHaveBeenCalledWith(mockInput); - expect(mockInput.focus).toHaveBeenCalled(); - })); - }); - - describe('Input Behavior', () => { - beforeEach(() => { - spyOn(customerSearch, 'startSearch').and.callThrough(); - spyOn(customerSearch.inputChange$, 'next').and.callThrough(); - }); - it('should start a search and close the autocomplete on Enter', () => { - fixture.detectChanges(); - spyOn(component.autocomplete, 'close').and.callThrough(); - - const input = getInput(); - input.triggerEventHandler('keydown.enter', {}); - - expect(customerSearch.startSearch).toHaveBeenCalled(); - expect(component.autocomplete.close).toHaveBeenCalled(); - }); - - it('should set the inputted value on the customer search service', () => { - fixture.detectChanges(); - - const inputString = 'unit test'; - - const input = getInput(); - input.triggerEventHandler('inputChange', inputString); - - expect(customerSearch.inputChange$.next).toHaveBeenCalledWith(inputString); - }); - }); - - describe('Reset Behaviour', () => { - beforeEach(() => { - fixture.detectChanges(); - spyOn(component.queryControl, 'reset').and.callThrough(); - }); - it('should reset the input and focus the input', () => { - const resetButton = getResetButton(); - const input = getInput().nativeElement; - spyOn(input, 'focus').and.callThrough(); - - resetButton.triggerEventHandler('click', {}); - - expect(component.queryControl.reset).toHaveBeenCalled(); - expect(component.queryControl.reset).toHaveBeenCalled(); - }); - }); -}); diff --git a/apps/page/customer/src/lib/customer-search/shared/customer-searchbox/customer-searchbox.component.ts b/apps/page/customer/src/lib/customer-search/shared/customer-searchbox/customer-searchbox.component.ts deleted file mode 100644 index 69251f46f..000000000 --- a/apps/page/customer/src/lib/customer-search/shared/customer-searchbox/customer-searchbox.component.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { Component, OnInit, ChangeDetectionStrategy, ChangeDetectorRef, OnDestroy, ViewChild, EventEmitter, Output } from '@angular/core'; -import { FormControl } from '@angular/forms'; -import { EnvironmentService } from '@core/environment'; -import { AutocompleteDTO } from '@swagger/crm'; -import { UiSearchboxAutocompleteComponent } from '@ui/searchbox'; -import { NativeContainerService } from 'native-container'; -import { Observable, Subject } from 'rxjs'; -import { delay, takeUntil, tap } from 'rxjs/operators'; -import { CustomerSearch } from '../../customer-search.service'; - -@Component({ - selector: 'page-customer-searchbox', - templateUrl: 'customer-searchbox.component.html', - styleUrls: ['customer-searchbox.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class CustomerSearchboxComponent implements OnInit, OnDestroy { - @ViewChild(UiSearchboxAutocompleteComponent, { - read: UiSearchboxAutocompleteComponent, - static: false, - }) - autocomplete: UiSearchboxAutocompleteComponent; - autocompleteResult$: Observable; - - private destroy$ = new Subject(); - - isMobile: boolean; - queryControl: FormControl; - - @Output() - searchStarted = new EventEmitter(); - - @Output() - valueChanged = new EventEmitter(); - - constructor( - public search: CustomerSearch, - private environmentService: EnvironmentService, - private cdr: ChangeDetectorRef, - private nativeContainer: NativeContainerService - ) {} - - startSearch() { - this.search.startSearch(); - this.searchStarted.emit(); - } - - ngOnInit() { - this.initQueryControl(); - this.detectDevice(); - this.initAutocomplete(); - - this.search.queryFilter$.pipe(takeUntil(this.destroy$)).subscribe((queryFilter) => { - this.queryControl.patchValue(queryFilter.query || '', { - emitEvent: false, - }); - }); - - this.queryControl.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((value) => { - this.search.setQuery(value); - this.valueChanged.emit(value); - }); - } - - ngOnDestroy() {} - - initQueryControl() { - this.queryControl = new FormControl(this.search.queryFilter.query || ''); - } - - initAutocomplete() { - this.autocompleteResult$ = this.search.autocompleteResult$.pipe( - takeUntil(this.destroy$), - tap((results) => { - if (this.autocomplete) { - if (results.length > 0) { - this.autocomplete.open(); - } else { - this.autocomplete.close(); - } - } - }), - delay(1), - tap(() => this.cdr.detectChanges()) - ); - } - - async detectDevice() { - this.isMobile = this.nativeContainer.isUiWebview().isNative; - this.cdr.detectChanges(); - } - - focus(input: HTMLInputElement): void { - setTimeout(() => { - input.focus(); - this.cdr.detectChanges(); - }, 200); - } -} diff --git a/apps/page/customer/src/lib/customer-search/shared/customer-searchbox/index.ts b/apps/page/customer/src/lib/customer-search/shared/customer-searchbox/index.ts deleted file mode 100644 index 29ab5ea75..000000000 --- a/apps/page/customer/src/lib/customer-search/shared/customer-searchbox/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -// start:ng42.barrel -export * from './customer-searchbox.component'; -// end:ng42.barrel diff --git a/apps/page/customer/src/lib/guards/customer-create.guard.ts b/apps/page/customer/src/lib/guards/customer-create.guard.ts index 873521bc2..19ce37459 100644 --- a/apps/page/customer/src/lib/guards/customer-create.guard.ts +++ b/apps/page/customer/src/lib/guards/customer-create.guard.ts @@ -44,7 +44,9 @@ export class CustomerCreateGuard implements CanActivateChild { for (const key in selectables) { if (Object.prototype.hasOwnProperty.call(selectables, key)) { if (selectables[key]) { - this.router.navigate(['/customer/create', key], { queryParams: route.queryParams }); + this.router.navigate(['/kunde', this.application.activatedProcessId, 'customer', 'create', key], { + queryParams: route.queryParams, + }); return false; } } diff --git a/apps/page/customer/src/lib/modals/cant-add-customer-to-cart-modal/cant-add-customer-to-cart.component.html b/apps/page/customer/src/lib/modals/cant-add-customer-to-cart-modal/cant-add-customer-to-cart.component.html index 1aca793c4..6545666f7 100644 --- a/apps/page/customer/src/lib/modals/cant-add-customer-to-cart-modal/cant-add-customer-to-cart.component.html +++ b/apps/page/customer/src/lib/modals/cant-add-customer-to-cart-modal/cant-add-customer-to-cart.component.html @@ -6,10 +6,16 @@

- Zur Kundensuche + Zur Kundensuche Kundendaten erfassen) {} + constructor(public ref: UiModalRef, public readonly applicationService: ApplicationService) {} } diff --git a/apps/page/customer/src/lib/modals/cant-select-guest/cant-select-guest-modal.component.html b/apps/page/customer/src/lib/modals/cant-select-guest/cant-select-guest-modal.component.html index 76e760781..a52b0305d 100644 --- a/apps/page/customer/src/lib/modals/cant-select-guest/cant-select-guest-modal.component.html +++ b/apps/page/customer/src/lib/modals/cant-select-guest/cant-select-guest-modal.component.html @@ -11,10 +11,12 @@

- Zur Kundensuche + Zur Kundensuche Kundendaten erfassen) {} + constructor(public ref: UiModalRef, public readonly applicationService: ApplicationService) {} createCustomerDataQuery(customer: CustomerDTO): { [key: string]: string } { const query: { [key: string]: string } = { diff --git a/apps/page/customer/src/lib/page-customer-routing.module.ts b/apps/page/customer/src/lib/page-customer-routing.module.ts index 5fb2a6918..059db38f4 100644 --- a/apps/page/customer/src/lib/page-customer-routing.module.ts +++ b/apps/page/customer/src/lib/page-customer-routing.module.ts @@ -68,7 +68,7 @@ const routes: Routes = [ component: CustomerOrderDetailsComponent, }, { - path: 'order/:orderId', + path: ':orderId', component: CustomerOrderDetailsComponent, }, { @@ -111,6 +111,11 @@ const routes: Routes = [ path: ':customerId/shipping/:shippingAddressId/edit/b2c', component: ShippingEditB2CComponent, }, + { + path: '', + pathMatch: 'full', + redirectTo: 'search', + }, ], }, ]; diff --git a/apps/page/customer/src/lib/page-customer.component.scss b/apps/page/customer/src/lib/page-customer.component.scss index 7d8b7d789..56e454d46 100644 --- a/apps/page/customer/src/lib/page-customer.component.scss +++ b/apps/page/customer/src/lib/page-customer.component.scss @@ -1,9 +1,3 @@ shell-breadcrumb { - margin-top: -5px; - @apply mb-px-10 pb-px-10; -} - -.content-container { - max-height: calc(100vh - 267px); - overflow: scroll; + @apply sticky z-sticky top-0; } diff --git a/apps/page/customer/src/test.ts b/apps/page/customer/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/page/customer/src/test.ts +++ b/apps/page/customer/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/page/dashboard/README.md b/apps/page/dashboard/README.md new file mode 100644 index 000000000..5bd82040e --- /dev/null +++ b/apps/page/dashboard/README.md @@ -0,0 +1,25 @@ +# Dashboard + +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 dashboard` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project dashboard`. + +> Note: Don't forget to add `--project dashboard` or else it will be added to the default project in your `angular.json` file. + +## Build + +Run `ng build dashboard` to build the project. The build artifacts will be stored in the `dist/` directory. + +## Publishing + +After building your library with `ng build dashboard`, go to the dist folder `cd dist/dashboard` and run `npm publish`. + +## Running unit tests + +Run `ng test dashboard` 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. diff --git a/apps/page/dashboard/karma.conf.js b/apps/page/dashboard/karma.conf.js new file mode 100644 index 000000000..6545bf81f --- /dev/null +++ b/apps/page/dashboard/karma.conf.js @@ -0,0 +1,43 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html +const customLaunchers = require('../../../karma/custom-launchers'); +const junitReporter = require('../../../karma/junit-reporter')('page-dashboard'); +const coverageReporter = require('../../../karma/coverage-reporter')('page-dashboard'); + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage'), + require('karma-junit-reporter'), + require('@angular-devkit/build-angular/plugins/karma'), + ], + client: { + jasmine: { + // you can add configuration options for Jasmine here + // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html + // for example, you can disable the random execution with `random: false` + // or set a specific seed with `seed: 4321` + }, + clearContext: false, // leave Jasmine Spec Runner output visible in browser + }, + jasmineHtmlReporter: { + suppressAll: true, // removes the duplicated traces + }, + coverageReporter, + junitReporter, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + customLaunchers, + singleRun: false, + restartOnFileChange: true, + }); +}; diff --git a/apps/page/dashboard/ng-package.json b/apps/page/dashboard/ng-package.json new file mode 100644 index 000000000..2046ed004 --- /dev/null +++ b/apps/page/dashboard/ng-package.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../../dist/page/dashboard", + "lib": { + "entryFile": "src/public-api.ts" + } +} \ No newline at end of file diff --git a/apps/page/dashboard/package.json b/apps/page/dashboard/package.json new file mode 100644 index 000000000..8d21ed3af --- /dev/null +++ b/apps/page/dashboard/package.json @@ -0,0 +1,11 @@ +{ + "name": "@page/dashboard", + "version": "0.0.1", + "peerDependencies": { + "@angular/common": "^12.2.0", + "@angular/core": "^12.2.0" + }, + "dependencies": { + "tslib": "^2.3.0" + } +} \ No newline at end of file diff --git a/apps/page/dashboard/src/lib/dashboard-routing-module.ts b/apps/page/dashboard/src/lib/dashboard-routing-module.ts new file mode 100644 index 000000000..e38f3e1b6 --- /dev/null +++ b/apps/page/dashboard/src/lib/dashboard-routing-module.ts @@ -0,0 +1,16 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { DashboardComponent } from './dashboard.component'; + +const routes: Routes = [ + { + path: '', + component: DashboardComponent, + }, +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], +}) +export class DashboardRoutingModule {} diff --git a/apps/page/dashboard/src/lib/dashboard.component.html b/apps/page/dashboard/src/lib/dashboard.component.html new file mode 100644 index 000000000..ac17ef7f8 --- /dev/null +++ b/apps/page/dashboard/src/lib/dashboard.component.html @@ -0,0 +1,4 @@ + + + + diff --git a/apps/page/dashboard/src/lib/dashboard.component.scss b/apps/page/dashboard/src/lib/dashboard.component.scss new file mode 100644 index 000000000..b430a24a3 --- /dev/null +++ b/apps/page/dashboard/src/lib/dashboard.component.scss @@ -0,0 +1,7 @@ +:host { + @apply grid grid-flow-row grid-cols-1 gap-4 p-4; + + @screen tablet { + @apply grid-cols-2; + } +} diff --git a/apps/page/dashboard/src/lib/dashboard.component.spec.ts b/apps/page/dashboard/src/lib/dashboard.component.spec.ts new file mode 100644 index 000000000..3e26ad416 --- /dev/null +++ b/apps/page/dashboard/src/lib/dashboard.component.spec.ts @@ -0,0 +1,117 @@ +import { By } from '@angular/platform-browser'; +import { DomainDashboardService } from '@domain/isa'; +import { Spectator, createComponentFactory, SpyObject, createSpyObject } from '@ngneat/spectator'; +import { MockComponent } from 'ng-mocks'; +import { of } from 'rxjs'; +import { DashboardComponent } from './dashboard.component'; +import { KpiCardComponent } from './kpi-card/kpi-card.component'; +import { ProductsCardComponent } from './products-card/product-card.component'; + +describe('DashboardComponent', () => { + let spectator: Spectator; + let domainIsaDashboardService: SpyObject; + + const createComponent = createComponentFactory({ + component: DashboardComponent, + declarations: [MockComponent(KpiCardComponent), MockComponent(ProductsCardComponent)], + componentMocks: [KpiCardComponent, ProductsCardComponent], + }); + + beforeEach(() => { + domainIsaDashboardService = createSpyObject(DomainDashboardService); + + domainIsaDashboardService.feed.andReturn(of({ result: [] })); + + spectator = createComponent({ + providers: [{ provide: DomainDashboardService, useValue: domainIsaDashboardService }], + }); + }); + + it('should create', () => { + expect(spectator.component).toBeTruthy(); + }); + + describe('page-kpi-card element', () => { + it('should be created', () => { + spectator.component.feeds$ = of([ + { + type: 'kpi', + }, + ]); + + spectator.detectChanges(); + expect(spectator.query('page-kpi-card')).toExist(); + }); + + it('should have the feed as [feed] input', () => { + spectator.component.feeds$ = of([ + { + type: 'kpi', + }, + ]); + + spectator.detectChanges(); + + expect(spectator.debugElement.query(By.css('page-kpi-card')).componentInstance.feed).toEqual({ + type: 'kpi', + }); + }); + + it('should render two page-kpi-card elements', () => { + spectator.component.feeds$ = of([ + { + type: 'kpi', + }, + { + type: 'kpi', + }, + ]); + + spectator.detectChanges(); + + expect(spectator.queryAll('page-kpi-card').length).toEqual(2); + }); + }); + + describe('page-products-card element', () => { + it('should be created', () => { + spectator.component.feeds$ = of([ + { + type: 'products', + }, + ]); + + spectator.detectChanges(); + expect(spectator.query('page-products-card')).toExist(); + }); + + it('should have the feed as [feed] input', () => { + spectator.component.feeds$ = of([ + { + type: 'products', + }, + ]); + + spectator.detectChanges(); + + expect(spectator.debugElement.query(By.css('page-products-card')).componentInstance.feed).toEqual({ + type: 'products', + }); + }); + + it('should render two page-products-card elements', () => { + spectator.component.feeds$ = of([ + { + type: 'products', + }, + { + type: 'products', + }, + ]); + + spectator.detectChanges(); + + expect(spectator.queryAll('page-products-card').length).toEqual(2); + }); + }); +}); diff --git a/apps/page/dashboard/src/lib/dashboard.component.ts b/apps/page/dashboard/src/lib/dashboard.component.ts new file mode 100644 index 000000000..e5d153131 --- /dev/null +++ b/apps/page/dashboard/src/lib/dashboard.component.ts @@ -0,0 +1,20 @@ +import { Component, OnInit } from '@angular/core'; +import { DomainDashboardService } from '@domain/isa'; +import { of } from 'rxjs'; +import { catchError, map } from 'rxjs/operators'; + +@Component({ + selector: 'page-dashboard', + templateUrl: './dashboard.component.html', + styleUrls: ['./dashboard.component.scss'], +}) +export class DashboardComponent implements OnInit { + feeds$ = this._domainIsaDashboardService?.feed()?.pipe( + map((response) => response?.result ?? []), + catchError(() => of([])) + ); + + constructor(private readonly _domainIsaDashboardService: DomainDashboardService) {} + + ngOnInit(): void {} +} diff --git a/apps/page/dashboard/src/lib/dashboard.module.ts b/apps/page/dashboard/src/lib/dashboard.module.ts new file mode 100644 index 000000000..e334c1003 --- /dev/null +++ b/apps/page/dashboard/src/lib/dashboard.module.ts @@ -0,0 +1,16 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { ProductImageModule } from '@cdn/product-image'; +import { UiProgressModule } from '@ui/progress'; +import { UiSliderModule } from '@ui/slider'; +import { DashboardRoutingModule } from './dashboard-routing-module'; +import { DashboardComponent } from './dashboard.component'; +import { KpiCardComponent } from './kpi-card/kpi-card.component'; +import { ProductsCardComponent } from './products-card/product-card.component'; + +@NgModule({ + declarations: [DashboardComponent, KpiCardComponent, ProductsCardComponent], + imports: [CommonModule, DashboardRoutingModule, UiProgressModule, UiSliderModule, ProductImageModule], + exports: [DashboardComponent], +}) +export class DashboardModule {} diff --git a/apps/page/dashboard/src/lib/kpi-card/kpi-card.component.html b/apps/page/dashboard/src/lib/kpi-card/kpi-card.component.html new file mode 100644 index 000000000..ab728b42c --- /dev/null +++ b/apps/page/dashboard/src/lib/kpi-card/kpi-card.component.html @@ -0,0 +1,10 @@ + +
+
{{ feed?.label }}
+
+ {{ item.actual }} +
+
{{ item.label }} von {{ item.target }} schon geschafft
+
+ +
diff --git a/apps/page/dashboard/src/lib/kpi-card/kpi-card.component.scss b/apps/page/dashboard/src/lib/kpi-card/kpi-card.component.scss new file mode 100644 index 000000000..6ae318596 --- /dev/null +++ b/apps/page/dashboard/src/lib/kpi-card/kpi-card.component.scss @@ -0,0 +1,29 @@ +:host { + @apply grid grid-flow-col gap-4 bg-white rounded p-4 shadow; + grid-template-columns: 1fr auto; +} + +.title { + @apply font-bold uppercase; + color: var(--page-dashboard-card-title-color); +} + +.content-wrapper { + @apply grid grid-flow-row; + grid-template-row: auto; +} + +.actual-numbers { + font-size: 3.375rem; + line-height: 3.375rem; + @apply font-bold pt-4; +} + +.target-numbers { + @apply text-sm; + color: var(--page-dashboard-card-title-color); +} + +ui-circle-progress { + @apply self-center; +} diff --git a/apps/page/dashboard/src/lib/kpi-card/kpi-card.component.spec.ts b/apps/page/dashboard/src/lib/kpi-card/kpi-card.component.spec.ts new file mode 100644 index 000000000..a97a76fa7 --- /dev/null +++ b/apps/page/dashboard/src/lib/kpi-card/kpi-card.component.spec.ts @@ -0,0 +1,71 @@ +import { By } from '@angular/platform-browser'; +import { Spectator, createComponentFactory } from '@ngneat/spectator'; +import { UiCircleProgressComponent } from '@ui/progress'; +import { MockComponent } from 'ng-mocks'; +import { KpiCardComponent } from './kpi-card.component'; + +describe('KpiCardComponent', () => { + let spectator: Spectator; + + const createComponent = createComponentFactory({ + component: KpiCardComponent, + declarations: [MockComponent(UiCircleProgressComponent)], + }); + + beforeEach(() => { + spectator = createComponent(); + }); + + it('should create', () => { + expect(spectator.component).toBeTruthy(); + }); + + describe('percentage()', () => { + it('should return the percentage of the current value over the target value', () => { + const current = 10; + const target = 20; + + expect(spectator.component.percentage(current, target)).toBe(50); + }); + }); + + it('should display the title, actual and targets', () => { + spectator.setInput('feed', { + label: 'title', + type: 'kpi', + items: [ + { + actual: 10, + target: 20, + label: 'label', + }, + ], + }); + + spectator.detectChanges(); + + expect(spectator.query('.title')).toHaveText('title'); + expect(spectator.query('.actual-numbers')).toHaveText('10'); + expect(spectator.query('.target-numbers')).toHaveText('20'); + }); + + describe('ui-circle-progress element', () => { + it('should set the [progress] input', () => { + spectator.setInput('feed', { + label: 'title', + type: 'kpi', + items: [ + { + actual: 10, + target: 20, + label: 'label', + }, + ], + }); + + spectator.detectChanges(); + + expect(spectator.debugElement.query(By.css('ui-circle-progress')).componentInstance.progress).toBe(50); + }); + }); +}); diff --git a/apps/page/dashboard/src/lib/kpi-card/kpi-card.component.ts b/apps/page/dashboard/src/lib/kpi-card/kpi-card.component.ts new file mode 100644 index 000000000..1100ecde4 --- /dev/null +++ b/apps/page/dashboard/src/lib/kpi-card/kpi-card.component.ts @@ -0,0 +1,19 @@ +import { Component, ChangeDetectionStrategy, Input } from '@angular/core'; +import { KpiFeed } from '@domain/isa'; + +@Component({ + selector: 'page-kpi-card', + templateUrl: 'kpi-card.component.html', + styleUrls: ['kpi-card.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class KpiCardComponent { + @Input() + feed: KpiFeed; + + constructor() {} + + percentage(current: number, target: number) { + return Math.round((current / target) * 100); + } +} diff --git a/apps/page/dashboard/src/lib/products-card/product-card.component.html b/apps/page/dashboard/src/lib/products-card/product-card.component.html new file mode 100644 index 000000000..b83b4d99e --- /dev/null +++ b/apps/page/dashboard/src/lib/products-card/product-card.component.html @@ -0,0 +1,8 @@ +
+
{{ feed?.label }}
+
+
+ + + + diff --git a/apps/page/dashboard/src/lib/products-card/product-card.component.scss b/apps/page/dashboard/src/lib/products-card/product-card.component.scss new file mode 100644 index 000000000..1d68487b5 --- /dev/null +++ b/apps/page/dashboard/src/lib/products-card/product-card.component.scss @@ -0,0 +1,30 @@ +:host { + @apply grid grid-flow-row gap-4 bg-white rounded p-4 shadow; + + @screen tablet { + @apply grid-flow-col grid-cols-2; + } +} + +.title { + @apply font-bold uppercase; + color: var(--page-dashboard-card-title-color); +} + +::ng-deep page-products-card { + .desc p { + @apply mt-4; + } +} + +ui-slider { + img { + @apply rounded; + height: 160px; + // max-width: aut; + } + + img + img { + @apply ml-4; + } +} diff --git a/apps/page/dashboard/src/lib/products-card/product-card.component.spec.ts b/apps/page/dashboard/src/lib/products-card/product-card.component.spec.ts new file mode 100644 index 000000000..1dcbce912 --- /dev/null +++ b/apps/page/dashboard/src/lib/products-card/product-card.component.spec.ts @@ -0,0 +1,72 @@ +import { ProductImagePipe } from '@cdn/product-image'; +import { createComponentFactory, Spectator } from '@ngneat/spectator'; +import { UiSliderComponent } from '@ui/slider'; +import { MockComponent, MockPipe } from 'ng-mocks'; +import { ProductsCardComponent } from './product-card.component'; +describe('ProductsCardComponent', () => { + let spectator: Spectator; + + const createComponent = createComponentFactory({ + component: ProductsCardComponent, + declarations: [MockComponent(UiSliderComponent), MockPipe(ProductImagePipe, (ean) => 'ean-' + ean)], + }); + + beforeEach(() => { + spectator = createComponent(); + }); + + it('should create', () => { + expect(spectator.component).toBeTruthy(); + }); + + it('should render the title', () => { + spectator.setInput('feed', { + label: 'title', + type: 'products', + items: [], + }); + + spectator.detectChanges(); + + expect(spectator.query('.title')).toHaveText('title'); + }); + + it('should render the .desc innerHtml', () => { + spectator.setInput('feed', { + label: 'title', + type: 'products', + desc: '

Hello Unit Test

', + items: [], + }); + + spectator.detectChanges(); + + expect(spectator.query('.desc').innerHTML).toBe('

Hello Unit Test

'); + }); + + it('should render the img provided in items', () => { + spectator.setInput('feed', { + label: 'title', + type: 'products', + items: [ + { + product: { + ean: '1234567890123', + name: 'Product 1', + }, + }, + { + product: { + ean: '1234567890124', + name: 'Product 2', + }, + }, + ], + }); + spectator.detectChanges(); + expect((spectator.queryAll('img')[0] as any).src).toContain('ean-1234567890123'); + expect((spectator.queryAll('img')[1] as any).src).toContain('ean-1234567890124'); + expect((spectator.queryAll('img')[0] as any).alt).toBe('Product 1'); + expect((spectator.queryAll('img')[1] as any).alt).toBe('Product 2'); + }); +}); diff --git a/apps/page/dashboard/src/lib/products-card/product-card.component.ts b/apps/page/dashboard/src/lib/products-card/product-card.component.ts new file mode 100644 index 000000000..7cb76b82c --- /dev/null +++ b/apps/page/dashboard/src/lib/products-card/product-card.component.ts @@ -0,0 +1,15 @@ +import { Component, ChangeDetectionStrategy, Input } from '@angular/core'; +import { ProductsFeed } from '@domain/isa'; + +@Component({ + selector: 'page-products-card', + templateUrl: 'product-card.component.html', + styleUrls: ['product-card.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ProductsCardComponent { + @Input() + feed: ProductsFeed; + + constructor() {} +} diff --git a/apps/page/dashboard/src/public-api.ts b/apps/page/dashboard/src/public-api.ts new file mode 100644 index 000000000..4de27dff7 --- /dev/null +++ b/apps/page/dashboard/src/public-api.ts @@ -0,0 +1,6 @@ +/* + * Public API Surface of dashboard + */ + +export * from './lib/dashboard.component'; +export * from './lib/dashboard.module'; diff --git a/apps/page/dashboard/src/test.ts b/apps/page/dashboard/src/test.ts new file mode 100644 index 000000000..b22c5d4af --- /dev/null +++ b/apps/page/dashboard/src/test.ts @@ -0,0 +1,25 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import 'zone.js'; +import 'zone.js/testing'; +import { getTestBed } from '@angular/core/testing'; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; + +declare const require: { + context( + path: string, + deep?: boolean, + filter?: RegExp + ): { + keys(): string[]; + (id: string): T; + }; +}; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), { teardown: { destroyAfterEach: true } }); + +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().map(context); diff --git a/apps/page/dashboard/tsconfig.lib.json b/apps/page/dashboard/tsconfig.lib.json new file mode 100644 index 000000000..a7141c543 --- /dev/null +++ b/apps/page/dashboard/tsconfig.lib.json @@ -0,0 +1,20 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "outDir": "../../../out-tsc/lib", + "target": "es2015", + "declaration": true, + "declarationMap": true, + "inlineSources": true, + "types": [], + "lib": [ + "dom", + "es2018" + ] + }, + "exclude": [ + "src/test.ts", + "**/*.spec.ts" + ] +} diff --git a/apps/page/dashboard/tsconfig.lib.prod.json b/apps/page/dashboard/tsconfig.lib.prod.json new file mode 100644 index 000000000..06de549e1 --- /dev/null +++ b/apps/page/dashboard/tsconfig.lib.prod.json @@ -0,0 +1,10 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.lib.json", + "compilerOptions": { + "declarationMap": false + }, + "angularCompilerOptions": { + "compilationMode": "partial" + } +} diff --git a/apps/page/dashboard/tsconfig.spec.json b/apps/page/dashboard/tsconfig.spec.json new file mode 100644 index 000000000..85392ee8f --- /dev/null +++ b/apps/page/dashboard/tsconfig.spec.json @@ -0,0 +1,17 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "outDir": "../../../out-tsc/spec", + "types": [ + "jasmine" + ] + }, + "files": [ + "src/test.ts" + ], + "include": [ + "**/*.spec.ts", + "**/*.d.ts" + ] +} diff --git a/apps/page/goods-in/src/lib/goods-in-cleanup-list/goods-in-cleanup-list.component.html b/apps/page/goods-in/src/lib/goods-in-cleanup-list/goods-in-cleanup-list.component.html index e1515b8ca..f2c918990 100644 --- a/apps/page/goods-in/src/lib/goods-in-cleanup-list/goods-in-cleanup-list.component.html +++ b/apps/page/goods-in/src/lib/goods-in-cleanup-list/goods-in-cleanup-list.component.html @@ -16,6 +16,7 @@ [loading]="loading$ | async" [deltaEnd]="150" [itemLength]="itemLength$ | async" + [containerHeight]="'24.5'" > @@ -66,11 +67,11 @@ - + Zur Bestellpostensuche - + Zur Remission
diff --git a/apps/page/goods-in/src/lib/goods-in-cleanup-list/goods-in-cleanup-list.component.scss b/apps/page/goods-in/src/lib/goods-in-cleanup-list/goods-in-cleanup-list.component.scss index 4e0327ac1..ff2d3d63b 100644 --- a/apps/page/goods-in/src/lib/goods-in-cleanup-list/goods-in-cleanup-list.component.scss +++ b/apps/page/goods-in/src/lib/goods-in-cleanup-list/goods-in-cleanup-list.component.scss @@ -53,6 +53,10 @@ } } +::ng-deep page-goods-in-cleanup-list ui-scroll-container .cta-scroll { + bottom: 35px !important; +} + shared-goods-in-out-order-group-item { @apply cursor-pointer; } diff --git a/apps/page/goods-in/src/lib/goods-in-cleanup-list/goods-in-cleanup-list.component.ts b/apps/page/goods-in/src/lib/goods-in-cleanup-list/goods-in-cleanup-list.component.ts index 2cc49e6a4..c33b9b671 100644 --- a/apps/page/goods-in/src/lib/goods-in-cleanup-list/goods-in-cleanup-list.component.ts +++ b/apps/page/goods-in/src/lib/goods-in-cleanup-list/goods-in-cleanup-list.component.ts @@ -2,6 +2,7 @@ import { ChangeDetectionStrategy, Component, OnDestroy, OnInit, QueryList, ViewC import { ActivatedRoute, Router } from '@angular/router'; import { BreadcrumbService } from '@core/breadcrumb'; import { CommandService } from '@core/command'; +import { Config } from '@core/config'; import { OrderItemsContext } from '@domain/oms'; import { GoodsInOutOrderGroupItemComponent } from '@shared/goods-in-out'; import { KeyValueDTOOfStringAndString, OrderItemListItemDTO } from '@swagger/oms'; @@ -75,7 +76,8 @@ export class GoodsInCleanupListComponent implements OnInit, OnDestroy { private _commandService: CommandService, private _modal: UiModalService, private _route: ActivatedRoute, - private _router: Router + private _router: Router, + private readonly _config: Config ) {} ngOnInit(): void { @@ -92,16 +94,19 @@ export class GoodsInCleanupListComponent implements OnInit, OnDestroy { async createBreadcrumb() { await this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({ - key: 'goods-in', - name: 'Abholfachbereinigung', - path: '/goods/in/cleanup', + key: this._config.get('process.ids.goodsIn'), + name: 'Abholfachbereinigungsliste', + path: '/filiale/goods/in/cleanup', section: 'branch', tags: ['goods-in', 'cleanup'], }); } async updateBreadcrumb() { - const crumbs = await this._breadcrumb.getBreadcrumbsByKeyAndTags$('goods-in', ['goods-in', 'cleanup']).pipe(first()).toPromise(); + const crumbs = await this._breadcrumb + .getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'cleanup']) + .pipe(first()) + .toPromise(); for (const crumb of crumbs) { this._breadcrumb.patchBreadcrumb(crumb.id, { name: crumb.name, @@ -111,8 +116,14 @@ export class GoodsInCleanupListComponent implements OnInit, OnDestroy { } async removeBreadcrumbs() { - const detailsCrumbs = await this._breadcrumb.getBreadcrumbsByKeyAndTags$('goods-in', ['goods-in', 'details']).pipe(first()).toPromise(); - const editCrumbs = await this._breadcrumb.getBreadcrumbsByKeyAndTags$('goods-in', ['goods-in', 'edit']).pipe(first()).toPromise(); + const detailsCrumbs = await this._breadcrumb + .getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'details']) + .pipe(first()) + .toPromise(); + const editCrumbs = await this._breadcrumb + .getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'edit']) + .pipe(first()) + .toPromise(); detailsCrumbs.forEach((crumb) => { this._breadcrumb.removeBreadcrumb(crumb.id, true); @@ -167,7 +178,7 @@ export class GoodsInCleanupListComponent implements OnInit, OnDestroy { const processingStatus = orderItem.processingStatus; const orderItemId = orderItem.orderItemId; - this._router.navigate([`/goods/in/details/order/${encodeURIComponent(orderNumber)}/item/${orderItemId}/${processingStatus}`]); + this._router.navigate([`/filiale/goods/in/details/order/${encodeURIComponent(orderNumber)}/item/${orderItemId}/${processingStatus}`]); } async handleAction(action: KeyValueDTOOfStringAndString) { diff --git a/apps/page/goods-in/src/lib/goods-in-details/goods-in-details.component.ts b/apps/page/goods-in/src/lib/goods-in-details/goods-in-details.component.ts index 96d06cfb0..9eeb31753 100644 --- a/apps/page/goods-in/src/lib/goods-in-details/goods-in-details.component.ts +++ b/apps/page/goods-in/src/lib/goods-in-details/goods-in-details.component.ts @@ -1,6 +1,7 @@ import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { BreadcrumbService } from '@core/breadcrumb'; +import { Config } from '@core/config'; import { DomainGoodsService, DomainOmsService, OrderItemsContext } from '@domain/oms'; import { ComponentStore, tapResponse } from '@ngrx/component-store'; import { OrderItemListItemDTO, OrderItemProcessingStatusValue } from '@swagger/oms'; @@ -58,7 +59,8 @@ export class GoodsInDetailsComponent extends ComponentStore { this._breadcrumb.removeBreadcrumb(crumb.id, true); @@ -92,7 +97,10 @@ export class GoodsInDetailsComponent extends ComponentStore { this._breadcrumb.removeBreadcrumb(crumb.id, true); @@ -102,9 +110,11 @@ export class GoodsInDetailsComponent extends ComponentStore true).compartmentCode), filter_orderitemprocessingstatus: orderItems.find((_) => true).processingStatus, }; - this._router.navigate(['/goods/in', 'results'], { queryParams }); + this._router.navigate(['/filiale/goods/in', 'results'], { queryParams }); } else { // Search by processingStatus and orderNumber const queryParams = { @@ -185,7 +197,7 @@ export class GoodsInDetailsComponent extends ComponentStore a.indexOf(v) === i) .join(';'), }; - this._router.navigate(['/goods/in', 'results'], { queryParams }); + this._router.navigate(['/filiale/goods/in', 'results'], { queryParams }); } } } diff --git a/apps/page/goods-in/src/lib/goods-in-edit/goods-in-edit.component.html b/apps/page/goods-in/src/lib/goods-in-edit/goods-in-edit.component.html index 79d8d6e2e..c3f3c65e3 100644 --- a/apps/page/goods-in/src/lib/goods-in-edit/goods-in-edit.component.html +++ b/apps/page/goods-in/src/lib/goods-in-edit/goods-in-edit.component.html @@ -1,3 +1,3 @@ -
+
diff --git a/apps/page/goods-in/src/lib/goods-in-edit/goods-in-edit.component.scss b/apps/page/goods-in/src/lib/goods-in-edit/goods-in-edit.component.scss index a113f1ddb..3a998ef57 100644 --- a/apps/page/goods-in/src/lib/goods-in-edit/goods-in-edit.component.scss +++ b/apps/page/goods-in/src/lib/goods-in-edit/goods-in-edit.component.scss @@ -1,4 +1,4 @@ -.container { +div { @apply overflow-y-scroll; height: calc(100vh - 280px); } diff --git a/apps/page/goods-in/src/lib/goods-in-edit/goods-in-edit.component.ts b/apps/page/goods-in/src/lib/goods-in-edit/goods-in-edit.component.ts index 7b1d47856..cee3293c5 100644 --- a/apps/page/goods-in/src/lib/goods-in-edit/goods-in-edit.component.ts +++ b/apps/page/goods-in/src/lib/goods-in-edit/goods-in-edit.component.ts @@ -1,6 +1,7 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { BreadcrumbService } from '@core/breadcrumb'; +import { Config } from '@core/config'; import { DomainGoodsService } from '@domain/oms'; import { Observable } from 'rxjs'; import { map, shareReplay, switchMap } from 'rxjs/operators'; @@ -25,7 +26,8 @@ export class GoodsInEditComponent implements OnInit { private _activatedRoute: ActivatedRoute, private _breadcrumb: BreadcrumbService, private _domainGoodsInService: DomainGoodsService, - private _router: Router + private _router: Router, + private readonly _config: Config ) {} ngOnInit() { @@ -37,9 +39,9 @@ export class GoodsInEditComponent implements OnInit { const orderItemId = this._activatedRoute.snapshot.params.orderItemId; const processingStatus = this._activatedRoute.snapshot.params.processingStatus; await this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({ - key: 'goods-in', + key: this._config.get('process.ids.goodsIn'), name: 'Bearbeiten', - path: `/goods/in/details/order/${encodeURIComponent(orderNumber)}/item/${orderItemId}/${processingStatus}/edit`, + path: `/filiale/goods/in/details/order/${encodeURIComponent(orderNumber)}/item/${orderItemId}/${processingStatus}/edit`, section: 'branch', tags: ['goods-in', 'edit', orderNumber], }); @@ -49,6 +51,6 @@ export class GoodsInEditComponent implements OnInit { const orderNumber = this._activatedRoute.snapshot.params.orderNumber; const orderItemId = this._activatedRoute.snapshot.params.orderItemId; const processingStatus = options?.processingStatus ? options.processingStatus : this._activatedRoute.snapshot.params.processingStatus; - this._router.navigate([`/goods/in/details/order/${encodeURIComponent(orderNumber)}/item/${orderItemId}/${processingStatus}`]); + this._router.navigate([`/filiale/goods/in/details/order/${encodeURIComponent(orderNumber)}/item/${orderItemId}/${processingStatus}`]); } } diff --git a/apps/page/goods-in/src/lib/goods-in-list/goods-in-list-item/goods-in-list-item.component.scss b/apps/page/goods-in/src/lib/goods-in-list/goods-in-list-item/goods-in-list-item.component.scss index 3e0f3f1af..ebe502464 100644 --- a/apps/page/goods-in/src/lib/goods-in-list/goods-in-list-item/goods-in-list-item.component.scss +++ b/apps/page/goods-in/src/lib/goods-in-list/goods-in-list-item/goods-in-list-item.component.scss @@ -183,6 +183,10 @@ @apply text-brand; } +h3 { + @apply font-bold; +} + @media (min-width: 1025px) { .header { .customer-name { diff --git a/apps/page/goods-in/src/lib/goods-in-list/goods-in-list.component.html b/apps/page/goods-in/src/lib/goods-in-list/goods-in-list.component.html index 0a8839fb0..0f5bdd691 100644 --- a/apps/page/goods-in/src/lib/goods-in-list/goods-in-list.component.html +++ b/apps/page/goods-in/src/lib/goods-in-list/goods-in-list.component.html @@ -23,6 +23,7 @@ (reachEnd)="loadMore()" [deltaEnd]="150" [itemLength]="itemLength$ | async" + [containerHeight]="'27'" > { this._breadcrumb.removeBreadcrumb(crumb.id, true); @@ -157,7 +165,10 @@ export class GoodsInListComponent implements OnInit, AfterViewInit, OnDestroy { const take = this._route?.snapshot?.queryParams?.take; if (queryParams) { - const crumbs = await this._breadcrumb.getBreadcrumbsByKeyAndTags$('goods-in', ['goods-in', 'list']).pipe(first()).toPromise(); + const crumbs = await this._breadcrumb + .getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'list']) + .pipe(first()) + .toPromise(); const params = { ...queryParams, scroll_position, take }; for (const crumb of crumbs) { @@ -170,9 +181,9 @@ export class GoodsInListComponent implements OnInit, AfterViewInit, OnDestroy { async createBreadcrumb(queryParams: Params) { await this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({ - key: 'goods-in', + key: this._config.get('process.ids.goodsIn'), name: 'Wareneingangsliste', - path: '/goods/in/list', + path: '/filiale/goods/in/list', section: 'branch', params: queryParams, tags: ['goods-in', 'list'], diff --git a/apps/page/goods-in/src/lib/goods-in-list/goods-in-list.store.ts b/apps/page/goods-in/src/lib/goods-in-list/goods-in-list.store.ts index 9223b5ad8..6bb655d0c 100644 --- a/apps/page/goods-in/src/lib/goods-in-list/goods-in-list.store.ts +++ b/apps/page/goods-in/src/lib/goods-in-list/goods-in-list.store.ts @@ -85,7 +85,7 @@ export class GoodsInListStore extends ComponentStore { this._searchResultSubject.next(res); if (res.hits > 1) { - const path = '/goods/in/list/'; + const path = '/filiale/goods/in/list/'; if (!this._router.isActive(path, false)) { this._router.navigate([path], { queryParams: { ...this.filter.getQueryParams(), take: res.result.length + _results?.length }, diff --git a/apps/page/goods-in/src/lib/goods-in-remission-preview/goods-in-remission-preview.component.html b/apps/page/goods-in/src/lib/goods-in-remission-preview/goods-in-remission-preview.component.html index a7e5aa157..34306a340 100644 --- a/apps/page/goods-in/src/lib/goods-in-remission-preview/goods-in-remission-preview.component.html +++ b/apps/page/goods-in/src/lib/goods-in-remission-preview/goods-in-remission-preview.component.html @@ -16,6 +16,7 @@ [loading]="loading$ | async" [deltaEnd]="150" [itemLength]="itemLength$ | async" + [containerHeight]="'24.5'" > diff --git a/apps/page/goods-in/src/lib/goods-in-remission-preview/goods-in-remission-preview.component.scss b/apps/page/goods-in/src/lib/goods-in-remission-preview/goods-in-remission-preview.component.scss index 271a34278..873f043da 100644 --- a/apps/page/goods-in/src/lib/goods-in-remission-preview/goods-in-remission-preview.component.scss +++ b/apps/page/goods-in/src/lib/goods-in-remission-preview/goods-in-remission-preview.component.scss @@ -52,3 +52,7 @@ shared-goods-in-out-order-group-item { @apply cursor-pointer; } + +::ng-deep page-goods-in-remission-preview ui-scroll-container .cta-scroll { + bottom: 35px !important; +} diff --git a/apps/page/goods-in/src/lib/goods-in-remission-preview/goods-in-remission-preview.component.ts b/apps/page/goods-in/src/lib/goods-in-remission-preview/goods-in-remission-preview.component.ts index f4ee7f1ee..0cb9b11a4 100644 --- a/apps/page/goods-in/src/lib/goods-in-remission-preview/goods-in-remission-preview.component.ts +++ b/apps/page/goods-in/src/lib/goods-in-remission-preview/goods-in-remission-preview.component.ts @@ -1,17 +1,13 @@ import { ChangeDetectionStrategy, Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { Breadcrumb, BreadcrumbService } from '@core/breadcrumb'; -import { CommandService } from '@core/command'; -import { OrderItemsContext } from '@domain/oms'; +import { BreadcrumbService } from '@core/breadcrumb'; import { KeyValueDTOOfStringAndString, OrderItemListItemDTO } from '@swagger/oms'; import { UiErrorModalComponent, UiModalService } from '@ui/modal'; import { UiScrollContainerComponent } from '@ui/scroll-container'; import { BehaviorSubject, combineLatest, Subject } from 'rxjs'; import { first, map, shareReplay, takeUntil } from 'rxjs/operators'; import { GoodsInRemissionPreviewStore } from './goods-in-remission-preview.store'; -import { Store } from '@ngxs/store'; -import { InitRemissionState } from 'apps/sales/src/app/core/store/actions/remission.actions'; -import { ResetBreadcrumbsTo } from 'apps/sales/src/app/core/store/actions/breadcrumb.actions'; +import { Config } from '@core/config'; @Component({ selector: 'page-goods-in-remission-preview', @@ -57,9 +53,8 @@ export class GoodsInRemissionPreviewComponent implements OnInit, OnDestroy { private _store: GoodsInRemissionPreviewStore, private _route: ActivatedRoute, private _router: Router, - private _commandService: CommandService, private _modal: UiModalService, - private _ngxsStore: Store + private _config: Config ) {} ngOnInit(): void { @@ -76,16 +71,19 @@ export class GoodsInRemissionPreviewComponent implements OnInit, OnDestroy { async createBreadcrumb() { await this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({ - key: 'goods-in', + key: this._config.get('process.ids.goodsIn'), name: 'Abholfachremissionsvorschau', - path: '/goods/in/preview', + path: '/filiale/goods/in/preview', section: 'branch', tags: ['goods-in', 'preview'], }); } async updateBreadcrumb() { - const crumbs = await this._breadcrumb.getBreadcrumbsByKeyAndTags$('goods-in', ['goods-in', 'preview']).pipe(first()).toPromise(); + const crumbs = await this._breadcrumb + .getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'preview']) + .pipe(first()) + .toPromise(); for (const crumb of crumbs) { this._breadcrumb.patchBreadcrumb(crumb.id, { name: crumb.name, @@ -95,8 +93,14 @@ export class GoodsInRemissionPreviewComponent implements OnInit, OnDestroy { } async removeBreadcrumbs() { - const detailsCrumbs = await this._breadcrumb.getBreadcrumbsByKeyAndTags$('goods-in', ['goods-in', 'details']).pipe(first()).toPromise(); - const editCrumbs = await this._breadcrumb.getBreadcrumbsByKeyAndTags$('goods-in', ['goods-in', 'edit']).pipe(first()).toPromise(); + const detailsCrumbs = await this._breadcrumb + .getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'details']) + .pipe(first()) + .toPromise(); + const editCrumbs = await this._breadcrumb + .getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'edit']) + .pipe(first()) + .toPromise(); detailsCrumbs.forEach((crumb) => { this._breadcrumb.removeBreadcrumb(crumb.id, true); @@ -124,22 +128,10 @@ export class GoodsInRemissionPreviewComponent implements OnInit, OnDestroy { } async navigateToRemission() { - this._ngxsStore.dispatch( - new ResetBreadcrumbsTo( - { - name: 'Remission', - path: '/remission/create', - }, - 'remission', - true - ) - ); - this._ngxsStore.dispatch(new InitRemissionState()); - // TODO: // In der Remission Filter Abholfach setzen - await this._router.navigate(['/remission/create']); + await this._router.navigate(['/filiale/remission/create']); } navigateToDetails(orderItem: OrderItemListItemDTO) { @@ -147,7 +139,7 @@ export class GoodsInRemissionPreviewComponent implements OnInit, OnDestroy { const processingStatus = orderItem.processingStatus; const orderItemId = orderItem.orderItemId; - this._router.navigate([`/goods/in/details/order/${encodeURIComponent(orderNumber)}/item/${orderItemId}/${processingStatus}`]); + this._router.navigate([`/filiale/goods/in/details/order/${encodeURIComponent(orderNumber)}/item/${orderItemId}/${processingStatus}`]); } async handleAction(action: KeyValueDTOOfStringAndString) { diff --git a/apps/page/goods-in/src/lib/goods-in-reservation/goods-in-reservation.component.html b/apps/page/goods-in/src/lib/goods-in-reservation/goods-in-reservation.component.html index 5cee66d0d..5ec9ee5fe 100644 --- a/apps/page/goods-in/src/lib/goods-in-reservation/goods-in-reservation.component.html +++ b/apps/page/goods-in/src/lib/goods-in-reservation/goods-in-reservation.component.html @@ -17,6 +17,7 @@ (reachEnd)="loadMore()" [deltaEnd]="150" [itemLength]="itemLength$ | async" + [containerHeight]="'24.5'" > diff --git a/apps/page/goods-in/src/lib/goods-in-reservation/goods-in-reservation.component.scss b/apps/page/goods-in/src/lib/goods-in-reservation/goods-in-reservation.component.scss index e7467c956..30978e47c 100644 --- a/apps/page/goods-in/src/lib/goods-in-reservation/goods-in-reservation.component.scss +++ b/apps/page/goods-in/src/lib/goods-in-reservation/goods-in-reservation.component.scss @@ -56,3 +56,7 @@ shared-goods-in-out-order-group-item { @apply cursor-pointer; } + +::ng-deep page-goods-in-reservation ui-scroll-container .cta-scroll { + bottom: 35px !important; +} diff --git a/apps/page/goods-in/src/lib/goods-in-reservation/goods-in-reservation.component.ts b/apps/page/goods-in/src/lib/goods-in-reservation/goods-in-reservation.component.ts index 7139c919e..4791e44ff 100644 --- a/apps/page/goods-in/src/lib/goods-in-reservation/goods-in-reservation.component.ts +++ b/apps/page/goods-in/src/lib/goods-in-reservation/goods-in-reservation.component.ts @@ -2,6 +2,7 @@ import { ChangeDetectionStrategy, Component, OnDestroy, OnInit, QueryList, ViewC import { ActivatedRoute, Router } from '@angular/router'; import { BreadcrumbService } from '@core/breadcrumb'; import { CommandService } from '@core/command'; +import { Config } from '@core/config'; import { OrderItemsContext } from '@domain/oms'; import { GoodsInOutOrderGroupItemComponent } from '@shared/goods-in-out'; import { KeyValueDTOOfStringAndString, OrderItemListItemDTO } from '@swagger/oms'; @@ -75,7 +76,8 @@ export class GoodsInReservationComponent implements OnInit, OnDestroy { private _commandService: CommandService, private _route: ActivatedRoute, private _router: Router, - private _modal: UiModalService + private _modal: UiModalService, + private readonly _config: Config ) {} ngOnInit() { @@ -92,16 +94,19 @@ export class GoodsInReservationComponent implements OnInit, OnDestroy { async createBreadcrumb() { await this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({ - key: 'goods-in', + key: this._config.get('process.ids.goodsIn'), name: 'Reservierungen', - path: '/goods/in/reservation', + path: '/filiale/goods/in/reservation', section: 'branch', tags: ['goods-in', 'reservation'], }); } async updateBreadcrumb() { - const crumbs = await this._breadcrumb.getBreadcrumbsByKeyAndTags$('goods-in', ['goods-in', 'reservation']).pipe(first()).toPromise(); + const crumbs = await this._breadcrumb + .getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'reservation']) + .pipe(first()) + .toPromise(); for (const crumb of crumbs) { this._breadcrumb.patchBreadcrumb(crumb.id, { name: crumb.name, @@ -111,8 +116,14 @@ export class GoodsInReservationComponent implements OnInit, OnDestroy { } async removeBreadcrumbs() { - const detailsCrumbs = await this._breadcrumb.getBreadcrumbsByKeyAndTags$('goods-in', ['goods-in', 'details']).pipe(first()).toPromise(); - const editCrumbs = await this._breadcrumb.getBreadcrumbsByKeyAndTags$('goods-in', ['goods-in', 'edit']).pipe(first()).toPromise(); + const detailsCrumbs = await this._breadcrumb + .getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'details']) + .pipe(first()) + .toPromise(); + const editCrumbs = await this._breadcrumb + .getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'edit']) + .pipe(first()) + .toPromise(); detailsCrumbs.forEach((crumb) => { this._breadcrumb.removeBreadcrumb(crumb.id, true); @@ -128,7 +139,7 @@ export class GoodsInReservationComponent implements OnInit, OnDestroy { const processingStatus = orderItem.processingStatus; const orderItemId = orderItem.orderItemId; - this._router.navigate([`/goods/in/details/order/${encodeURIComponent(orderNumber)}/item/${orderItemId}/${processingStatus}`]); + this._router.navigate([`/filiale/goods/in/details/order/${encodeURIComponent(orderNumber)}/item/${orderItemId}/${processingStatus}`]); } initInitialSearch() { diff --git a/apps/page/goods-in/src/lib/goods-in-search/goods-in-search-filter/goods-in-search-filter.component.scss b/apps/page/goods-in/src/lib/goods-in-search/goods-in-search-filter/goods-in-search-filter.component.scss index ec095072e..fc64e2571 100644 --- a/apps/page/goods-in/src/lib/goods-in-search/goods-in-search-filter/goods-in-search-filter.component.scss +++ b/apps/page/goods-in/src/lib/goods-in-search/goods-in-search-filter/goods-in-search-filter.component.scss @@ -1,5 +1,5 @@ :host { - @apply block bg-branch; + @apply block; } .goods-in-search-filter-content { @@ -8,13 +8,13 @@ } .btn-close { - @apply absolute text-cool-grey top-0 right-4 outline-none border-none bg-transparent; + @apply absolute text-cool-grey top-3 right-4 outline-none border-none bg-transparent; } .goods-in-search-filter-content-main { @apply px-4; h1.title { - @apply text-center; + @apply text-center text-3xl font-bold py-4; } } diff --git a/apps/page/goods-in/src/lib/goods-in-search/goods-in-search-filter/goods-in-search-filter.component.ts b/apps/page/goods-in/src/lib/goods-in-search/goods-in-search-filter/goods-in-search-filter.component.ts index a309c0b3a..91452802e 100644 --- a/apps/page/goods-in/src/lib/goods-in-search/goods-in-search-filter/goods-in-search-filter.component.ts +++ b/apps/page/goods-in/src/lib/goods-in-search/goods-in-search-filter/goods-in-search-filter.component.ts @@ -1,6 +1,7 @@ import { Component, ChangeDetectionStrategy, Output, EventEmitter, ChangeDetectorRef, OnInit, OnDestroy } from '@angular/core'; import { Router } from '@angular/router'; import { BreadcrumbService } from '@core/breadcrumb'; +import { Config } from '@core/config'; import { UiFilter } from '@ui/filter'; import { Observable, Subject } from 'rxjs'; import { first, take, takeUntil } from 'rxjs/operators'; @@ -28,7 +29,8 @@ export class GoodsInSearchFilterComponent implements OnInit, OnDestroy { private _goodsInSearchStore: GoodsInSearchStore, private _breadcrumb: BreadcrumbService, private _cdr: ChangeDetectorRef, - private _router: Router + private _router: Router, + private _config: Config ) {} ngOnInit() { @@ -70,12 +72,14 @@ export class GoodsInSearchFilterComponent implements OnInit, OnDestroy { if (result.hits === 1) { const orderItem = result.result[0]; this._router.navigate([ - `/goods/in/details/order/${encodeURIComponent(orderItem.orderNumber)}/item/${orderItem.orderItemId}/${ + `/filiale/goods/in/details/order/${encodeURIComponent(orderItem.orderNumber)}/item/${orderItem.orderItemId}/${ orderItem.processingStatus }`, ]); } else { - this._router.navigate(['goods', 'in', 'results'], { queryParams: this._goodsInSearchStore.filter.getQueryParams() }); + this._router.navigate(['/filiale', 'goods', 'in', 'results'], { + queryParams: this._goodsInSearchStore.filter.getQueryParams(), + }); } this.close.emit(); @@ -92,9 +96,9 @@ export class GoodsInSearchFilterComponent implements OnInit, OnDestroy { async updateBreadcrumb() { await this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({ - key: 'goods-in', + key: this._config.get('process.ids.goodsIn'), name: 'Abholfach', - path: '/goods/in', + path: '/filiale/goods/in', tags: ['goods-in', 'main', 'filter'], section: 'branch', params: this._goodsInSearchStore.filter?.getQueryParams(), diff --git a/apps/page/goods-in/src/lib/goods-in-search/goods-in-search.component.html b/apps/page/goods-in/src/lib/goods-in-search/goods-in-search.component.html index b68973604..3b45440c6 100644 --- a/apps/page/goods-in/src/lib/goods-in-search/goods-in-search.component.html +++ b/apps/page/goods-in/src/lib/goods-in-search/goods-in-search.component.html @@ -1,8 +1,13 @@ - - + + + diff --git a/apps/page/goods-in/src/lib/goods-in-search/goods-in-search.component.scss b/apps/page/goods-in/src/lib/goods-in-search/goods-in-search.component.scss index c932257b2..cb8c3496e 100644 --- a/apps/page/goods-in/src/lib/goods-in-search/goods-in-search.component.scss +++ b/apps/page/goods-in/src/lib/goods-in-search/goods-in-search.component.scss @@ -1,11 +1,10 @@ :host { - @apply block relative; + @apply flex flex-col w-full box-content relative; } .filter { - @apply absolute font-sans flex items-center font-bold bg-gray-400 border-0 text-regular py-px-8 px-px-15 rounded-filter justify-center right-0 top-0; + @apply absolute font-sans flex items-center font-bold bg-gray-400 border-0 text-regular -top-12 right-0 py-px-8 px-px-15 rounded-filter justify-center z-sticky; min-width: 106px; - margin-top: -53px; .label { @apply ml-px-5; @@ -15,8 +14,3 @@ @apply bg-active-branch text-white ml-px-5; } } - -page-goods-in-search-filter { - @apply fixed right-0 bottom-0 left-0 z-fixed; - top: 136px; -} diff --git a/apps/page/goods-in/src/lib/goods-in-search/goods-in-search.component.ts b/apps/page/goods-in/src/lib/goods-in-search/goods-in-search.component.ts index cbac76ca2..613343d1a 100644 --- a/apps/page/goods-in/src/lib/goods-in-search/goods-in-search.component.ts +++ b/apps/page/goods-in/src/lib/goods-in-search/goods-in-search.component.ts @@ -2,9 +2,11 @@ import { animate, style, transition, trigger } from '@angular/animations'; import { Component, ChangeDetectionStrategy, OnInit, OnDestroy } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { BreadcrumbService } from '@core/breadcrumb'; +import { Config } from '@core/config'; import { UiFilterAutocompleteProvider, UiFilterScanProvider } from '@ui/filter'; +import { isEqual } from 'lodash'; import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; +import { filter, first, map, takeUntil, withLatestFrom } from 'rxjs/operators'; import { GoodsInSearchStore } from './goods-in-search.store'; import { GoodsInSearchMainAutocompleteProvider } from './providers/goods-in-search-main-autocomplete.provider'; @@ -39,10 +41,21 @@ export class GoodsInSearchComponent implements OnInit, OnDestroy { private _onDestroy$ = new Subject(); showFilterOverlay = false; + initialFilter$ = this._goodsInSearchStore.filter$.pipe( + filter((filter) => !!filter), + first() + ); + + hasFilter$ = this._goodsInSearchStore.filter$.pipe( + withLatestFrom(this.initialFilter$), + map(([filter, initialFilter]) => !isEqual(filter?.getQueryParams(), initialFilter?.getQueryParams())) + ); + constructor( private _goodsInSearchStore: GoodsInSearchStore, private _breadcrumb: BreadcrumbService, - private _activatedRoute: ActivatedRoute + private _activatedRoute: ActivatedRoute, + private readonly _config: Config ) {} ngOnInit() { @@ -56,9 +69,9 @@ export class GoodsInSearchComponent implements OnInit, OnDestroy { }); this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({ - key: 'goods-in', + key: this._config.get('process.ids.goodsIn'), name: 'Abholfach', - path: '/goods/in', + path: '/filiale/goods/in', tags: ['goods-in', 'main', 'filter'], section: 'branch', }); diff --git a/apps/page/goods-in/src/lib/goods-in-search/goods-in-search.module.ts b/apps/page/goods-in/src/lib/goods-in-search/goods-in-search.module.ts index 1996feca5..ec2ba1a4f 100644 --- a/apps/page/goods-in/src/lib/goods-in-search/goods-in-search.module.ts +++ b/apps/page/goods-in/src/lib/goods-in-search/goods-in-search.module.ts @@ -6,9 +6,10 @@ import { UiIconModule } from '@ui/icon'; import { GoodsInSearchFilterComponent } from './goods-in-search-filter'; import { RouterModule } from '@angular/router'; import { UiFilterNextModule } from '@ui/filter'; +import { ShellFilterOverlayModule } from '@shell/filter-overlay'; @NgModule({ - imports: [CommonModule, RouterModule, UiIconModule, UiFilterNextModule], + imports: [CommonModule, RouterModule, UiIconModule, UiFilterNextModule, ShellFilterOverlayModule], exports: [GoodsInSearchComponent], declarations: [GoodsInSearchComponent, GoodsInSearchFilterComponent], }) diff --git a/apps/page/goods-in/src/lib/goods-in-search/search-main/goods-in-search-main.component.html b/apps/page/goods-in/src/lib/goods-in-search/search-main/goods-in-search-main.component.html index 43d013659..c50965ddc 100644 --- a/apps/page/goods-in/src/lib/goods-in-search/search-main/goods-in-search-main.component.html +++ b/apps/page/goods-in/src/lib/goods-in-search/search-main/goods-in-search-main.component.html @@ -1,13 +1,13 @@ - + Wareneingangsliste - + Abholfachremissionsvorschau - + Abholfachbereinigung - + Reservierungen
diff --git a/apps/page/goods-in/src/lib/goods-in-search/search-main/goods-in-search-main.component.scss b/apps/page/goods-in/src/lib/goods-in-search/search-main/goods-in-search-main.component.scss index bf8e1052e..a1b92a410 100644 --- a/apps/page/goods-in/src/lib/goods-in-search/search-main/goods-in-search-main.component.scss +++ b/apps/page/goods-in/src/lib/goods-in-search/search-main/goods-in-search-main.component.scss @@ -17,14 +17,14 @@ .search-main { @apply bg-white text-center rounded-t-card py-5 shadow-card; - height: calc(100vh - 495px); + height: calc(100vh - 510px); .search-main-title { @apply text-2xl font-bold; } .search-main-paragraph { - @apply text-2xl mb-12; + @apply text-2xl mb-12 mt-6; } ui-filter-input-group-main { diff --git a/apps/page/goods-in/src/lib/goods-in-search/search-main/goods-in-search-main.component.ts b/apps/page/goods-in/src/lib/goods-in-search/search-main/goods-in-search-main.component.ts index fe0d89f53..1404e2289 100644 --- a/apps/page/goods-in/src/lib/goods-in-search/search-main/goods-in-search-main.component.ts +++ b/apps/page/goods-in/src/lib/goods-in-search/search-main/goods-in-search-main.component.ts @@ -1,6 +1,7 @@ import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, ChangeDetectorRef } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { BreadcrumbService } from '@core/breadcrumb'; +import { Config } from '@core/config'; import { Subscription } from 'rxjs'; import { first } from 'rxjs/operators'; import { GoodsInSearchStore } from '../goods-in-search.store'; @@ -25,7 +26,8 @@ export class GoodsInSearchMainComponent implements OnInit, OnDestroy { private _cdr: ChangeDetectorRef, private _router: Router, private _activatedRoute: ActivatedRoute, - private _breadcrumb: BreadcrumbService + private _breadcrumb: BreadcrumbService, + private readonly _config: Config ) {} ngOnInit() { @@ -45,17 +47,35 @@ export class GoodsInSearchMainComponent implements OnInit, OnDestroy { } async removeBreadcrumbs() { - const resultCrumbs = await this._breadcrumb.getBreadcrumbsByKeyAndTags$('goods-in', ['goods-in', 'results']).pipe(first()).toPromise(); - const detailsCrumbs = await this._breadcrumb.getBreadcrumbsByKeyAndTags$('goods-in', ['goods-in', 'details']).pipe(first()).toPromise(); - const editCrumbs = await this._breadcrumb.getBreadcrumbsByKeyAndTags$('goods-in', ['goods-in', 'edit']).pipe(first()).toPromise(); - const listCrumbs = await this._breadcrumb.getBreadcrumbsByKeyAndTags$('goods-in', ['goods-in', 'list']).pipe(first()).toPromise(); + const resultCrumbs = await this._breadcrumb + .getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'results']) + .pipe(first()) + .toPromise(); + const detailsCrumbs = await this._breadcrumb + .getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'details']) + .pipe(first()) + .toPromise(); + const editCrumbs = await this._breadcrumb + .getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'edit']) + .pipe(first()) + .toPromise(); + const listCrumbs = await this._breadcrumb + .getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'list']) + .pipe(first()) + .toPromise(); const reservationCrumbs = await this._breadcrumb - .getBreadcrumbsByKeyAndTags$('goods-in', ['goods-in', 'reservation']) + .getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'reservation']) .pipe(first()) .toPromise(); - const cleanupCrumbs = await this._breadcrumb.getBreadcrumbsByKeyAndTags$('goods-in', ['goods-in', 'cleanup']).pipe(first()).toPromise(); - const previewCrumbs = await this._breadcrumb.getBreadcrumbsByKeyAndTags$('goods-in', ['goods-in', 'preview']).pipe(first()).toPromise(); + const cleanupCrumbs = await this._breadcrumb + .getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'cleanup']) + .pipe(first()) + .toPromise(); + const previewCrumbs = await this._breadcrumb + .getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'preview']) + .pipe(first()) + .toPromise(); resultCrumbs.forEach((crumb) => { this._breadcrumb.removeBreadcrumb(crumb.id, true); @@ -98,12 +118,14 @@ export class GoodsInSearchMainComponent implements OnInit, OnDestroy { if (result.hits === 1) { const orderItem = result.result[0]; this._router.navigate([ - `/goods/in/details/order/${encodeURIComponent(orderItem.orderNumber)}/item/${orderItem.orderItemId}/${ + `/filiale/goods/in/details/order/${encodeURIComponent(orderItem.orderNumber)}/item/${orderItem.orderItemId}/${ orderItem.processingStatus }`, ]); } else { - this._router.navigate(['goods', 'in', 'results'], { queryParams: this._goodsInSearchStore.filter.getQueryParams() }); + this._router.navigate(['/filiale', 'goods', 'in', 'results'], { + queryParams: this._goodsInSearchStore.filter.getQueryParams(), + }); } } else { this.message = 'keine Suchergebnisse'; @@ -118,9 +140,9 @@ export class GoodsInSearchMainComponent implements OnInit, OnDestroy { async updateBreadcrumb() { await this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({ - key: 'goods-in', + key: this._config.get('process.ids.goodsIn'), name: 'Abholfach', - path: '/goods/in', + path: '/filiale/goods/in', tags: ['goods-in', 'main', 'filter'], section: 'branch', params: this._goodsInSearchStore.filter?.getQueryParams(), diff --git a/apps/page/goods-in/src/lib/goods-in-search/search-results/goods-in-search-results.component.html b/apps/page/goods-in/src/lib/goods-in-search/search-results/goods-in-search-results.component.html index 83eb92dd2..f086ae181 100644 --- a/apps/page/goods-in/src/lib/goods-in-search/search-results/goods-in-search-results.component.html +++ b/apps/page/goods-in/src/lib/goods-in-search/search-results/goods-in-search-results.component.html @@ -14,6 +14,7 @@ (reachEnd)="loadMore()" [deltaEnd]="150" [itemLength]="(items$ | async).length" + [containerHeight]="'24.5'" > diff --git a/apps/page/goods-in/src/lib/goods-in-search/search-results/goods-in-search-results.component.scss b/apps/page/goods-in/src/lib/goods-in-search/search-results/goods-in-search-results.component.scss index 8473231b2..568f89d6a 100644 --- a/apps/page/goods-in/src/lib/goods-in-search/search-results/goods-in-search-results.component.scss +++ b/apps/page/goods-in/src/lib/goods-in-search/search-results/goods-in-search-results.component.scss @@ -30,3 +30,7 @@ shared-goods-in-out-order-group-item { @apply cursor-pointer; } + +::ng-deep page-goods-in-search-results ui-scroll-container .cta-scroll { + bottom: 35px !important; +} diff --git a/apps/page/goods-in/src/lib/goods-in-search/search-results/goods-in-search-results.component.ts b/apps/page/goods-in/src/lib/goods-in-search/search-results/goods-in-search-results.component.ts index 787fa6eba..24318b099 100644 --- a/apps/page/goods-in/src/lib/goods-in-search/search-results/goods-in-search-results.component.ts +++ b/apps/page/goods-in/src/lib/goods-in-search/search-results/goods-in-search-results.component.ts @@ -6,6 +6,7 @@ import { GoodsInSearchStore } from '../goods-in-search.store'; import { combineLatest, Subject } from 'rxjs'; import { BreadcrumbService } from '@core/breadcrumb'; import { UiScrollContainerComponent } from '@ui/scroll-container'; +import { Config } from '@core/config'; @Component({ selector: 'page-goods-in-search-results', @@ -41,7 +42,8 @@ export class GoodsInSearchResultsComponent implements OnInit, OnDestroy { private _goodsInSearchStore: GoodsInSearchStore, private _router: Router, private _activatedRoute: ActivatedRoute, - private _breadcrumb: BreadcrumbService + private _breadcrumb: BreadcrumbService, + private readonly _config: Config ) {} ngOnInit() { @@ -60,8 +62,14 @@ export class GoodsInSearchResultsComponent implements OnInit, OnDestroy { } async removeBreadcrumbs() { - const detailsCrumbs = await this._breadcrumb.getBreadcrumbsByKeyAndTags$('goods-in', ['goods-in', 'details']).pipe(first()).toPromise(); - const editCrumbs = await this._breadcrumb.getBreadcrumbsByKeyAndTags$('goods-in', ['goods-in', 'edit']).pipe(first()).toPromise(); + const detailsCrumbs = await this._breadcrumb + .getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'details']) + .pipe(first()) + .toPromise(); + const editCrumbs = await this._breadcrumb + .getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'edit']) + .pipe(first()) + .toPromise(); detailsCrumbs.forEach((crumb) => { this._breadcrumb.removeBreadcrumb(crumb.id, true); @@ -74,9 +82,9 @@ export class GoodsInSearchResultsComponent implements OnInit, OnDestroy { async createBreadcrumb() { await this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({ - key: 'goods-in', + key: this._config.get('process.ids.goodsIn'), name: this.getBreadcrumbName(), - path: '/goods/in/results', + path: '/filiale/goods/in/results', section: 'branch', params: this._goodsInSearchStore.filter?.getQueryParams(), tags: ['goods-in', 'results', 'filter'], @@ -89,7 +97,7 @@ export class GoodsInSearchResultsComponent implements OnInit, OnDestroy { if (queryParams) { const crumbs = await this._breadcrumb - .getBreadcrumbsByKeyAndTags$('goods-in', ['goods-in', 'results', 'filter']) + .getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'results', 'filter']) .pipe(first()) .toPromise(); @@ -115,7 +123,7 @@ export class GoodsInSearchResultsComponent implements OnInit, OnDestroy { if (this._goodsInSearchStore.hits === 0) { this._goodsInSearchStore.searchResult$.pipe(takeUntil(this._onDestroy$)).subscribe(async (result) => { if (result.hits === 0) { - await this._router.navigate(['/goods/in'], { queryParams: this._goodsInSearchStore.filter.getQueryParams() }); + await this._router.navigate(['/filiale/goods/in'], { queryParams: this._goodsInSearchStore.filter.getQueryParams() }); } else { await this.createBreadcrumb(); if (result.hits === 1) { @@ -148,6 +156,6 @@ export class GoodsInSearchResultsComponent implements OnInit, OnDestroy { const processingStatus = orderItem.processingStatus; const orderItemId = orderItem.orderItemId; - this._router.navigate([`/goods/in/details/order/${encodeURIComponent(orderNumber)}/item/${orderItemId}/${processingStatus}`]); + this._router.navigate([`/filiale/goods/in/details/order/${encodeURIComponent(orderNumber)}/item/${orderItemId}/${processingStatus}`]); } } diff --git a/apps/page/goods-in/src/lib/goods-in.component.html b/apps/page/goods-in/src/lib/goods-in.component.html index 22ada0f32..6aad7de52 100644 --- a/apps/page/goods-in/src/lib/goods-in.component.html +++ b/apps/page/goods-in/src/lib/goods-in.component.html @@ -1,3 +1,3 @@ - + diff --git a/apps/page/goods-in/src/lib/goods-in.component.scss b/apps/page/goods-in/src/lib/goods-in.component.scss index bcbc5d819..551485e13 100644 --- a/apps/page/goods-in/src/lib/goods-in.component.scss +++ b/apps/page/goods-in/src/lib/goods-in.component.scss @@ -1,9 +1,7 @@ :host { - @apply overflow-scroll; - max-height: calc(100vh - 135px - 80px); + @apply block; } shell-breadcrumb { - @apply pb-6; - margin-top: -0.5rem; + @apply sticky z-sticky top-0 py-4; } diff --git a/apps/page/goods-in/src/lib/goods-in.component.ts b/apps/page/goods-in/src/lib/goods-in.component.ts index 109d596ab..12f5b6998 100644 --- a/apps/page/goods-in/src/lib/goods-in.component.ts +++ b/apps/page/goods-in/src/lib/goods-in.component.ts @@ -1,4 +1,5 @@ import { Component, ChangeDetectionStrategy, OnInit } from '@angular/core'; +import { Config } from '@core/config'; @Component({ selector: 'page-goods-in', @@ -7,7 +8,9 @@ import { Component, ChangeDetectionStrategy, OnInit } from '@angular/core'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class GoodsInComponent implements OnInit { - constructor() {} + goodsInKey = this._config.get('process.ids.goodsIn'); + + constructor(private readonly _config: Config) {} ngOnInit() {} } diff --git a/apps/page/goods-in/src/test.ts b/apps/page/goods-in/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/page/goods-in/src/test.ts +++ b/apps/page/goods-in/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/page/goods-out/src/lib/goods-out-details/goods-out-details.component.ts b/apps/page/goods-out/src/lib/goods-out-details/goods-out-details.component.ts index 3ce6bfe82..d213df0f0 100644 --- a/apps/page/goods-out/src/lib/goods-out-details/goods-out-details.component.ts +++ b/apps/page/goods-out/src/lib/goods-out-details/goods-out-details.component.ts @@ -83,7 +83,7 @@ export class GoodsOutDetailsComponent extends ComponentStore { this._breadcrumb.removeBreadcrumb(crumb.id, true); @@ -101,10 +101,7 @@ export class GoodsOutDetailsComponent extends ComponentStore { this._breadcrumb.removeBreadcrumb(crumb.id, true); @@ -160,13 +157,13 @@ export class GoodsOutDetailsComponent extends ComponentStore +
diff --git a/apps/page/goods-out/src/lib/goods-out-edit/goods-out-edit.component.scss b/apps/page/goods-out/src/lib/goods-out-edit/goods-out-edit.component.scss index a113f1ddb..3a998ef57 100644 --- a/apps/page/goods-out/src/lib/goods-out-edit/goods-out-edit.component.scss +++ b/apps/page/goods-out/src/lib/goods-out-edit/goods-out-edit.component.scss @@ -1,4 +1,4 @@ -.container { +div { @apply overflow-y-scroll; height: calc(100vh - 280px); } diff --git a/apps/page/goods-out/src/lib/goods-out-edit/goods-out-edit.component.ts b/apps/page/goods-out/src/lib/goods-out-edit/goods-out-edit.component.ts index 04e733316..727ee346d 100644 --- a/apps/page/goods-out/src/lib/goods-out-edit/goods-out-edit.component.ts +++ b/apps/page/goods-out/src/lib/goods-out-edit/goods-out-edit.component.ts @@ -1,6 +1,7 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { BreadcrumbService } from '@core/breadcrumb'; +import { Config } from '@core/config'; import { DomainGoodsService } from '@domain/oms'; import { combineLatest, Observable } from 'rxjs'; import { map, shareReplay, switchMap, withLatestFrom } from 'rxjs/operators'; @@ -37,7 +38,8 @@ export class GoodsOutEditComponent implements OnInit { private _activatedRoute: ActivatedRoute, private _breadcrumb: BreadcrumbService, private _domainGoodsInService: DomainGoodsService, - private _router: Router + private _router: Router, + private readonly _config: Config ) {} ngOnInit() { @@ -49,11 +51,11 @@ export class GoodsOutEditComponent implements OnInit { const compartmentCode = this._activatedRoute.snapshot.params.compartmentCode; const processingStatus = this._activatedRoute.snapshot.params.processingStatus; await this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({ - key: 'goods-out', + key: this._config.get('process.ids.goodsOut'), name: 'Bearbeiten', path: compartmentCode - ? `/goods/out/details/compartment/${encodeURIComponent(compartmentCode)}/${processingStatus}/edit` - : `/goods/out/details/order/${encodeURIComponent(orderNumber)}/${processingStatus}/edit`, + ? `/kunde/goods/out/details/compartment/${encodeURIComponent(compartmentCode)}/${processingStatus}/edit` + : `/kunde/goods/out/details/order/${encodeURIComponent(orderNumber)}/${processingStatus}/edit`, section: 'customer', tags: ['goods-out', 'edit', compartmentCode || orderNumber], }); @@ -64,7 +66,7 @@ export class GoodsOutEditComponent implements OnInit { const compartmentCode = this._activatedRoute.snapshot.params.compartmentCode; const processingStatus = options?.processingStatus ? options.processingStatus : this._activatedRoute.snapshot.params.processingStatus; compartmentCode - ? this._router.navigate([`/goods/out/details/compartment/${encodeURIComponent(compartmentCode)}/${processingStatus}`]) - : this._router.navigate([`/goods/out/details/order/${encodeURIComponent(orderNumber)}/${processingStatus}`]); + ? this._router.navigate([`/kunde/goods/out/details/compartment/${encodeURIComponent(compartmentCode)}/${processingStatus}`]) + : this._router.navigate([`/kunde/goods/out/details/order/${encodeURIComponent(orderNumber)}/${processingStatus}`]); } } diff --git a/apps/page/goods-out/src/lib/goods-out-search/goods-out-search-filter/goods-out-search-filter.component.scss b/apps/page/goods-out/src/lib/goods-out-search/goods-out-search-filter/goods-out-search-filter.component.scss index 660bbf22f..39adb8d26 100644 --- a/apps/page/goods-out/src/lib/goods-out-search/goods-out-search-filter/goods-out-search-filter.component.scss +++ b/apps/page/goods-out/src/lib/goods-out-search/goods-out-search-filter/goods-out-search-filter.component.scss @@ -1,5 +1,5 @@ :host { - @apply block bg-customer; + @apply block; } .goods-out-search-filter-content { @@ -8,13 +8,13 @@ } .btn-close { - @apply absolute text-inactive-customer top-0 right-4 outline-none border-none bg-transparent; + @apply absolute text-inactive-customer top-3 p-4 right-4 outline-none border-none bg-transparent; } .goods-out-search-filter-content-main { @apply px-4; h1.title { - @apply text-center; + @apply text-center text-3xl font-bold py-4; } } diff --git a/apps/page/goods-out/src/lib/goods-out-search/goods-out-search-filter/goods-out-search-filter.component.ts b/apps/page/goods-out/src/lib/goods-out-search/goods-out-search-filter/goods-out-search-filter.component.ts index f3283fe4e..d7ad61971 100644 --- a/apps/page/goods-out/src/lib/goods-out-search/goods-out-search-filter/goods-out-search-filter.component.ts +++ b/apps/page/goods-out/src/lib/goods-out-search/goods-out-search-filter/goods-out-search-filter.component.ts @@ -1,6 +1,7 @@ import { Component, ChangeDetectionStrategy, Output, EventEmitter, ChangeDetectorRef, OnInit, OnDestroy } from '@angular/core'; import { Router } from '@angular/router'; import { BreadcrumbService } from '@core/breadcrumb'; +import { Config } from '@core/config'; import { OrderItemListItemDTO } from '@swagger/oms'; import { UiFilter } from '@ui/filter'; import { Observable, Subject } from 'rxjs'; @@ -29,7 +30,8 @@ export class GoodsOutSearchFilterComponent implements OnInit, OnDestroy { private _goodsOutSearchStore: GoodsOutSearchStore, private _breadcrumb: BreadcrumbService, private _cdr: ChangeDetectorRef, - private _router: Router + private _router: Router, + private readonly _config: Config ) {} ngOnInit() { @@ -72,7 +74,9 @@ export class GoodsOutSearchFilterComponent implements OnInit, OnDestroy { const orderItem = result.result[0]; this._router.navigate([this.getDetailsPath(orderItem)]); } else { - this._router.navigate(['goods', 'out', 'results'], { queryParams: this._goodsOutSearchStore.filter.getQueryParams() }); + this._router.navigate(['/kunde', 'goods', 'out', 'results'], { + queryParams: this._goodsOutSearchStore.filter.getQueryParams(), + }); } this.close.emit(); @@ -89,9 +93,9 @@ export class GoodsOutSearchFilterComponent implements OnInit, OnDestroy { async updateBreadcrumb() { await this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({ - key: 'goods-out', + key: this._config.get('process.ids.goodsOut'), name: 'Warenausgabe', - path: '/goods/out', + path: '/kunde/goods/out', tags: ['goods-out', 'main', 'filter'], section: 'customer', params: this._goodsOutSearchStore.filter?.getQueryParams(), @@ -105,7 +109,7 @@ export class GoodsOutSearchFilterComponent implements OnInit, OnDestroy { getDetailsPath(item: OrderItemListItemDTO) { return item?.compartmentCode - ? `/goods/out/details/compartment/${encodeURIComponent(item?.compartmentCode)}/${item?.processingStatus}` - : `/goods/out/details/order/${encodeURIComponent(item?.orderNumber)}/${item?.processingStatus}`; + ? `/kunde/goods/out/details/compartment/${encodeURIComponent(item?.compartmentCode)}/${item?.processingStatus}` + : `/kunde/goods/out/details/order/${encodeURIComponent(item?.orderNumber)}/${item?.processingStatus}`; } } diff --git a/apps/page/goods-out/src/lib/goods-out-search/goods-out-search.component.html b/apps/page/goods-out/src/lib/goods-out-search/goods-out-search.component.html index 679698c24..b8f5aebc4 100644 --- a/apps/page/goods-out/src/lib/goods-out-search/goods-out-search.component.html +++ b/apps/page/goods-out/src/lib/goods-out-search/goods-out-search.component.html @@ -1,8 +1,13 @@ - - + + + diff --git a/apps/page/goods-out/src/lib/goods-out-search/goods-out-search.component.scss b/apps/page/goods-out/src/lib/goods-out-search/goods-out-search.component.scss index 92f159da2..6a1428634 100644 --- a/apps/page/goods-out/src/lib/goods-out-search/goods-out-search.component.scss +++ b/apps/page/goods-out/src/lib/goods-out-search/goods-out-search.component.scss @@ -1,18 +1,16 @@ +:host { + @apply flex flex-col w-full box-content relative; +} + .filter { - @apply absolute font-sans flex items-center font-bold bg-gray-400 border-0 text-regular py-px-8 px-px-15 rounded-filter justify-center right-0 top-0; + @apply absolute font-sans flex items-center font-bold bg-wild-blue-yonder border-0 text-regular -top-12 right-0 py-px-8 px-px-15 rounded-filter justify-center z-sticky; min-width: 106px; - margin-top: 0.5rem; .label { @apply ml-px-5; } &.active { - @apply bg-active-customer text-white ml-px-5; + @apply bg-dark-cerulean text-white ml-px-5; } } - -page-goods-out-search-filter { - @apply fixed right-0 bottom-0 left-0 z-fixed; - top: 136px; -} diff --git a/apps/page/goods-out/src/lib/goods-out-search/goods-out-search.component.ts b/apps/page/goods-out/src/lib/goods-out-search/goods-out-search.component.ts index 05003d190..80a3e020a 100644 --- a/apps/page/goods-out/src/lib/goods-out-search/goods-out-search.component.ts +++ b/apps/page/goods-out/src/lib/goods-out-search/goods-out-search.component.ts @@ -2,9 +2,11 @@ import { animate, style, transition, trigger } from '@angular/animations'; import { Component, ChangeDetectionStrategy, OnInit, OnDestroy } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { BreadcrumbService } from '@core/breadcrumb'; +import { Config } from '@core/config'; import { UiFilterAutocompleteProvider, UiFilterScanProvider } from '@ui/filter'; +import { isEqual } from 'lodash'; import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; +import { filter, first, map, takeUntil, withLatestFrom } from 'rxjs/operators'; import { GoodsOutSearchStore } from './goods-out-search.store'; import { GoodsOutSearchMainAutocompleteProvider } from './providers/goods-out-search-main-autocomplete.provider'; import { GoodsOutSearchMainScanProvider } from './providers/goods-out-search-main-scan.provider'; @@ -38,10 +40,21 @@ export class GoodsOutSearchComponent implements OnInit, OnDestroy { private _onDestroy$ = new Subject(); showFilterOverlay = false; + initialFilter$ = this._goodsOutSearchStore.filter$.pipe( + filter((filter) => !!filter), + first() + ); + + hasFilter$ = this._goodsOutSearchStore.filter$.pipe( + withLatestFrom(this.initialFilter$), + map(([filter, initialFilter]) => !isEqual(filter?.getQueryParams(), initialFilter?.getQueryParams())) + ); + constructor( private _goodsOutSearchStore: GoodsOutSearchStore, private _breadcrumb: BreadcrumbService, - private _activatedRoute: ActivatedRoute + private _activatedRoute: ActivatedRoute, + private readonly _config: Config ) {} ngOnInit() { @@ -55,9 +68,9 @@ export class GoodsOutSearchComponent implements OnInit, OnDestroy { }); this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({ - key: 'goods-out', + key: this._config.get('process.ids.goodsOut'), name: 'Warenausgabe', - path: '/goods/out', + path: '/kunde/goods/out', tags: ['goods-out', 'main', 'filter'], section: 'customer', }); diff --git a/apps/page/goods-out/src/lib/goods-out-search/goods-out-search.module.ts b/apps/page/goods-out/src/lib/goods-out-search/goods-out-search.module.ts index ef880df17..a0e5d5a73 100644 --- a/apps/page/goods-out/src/lib/goods-out-search/goods-out-search.module.ts +++ b/apps/page/goods-out/src/lib/goods-out-search/goods-out-search.module.ts @@ -6,9 +6,10 @@ import { GoodsOutSearchFilterComponent } from './goods-out-search-filter'; import { UiIconModule } from '@ui/icon'; import { RouterModule } from '@angular/router'; import { UiFilterNextModule } from '@ui/filter'; +import { ShellFilterOverlayModule } from '@shell/filter-overlay'; @NgModule({ - imports: [CommonModule, UiIconModule, RouterModule, UiFilterNextModule], + imports: [CommonModule, UiIconModule, RouterModule, UiFilterNextModule, ShellFilterOverlayModule], exports: [GoodsOutSearchComponent], declarations: [GoodsOutSearchComponent, GoodsOutSearchFilterComponent], }) diff --git a/apps/page/goods-out/src/lib/goods-out-search/search-main/goods-out-search-main.component.scss b/apps/page/goods-out/src/lib/goods-out-search/search-main/goods-out-search-main.component.scss index 1ab4ba6b3..df66b06ab 100644 --- a/apps/page/goods-out/src/lib/goods-out-search/search-main/goods-out-search-main.component.scss +++ b/apps/page/goods-out/src/lib/goods-out-search/search-main/goods-out-search-main.component.scss @@ -15,7 +15,7 @@ } .search-main-paragraph { - @apply text-2xl mb-12; + @apply text-2xl mb-12 mt-6; } ui-filter-input-group-main { diff --git a/apps/page/goods-out/src/lib/goods-out-search/search-main/goods-out-search-main.component.ts b/apps/page/goods-out/src/lib/goods-out-search/search-main/goods-out-search-main.component.ts index ab997a4b0..56b235c19 100644 --- a/apps/page/goods-out/src/lib/goods-out-search/search-main/goods-out-search-main.component.ts +++ b/apps/page/goods-out/src/lib/goods-out-search/search-main/goods-out-search-main.component.ts @@ -1,6 +1,7 @@ import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, ChangeDetectorRef } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { BreadcrumbService } from '@core/breadcrumb'; +import { Config } from '@core/config'; import { OrderItemListItemDTO } from '@swagger/oms'; import { Subscription } from 'rxjs'; import { first } from 'rxjs/operators'; @@ -26,7 +27,8 @@ export class GoodsOutSearchMainComponent implements OnInit, OnDestroy { private _cdr: ChangeDetectorRef, private _router: Router, private _activatedRoute: ActivatedRoute, - private _breadcrumb: BreadcrumbService + private _breadcrumb: BreadcrumbService, + private readonly _config: Config ) {} ngOnInit() { @@ -47,15 +49,18 @@ export class GoodsOutSearchMainComponent implements OnInit, OnDestroy { async removeBreadcrumbs() { const resultCrumbs = await this._breadcrumb - .getBreadcrumbsByKeyAndTags$('goods-out', ['goods-out', 'results']) + .getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsOut'), ['goods-out', 'results']) .pipe(first()) .toPromise(); const detailsCrumbs = await this._breadcrumb - .getBreadcrumbsByKeyAndTags$('goods-out', ['goods-out', 'details']) + .getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsOut'), ['goods-out', 'details']) .pipe(first()) .toPromise(); - const editCrumbs = await this._breadcrumb.getBreadcrumbsByKeyAndTags$('goods-out', ['goods-out', 'edit']).pipe(first()).toPromise(); + const editCrumbs = await this._breadcrumb + .getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsOut'), ['goods-out', 'edit']) + .pipe(first()) + .toPromise(); editCrumbs.forEach((crumb) => { this._breadcrumb.removeBreadcrumb(crumb.id, true); @@ -83,7 +88,9 @@ export class GoodsOutSearchMainComponent implements OnInit, OnDestroy { const orderItem = result.result[0]; this._router.navigate([this.getDetailsPath(orderItem)]); } else { - this._router.navigate(['goods', 'out', 'results'], { queryParams: this._goodsOutSearchStore.filter.getQueryParams() }); + this._router.navigate(['/kunde', 'goods', 'out', 'results'], { + queryParams: this._goodsOutSearchStore.filter.getQueryParams(), + }); } } else { this.message = 'keine Suchergebnisse'; @@ -98,9 +105,9 @@ export class GoodsOutSearchMainComponent implements OnInit, OnDestroy { async updateBreadcrumb() { await this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({ - key: 'goods-out', + key: this._config.get('process.ids.goodsOut'), name: 'Warenausgabe', - path: '/goods/out', + path: '/kunde/goods/out', tags: ['goods-out', 'main', 'filter'], section: 'customer', params: this._goodsOutSearchStore.filter?.getQueryParams(), @@ -114,7 +121,7 @@ export class GoodsOutSearchMainComponent implements OnInit, OnDestroy { getDetailsPath(item: OrderItemListItemDTO) { return item?.compartmentCode - ? `/goods/out/details/compartment/${encodeURIComponent(item?.compartmentCode)}/${item?.processingStatus}` - : `/goods/out/details/order/${encodeURIComponent(item?.orderNumber)}/${item?.processingStatus}`; + ? `/kunde/goods/out/details/compartment/${encodeURIComponent(item?.compartmentCode)}/${item?.processingStatus}` + : `/kunde/goods/out/details/order/${encodeURIComponent(item?.orderNumber)}/${item?.processingStatus}`; } } diff --git a/apps/page/goods-out/src/lib/goods-out-search/search-results/goods-out-search-results.component.ts b/apps/page/goods-out/src/lib/goods-out-search/search-results/goods-out-search-results.component.ts index acbd111c8..98645394a 100644 --- a/apps/page/goods-out/src/lib/goods-out-search/search-results/goods-out-search-results.component.ts +++ b/apps/page/goods-out/src/lib/goods-out-search/search-results/goods-out-search-results.component.ts @@ -10,6 +10,7 @@ import { CommandService } from '@core/command'; import { OrderItemsContext } from '@domain/oms'; import { UiErrorModalComponent, UiModalService } from '@ui/modal'; import { UiScrollContainerComponent } from '@ui/scroll-container'; +import { Config } from '@core/config'; export interface GoodsOutSearchResultsState { selectedOrderItemSubsetIds: number[]; @@ -74,7 +75,8 @@ export class GoodsOutSearchResultsComponent extends ComponentStore { this._breadcrumb.removeBreadcrumb(crumb.id, true); @@ -117,9 +122,9 @@ export class GoodsOutSearchResultsComponent extends ComponentStore { if (result.hits === 0) { - await this._router.navigate(['/goods/out'], { queryParams: this._goodsOutSearchStore.filter.getQueryParams() }); + await this._router.navigate(['/kunde/goods/out'], { queryParams: this._goodsOutSearchStore.filter.getQueryParams() }); } else { await this.createBreadcrumb(); if (result.hits === 1) { @@ -192,9 +197,9 @@ export class GoodsOutSearchResultsComponent extends ComponentStore + diff --git a/apps/page/goods-out/src/lib/goods-out.component.scss b/apps/page/goods-out/src/lib/goods-out.component.scss index 101ec5d04..551485e13 100644 --- a/apps/page/goods-out/src/lib/goods-out.component.scss +++ b/apps/page/goods-out/src/lib/goods-out.component.scss @@ -1,8 +1,7 @@ :host { - @apply block -mt-px-15; - max-height: calc(100vh - 135px - 80px); + @apply block; } shell-breadcrumb { - @apply my-6; + @apply sticky z-sticky top-0 py-4; } diff --git a/apps/page/goods-out/src/lib/goods-out.component.ts b/apps/page/goods-out/src/lib/goods-out.component.ts index 01485e477..1cec1b96e 100644 --- a/apps/page/goods-out/src/lib/goods-out.component.ts +++ b/apps/page/goods-out/src/lib/goods-out.component.ts @@ -1,4 +1,5 @@ import { Component, ChangeDetectionStrategy } from '@angular/core'; +import { Config } from '@core/config'; @Component({ selector: 'page-goods-out', @@ -7,5 +8,7 @@ import { Component, ChangeDetectionStrategy } from '@angular/core'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class GoodsOutComponent { - constructor() {} + goodsOutKey = this._config.get('process.ids.goodsOut'); + + constructor(private readonly _config: Config) {} } diff --git a/apps/page/goods-out/src/test.ts b/apps/page/goods-out/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/page/goods-out/src/test.ts +++ b/apps/page/goods-out/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/page/remission/README.md b/apps/page/remission/README.md new file mode 100644 index 000000000..84cec88e6 --- /dev/null +++ b/apps/page/remission/README.md @@ -0,0 +1,25 @@ +# Remission + +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 remission` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project remission`. + +> Note: Don't forget to add `--project remission` or else it will be added to the default project in your `angular.json` file. + +## Build + +Run `ng build remission` to build the project. The build artifacts will be stored in the `dist/` directory. + +## Publishing + +After building your library with `ng build remission`, go to the dist folder `cd dist/remission` and run `npm publish`. + +## Running unit tests + +Run `ng test remission` 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. diff --git a/apps/page/remission/karma.conf.js b/apps/page/remission/karma.conf.js new file mode 100644 index 000000000..a70b69a7f --- /dev/null +++ b/apps/page/remission/karma.conf.js @@ -0,0 +1,43 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html +const customLaunchers = require('../../../karma/custom-launchers'); +const junitReporter = require('../../../karma/junit-reporter')('page-remission'); +const coverageReporter = require('../../../karma/coverage-reporter')('page-remission'); + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage'), + require('karma-junit-reporter'), + require('@angular-devkit/build-angular/plugins/karma'), + ], + client: { + jasmine: { + // you can add configuration options for Jasmine here + // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html + // for example, you can disable the random execution with `random: false` + // or set a specific seed with `seed: 4321` + }, + clearContext: false, // leave Jasmine Spec Runner output visible in browser + }, + jasmineHtmlReporter: { + suppressAll: true, // removes the duplicated traces + }, + coverageReporter, + junitReporter, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + customLaunchers, + singleRun: false, + restartOnFileChange: true, + }); +}; diff --git a/apps/page/remission/ng-package.json b/apps/page/remission/ng-package.json new file mode 100644 index 000000000..d7130d32b --- /dev/null +++ b/apps/page/remission/ng-package.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../../dist/page/remission", + "lib": { + "entryFile": "src/public-api.ts" + } +} \ No newline at end of file diff --git a/apps/page/remission/package.json b/apps/page/remission/package.json new file mode 100644 index 000000000..c73698b66 --- /dev/null +++ b/apps/page/remission/package.json @@ -0,0 +1,11 @@ +{ + "name": "@page/remission", + "version": "0.0.1", + "peerDependencies": { + "@angular/common": "^12.2.0", + "@angular/core": "^12.2.0" + }, + "dependencies": { + "tslib": "^2.3.0" + } +} \ No newline at end of file diff --git a/apps/page/remission/src/lib/add-product/add-product.component.html b/apps/page/remission/src/lib/add-product/add-product.component.html new file mode 100644 index 000000000..02626d9a5 --- /dev/null +++ b/apps/page/remission/src/lib/add-product/add-product.component.html @@ -0,0 +1,18 @@ +
+

Artikel hinzufügen

+

+ Geben Sie die ISBN/EAN ein oder
+ öffnen Sie einen externen Scanner
+ um den Artikel hinzuzufügen. +

+ + +
diff --git a/apps/page/remission/src/lib/add-product/add-product.component.scss b/apps/page/remission/src/lib/add-product/add-product.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/apps/page/remission/src/lib/add-product/add-product.component.ts b/apps/page/remission/src/lib/add-product/add-product.component.ts new file mode 100644 index 000000000..b09bb2303 --- /dev/null +++ b/apps/page/remission/src/lib/add-product/add-product.component.ts @@ -0,0 +1,116 @@ +import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, Optional, Inject } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { BreadcrumbService } from '@core/breadcrumb'; +import { Config } from '@core/config'; +import { DomainRemissionService } from '@domain/remission'; +import { UiFilterScanProvider } from '@ui/filter'; +import { UiModalService } from '@ui/modal'; +import { NEVER, Subject } from 'rxjs'; +import { catchError, switchMap, takeUntil, tap } from 'rxjs/operators'; +import { AddProductModalComponent, AddProductModalData } from '../modals/add-product-modal'; + +@Component({ + selector: 'page-remission-add-product', + templateUrl: 'add-product.component.html', + styleUrls: ['add-product.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class AddProductComponent implements OnInit, OnDestroy { + private _onDestroy = new Subject(); + + loading$ = new Subject(); + + search$ = new Subject(); + + hint$ = new Subject(); + + private _scanProvider: UiFilterScanProvider; + get scanProvider() { + return this._scanProvider; + } + + constructor( + @Inject(UiFilterScanProvider) @Optional() private scanProviders: UiFilterScanProvider[], + private _remiService: DomainRemissionService, + private _modal: UiModalService, + private _breadcrumb: BreadcrumbService, + private _config: Config, + private _router: Router, + private _activatedRoute: ActivatedRoute + ) {} + + ngOnInit() { + this._scanProvider = this.scanProviders?.find((provider) => provider.for === 'add-product'); + this.addBreadcrumbIfNotExists(); + this.initSearch(); + } + + initSearch() { + this.search$ + .pipe( + tap((_) => { + this.loading$.next(true); + this.hint$.next(''); + }), + switchMap((query) => + this._remiService.searchItemToRemit(query).pipe( + catchError((e) => { + let message = 'Fehler beim Laden der Produkte'; + if (e.error?.invalidProperties) { + message = Object.values(e.error.invalidProperties).join(', '); + } + this.hint$.next(message || e.error?.message); + this.loading$.next(false); + return NEVER; + }), + tap(() => this.loading$.next(false)) + ) + ) + ) + .subscribe((result) => { + if (!result) { + this.hint$.next('Keine Produkte gefunden'); + } else { + const modal = this._modal.open({ + content: AddProductModalComponent, + title: 'Artikel zur Remi-Liste hinzufügen', + data: { + item: result, + } as AddProductModalData, + }); + modal.afterClosed$.pipe(takeUntil(this._onDestroy)).subscribe((result) => { + if (result.data) { + this._router.navigate(['..', 'list'], { relativeTo: this._activatedRoute }); + } + }); + } + }); + } + + ngOnDestroy(): void { + this._onDestroy.next(); + this._onDestroy.complete(); + + this.loading$.complete(); + this.search$.complete(); + this.hint$.complete(); + } + + search(query: string) { + if (query.length > 0) { + this.search$.next(query); + } else { + this.hint$.next('Bitte geben Sie einen ISBN/EAN ein'); + } + } + + addBreadcrumbIfNotExists() { + this._breadcrumb.addBreadcrumbIfNotExists({ + key: this._config.get('process.ids.remission'), + name: 'Wannennummer', + path: '/filiale/remission/add-product', + section: 'branch', + tags: ['remission', 'add-product'], + }); + } +} diff --git a/apps/page/remission/src/lib/add-product/add-product.module.ts b/apps/page/remission/src/lib/add-product/add-product.module.ts new file mode 100644 index 000000000..379d4b589 --- /dev/null +++ b/apps/page/remission/src/lib/add-product/add-product.module.ts @@ -0,0 +1,13 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { AddProductComponent } from './add-product.component'; +import { UiSearchboxNextModule } from '@ui/searchbox'; +import { AddProductModalModule } from '../modals/add-product-modal'; + +@NgModule({ + imports: [CommonModule, UiSearchboxNextModule, AddProductModalModule], + exports: [AddProductComponent], + declarations: [AddProductComponent], +}) +export class AddProductModule {} diff --git a/apps/page/remission/src/lib/add-product/index.ts b/apps/page/remission/src/lib/add-product/index.ts new file mode 100644 index 000000000..d692c423f --- /dev/null +++ b/apps/page/remission/src/lib/add-product/index.ts @@ -0,0 +1,2 @@ +export * from './add-product.component'; +export * from './add-product.module'; diff --git a/apps/page/remission/src/lib/create-remission/create-remission.component.html b/apps/page/remission/src/lib/create-remission/create-remission.component.html new file mode 100644 index 000000000..3a9a75226 --- /dev/null +++ b/apps/page/remission/src/lib/create-remission/create-remission.component.html @@ -0,0 +1,28 @@ +
+

Warenbegleitschein eröffnen

+
+

+ Um einen Warenbegleitschein zu
+ eröffnen, scannen Sie die Packstück-ID
+ oder lassen Sie diese automatisch generieren. +

+
+
+ + +
+
diff --git a/apps/page/remission/src/lib/create-remission/create-remission.component.ts b/apps/page/remission/src/lib/create-remission/create-remission.component.ts new file mode 100644 index 000000000..13e2fb1ca --- /dev/null +++ b/apps/page/remission/src/lib/create-remission/create-remission.component.ts @@ -0,0 +1,88 @@ +import { Component, Inject, OnInit, Optional, ViewChild } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { BreadcrumbService } from '@core/breadcrumb'; +import { Config } from '@core/config'; +import { DomainRemissionService } from '@domain/remission'; +import { ReturnDTO } from '@swagger/remi'; +import { UiFilterScanProvider } from '@ui/filter'; +import { UiErrorModalComponent, UiModalService } from '@ui/modal'; +import { UiSearchboxNextComponent } from '@ui/searchbox'; +import { Subject } from 'rxjs'; +import { first } from 'rxjs/operators'; + +@Component({ + selector: 'page-create-remission', + templateUrl: 'create-remission.component.html', +}) +export class CreateRemissionComponent implements OnInit { + @ViewChild(UiSearchboxNextComponent) + searchboxComponent: UiSearchboxNextComponent; + + loading$ = new Subject(); + + hint$ = new Subject(); + + private _scanProvider: UiFilterScanProvider; + get scanProvider() { + return this._scanProvider; + } + + constructor( + @Inject(UiFilterScanProvider) @Optional() private scanProviders: UiFilterScanProvider[], + private _activatedRoute: ActivatedRoute, + private readonly _domainRemissionService: DomainRemissionService, + private readonly _modal: UiModalService, + private readonly _router: Router, + private readonly _breadcrumb: BreadcrumbService, + private readonly _config: Config + ) {} + + ngOnInit(): void { + this._scanProvider = this.scanProviders?.find((provider) => provider.for === 'create'); + this.addBreadcrumbIfNotExists(); + } + + async startRemission(receiptNumber?: string) { + try { + const returnGroup = this._activatedRoute.snapshot.params?.returnGroup; + + const supplier = await this.getSupplier(); + const returnDTO = await this.createReturn(supplier.id, returnGroup); + await this.createReceipt({ returnDTO, receiptNumber }); + await this.navigateToList(returnDTO.id); + } catch (error) { + console.error(error); + this._modal.open({ content: UiErrorModalComponent, title: 'Fehler beim Starten der Remission', data: error }); + return undefined; + } + } + + async getSupplier() { + const supplierId = +this._activatedRoute?.snapshot?.queryParams?.supplierId; + const suppliers = await this._domainRemissionService.getSuppliers().pipe(first()).toPromise(); + return suppliers.find((s) => s.id === supplierId); + } + + async createReturn(supplierId: number, returnGroup: string) { + return await this._domainRemissionService.createReturn(supplierId, returnGroup); + } + + async createReceipt({ returnDTO, receiptNumber }: { returnDTO: ReturnDTO; receiptNumber?: string }) { + await this._domainRemissionService.createReceipt(returnDTO, receiptNumber); + } + + async navigateToList(returnId: number) { + await this._router.navigate(['/filiale', 'remission', returnId, 'list']); + } + + addBreadcrumbIfNotExists() { + this._breadcrumb.addBreadcrumbIfNotExists({ + key: this._config.get('process.ids.remission'), + name: 'Warenbegleitschein eröffnen', + path: '/filiale/remission/create', + params: this._activatedRoute.snapshot.queryParams, + section: 'branch', + tags: ['remission', 'create'], + }); + } +} diff --git a/apps/page/remission/src/lib/create-remission/create-remission.module.ts b/apps/page/remission/src/lib/create-remission/create-remission.module.ts new file mode 100644 index 000000000..8cdc99d1b --- /dev/null +++ b/apps/page/remission/src/lib/create-remission/create-remission.module.ts @@ -0,0 +1,13 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { UiSearchboxNextModule } from '@ui/searchbox'; + +import { CreateRemissionComponent } from './create-remission.component'; + +@NgModule({ + imports: [CommonModule, UiSearchboxNextModule], + exports: [CreateRemissionComponent], + declarations: [CreateRemissionComponent], + providers: [], +}) +export class CreateRemissionModule {} diff --git a/apps/page/remission/src/lib/finish-remission/finish-remission.component.html b/apps/page/remission/src/lib/finish-remission/finish-remission.component.html new file mode 100644 index 000000000..d92bb016f --- /dev/null +++ b/apps/page/remission/src/lib/finish-remission/finish-remission.component.html @@ -0,0 +1,18 @@ +
+
+
+ +
+
+ +

Wanne abgeschlossen

+

+ Legen Sie abschließend den "Blank
Beizettel" in die abgeschlossene
Wanne. Der Warenbegleitschein
wird nach Abschluss + der
Remission versendet. Zum Öffnen
eines neuen Warenbegleitscheins
setzen Sie die Remission fort. +

+ +
+ + +
+
diff --git a/apps/page/remission/src/lib/finish-remission/finish-remission.component.scss b/apps/page/remission/src/lib/finish-remission/finish-remission.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/apps/page/remission/src/lib/finish-remission/finish-remission.component.ts b/apps/page/remission/src/lib/finish-remission/finish-remission.component.ts new file mode 100644 index 000000000..ca8b5d80e --- /dev/null +++ b/apps/page/remission/src/lib/finish-remission/finish-remission.component.ts @@ -0,0 +1,91 @@ +import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { BreadcrumbService } from '@core/breadcrumb'; +import { Config } from '@core/config'; +import { DomainRemissionService } from '@domain/remission'; +import { UiErrorModalComponent, UiModalService } from '@ui/modal'; + +@Component({ + selector: 'page-finish-remission', + templateUrl: 'finish-remission.component.html', + styleUrls: ['finish-remission.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class FinishRemissionComponent implements OnInit { + constructor( + private _activatedRoute: ActivatedRoute, + private _modal: UiModalService, + private _remissionService: DomainRemissionService, + private _router: Router, + private _breadcrumb: BreadcrumbService, + private _config: Config + ) {} + + ngOnInit() { + this.addBreadcrumbIfNotExists(); + } + + async createRemission() { + const returnId = +this._activatedRoute.snapshot.params?.returnId; + + if (returnId) { + try { + const returnDto = await this._remissionService.getReturn(returnId).toPromise(); + this._router.navigate(['/filiale', 'remission', 'create', returnDto.returnGroup], { + queryParams: { supplierId: returnDto.supplier.id }, + }); + } catch (err) { + this._modal.open({ + content: UiErrorModalComponent, + title: 'Fehler beim fortsetzen der Remission', + data: err, + }); + } + } else { + this._modal.open({ + content: UiErrorModalComponent, + title: 'Fehler beim Abschließen der Remission', + data: new Error('Keine gültige ID'), + }); + } + } + + async completeRemission() { + const returnId = +this._activatedRoute.snapshot.params?.returnId; + + if (returnId) { + try { + await this._remissionService.completeRemission(returnId); + this.navigateToList(); + } catch (err) { + this._modal.open({ + content: UiErrorModalComponent, + title: 'Fehler beim Abschließen der Remission', + data: err, + }); + } + } else { + this._modal.open({ + content: UiErrorModalComponent, + title: 'Fehler beim Abschließen der Remission', + data: new Error('Keine gültige ID'), + }); + } + } + + navigateToList() { + this._router.navigate(['/filiale', 'remission', 'list']); + } + + addBreadcrumbIfNotExists() { + const returnId = +this._activatedRoute.snapshot.params?.returnId; + this._breadcrumb.addBreadcrumbIfNotExists({ + key: this._config.get('process.ids.remission'), + name: 'Wanne abgeschlossen', + path: `/filiale/remission/${returnId}/finish-remission`, + params: this._activatedRoute.snapshot.queryParams, + section: 'branch', + tags: ['remission', 'finish-remission'], + }); + } +} diff --git a/apps/page/remission/src/lib/finish-remission/finish-remission.module.ts b/apps/page/remission/src/lib/finish-remission/finish-remission.module.ts new file mode 100644 index 000000000..6cf745b47 --- /dev/null +++ b/apps/page/remission/src/lib/finish-remission/finish-remission.module.ts @@ -0,0 +1,12 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { UiIconModule } from '@ui/icon'; +import { FinishRemissionComponent } from './finish-remission.component'; + +@NgModule({ + imports: [CommonModule, UiIconModule], + exports: [FinishRemissionComponent], + declarations: [FinishRemissionComponent], + providers: [], +}) +export class FinishRemissionModule {} diff --git a/apps/page/remission/src/lib/finish-shipping-document/finish-shipping-document.component.html b/apps/page/remission/src/lib/finish-shipping-document/finish-shipping-document.component.html new file mode 100644 index 000000000..cfc535f85 --- /dev/null +++ b/apps/page/remission/src/lib/finish-shipping-document/finish-shipping-document.component.html @@ -0,0 +1,15 @@ +
+

Wannennummer scannen

+

Scannen Sie die Wannennummer
um den Warenbegleitschein
abzuschließen.

+ + +
diff --git a/apps/page/remission/src/lib/finish-shipping-document/finish-shipping-document.component.scss b/apps/page/remission/src/lib/finish-shipping-document/finish-shipping-document.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/apps/page/remission/src/lib/finish-shipping-document/finish-shipping-document.component.ts b/apps/page/remission/src/lib/finish-shipping-document/finish-shipping-document.component.ts new file mode 100644 index 000000000..52338a8fe --- /dev/null +++ b/apps/page/remission/src/lib/finish-shipping-document/finish-shipping-document.component.ts @@ -0,0 +1,153 @@ +import { ChangeDetectionStrategy, Component, Inject, OnDestroy, OnInit, Optional, ViewChild } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { BreadcrumbService } from '@core/breadcrumb'; +import { Config } from '@core/config'; +import { DomainRemissionService } from '@domain/remission'; +import { UiFilterScanProvider } from '@ui/filter'; +import { UiDialogModalComponent, UiErrorModalComponent, UiModalService } from '@ui/modal'; +import { UiSearchboxNextComponent } from '@ui/searchbox'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; + +@Component({ + selector: 'page-finish-shipping-document', + templateUrl: 'finish-shipping-document.component.html', + styleUrls: ['finish-shipping-document.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class FinishShippingDocumentComponent implements OnInit, OnDestroy { + private _onDestroy$ = new Subject(); + + @ViewChild(UiSearchboxNextComponent) + searchboxComponent: UiSearchboxNextComponent; + + loading$ = new Subject(); + + hint$ = new Subject(); + + private _scanProvider: UiFilterScanProvider; + get scanProvider() { + return this._scanProvider; + } + + constructor( + @Inject(UiFilterScanProvider) @Optional() private scanProviders: UiFilterScanProvider[], + private _activatedRoute: ActivatedRoute, + private _modal: UiModalService, + private _remissionService: DomainRemissionService, + private _router: Router, + private _breadcrumb: BreadcrumbService, + private _config: Config + ) {} + + ngOnInit() { + this._scanProvider = this.scanProviders?.find((provider) => provider.for === 'shipping-document'); + this.removeBreadcrumbs(); + this.addBreadcrumbIfNotExists(); + } + + ngOnDestroy(): void { + this._onDestroy$.next(); + this._onDestroy$.complete(); + } + + search(query: string) { + if (!query) { + this.hint$.next('Ungültige Eingabe'); + return; + } + + if (query.length !== 14) { + this.hint$.next('14 Zeichen benötigt'); + return; + } + + const modal = this._modal.open({ + content: UiDialogModalComponent, + title: `Ist die Wannenummer korrekt oder möchten Sie die\nWannenummer erneut scannen?\n\n${query}`, + data: { + actions: [ + { label: 'Erneut scannen', command: false }, + { label: 'Nummer korrekt', selected: true, command: true }, + ], + }, + }); + + modal.afterClosed$.pipe(takeUntil(this._onDestroy$)).subscribe(async (result) => { + if (result?.data) { + await this.completeReceipt(query); + await this.completeReturn(); + this.navigateToFinishRemission(); + } else { + this.searchboxComponent.clear(); + } + }); + } + + async completeReceipt(packageCode: string) { + const returnId = +this._activatedRoute.snapshot.params?.returnId; + const receiptId = +this._activatedRoute.snapshot.params?.receiptId; + + if (receiptId) { + try { + await this._remissionService.completeReceipt(returnId, receiptId, packageCode); + } catch (err) { + this._modal.open({ + content: UiErrorModalComponent, + title: 'Fehler beim Abschließen des Warenbegleitscheins', + data: err, + }); + } + } else { + this._modal.open({ + content: UiErrorModalComponent, + title: 'Fehler beim Abschließen des Warenbegleitscheins', + data: new Error('Keine gültige ID'), + }); + } + } + + async completeReturn() { + const returnId = +this._activatedRoute.snapshot.params?.returnId; + + if (returnId) { + try { + await this._remissionService.completeReturn(returnId); + } catch (err) { + this._modal.open({ + content: UiErrorModalComponent, + title: 'Fehler beim Abschließen der Remission', + data: err, + }); + } + } else { + this._modal.open({ + content: UiErrorModalComponent, + title: 'Fehler beim Abschließen der Remission', + data: new Error('Keine gültige ID'), + }); + } + } + + navigateToFinishRemission() { + this._router.navigate(['../../', 'finish-remission'], { relativeTo: this._activatedRoute }); + } + + addBreadcrumbIfNotExists() { + const returnId = +this._activatedRoute.snapshot.params?.returnId; + const receiptId = +this._activatedRoute.snapshot.params?.receiptId; + + this._breadcrumb.addBreadcrumbIfNotExists({ + key: this._config.get('process.ids.remission'), + name: 'Wannennummer scannen', + path: `/filiale/remission/${returnId}/finish-shipping-document/${receiptId}`, + params: this._activatedRoute.snapshot.queryParams, + section: 'branch', + tags: ['remission', 'finish-shipping-document'], + }); + } + + removeBreadcrumbs() { + this._breadcrumb.removeBreadcrumbsByKeyAndTags(this._config.get('process.ids.remission'), ['remission', 'finish-remission']); + } +} diff --git a/apps/page/remission/src/lib/finish-shipping-document/finish-shipping-document.module.ts b/apps/page/remission/src/lib/finish-shipping-document/finish-shipping-document.module.ts new file mode 100644 index 000000000..320548e10 --- /dev/null +++ b/apps/page/remission/src/lib/finish-shipping-document/finish-shipping-document.module.ts @@ -0,0 +1,12 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { UiSearchboxNextModule } from '@ui/searchbox'; +import { FinishShippingDocumentComponent } from './finish-shipping-document.component'; + +@NgModule({ + imports: [CommonModule, UiSearchboxNextModule], + exports: [FinishShippingDocumentComponent], + declarations: [FinishShippingDocumentComponent], + providers: [], +}) +export class FinishShippingDocumentModule {} diff --git a/apps/page/remission/src/lib/index.ts b/apps/page/remission/src/lib/index.ts new file mode 100644 index 000000000..e87528ada --- /dev/null +++ b/apps/page/remission/src/lib/index.ts @@ -0,0 +1,7 @@ +// start:ng42.barrel +export * from './remission-routing.module'; +export * from './remission.component'; +export * from './remission.module'; +export * from './remission-list'; +export * from './providers'; +// end:ng42.barrel diff --git a/apps/page/remission/src/lib/modals/add-product-modal/add-product-modal.component.html b/apps/page/remission/src/lib/modals/add-product-modal/add-product-modal.component.html new file mode 100644 index 000000000..958f3badc --- /dev/null +++ b/apps/page/remission/src/lib/modals/add-product-modal/add-product-modal.component.html @@ -0,0 +1,53 @@ +
+
+
+ +
+
+
+

{{ title }}

+ {{ item.product.productGroup }}:{{ item.product.productGroup | productGroup }} + +
+
+
+
Format
+
+ + {{ item.product.formatDetail }} +
+
+
+
Kennzeichen
+ +
+ + + {{ assortment.label }} + +
+
+
+
Remigrund
+
+ + + +
+
+
+
+
{{ message }}
+
+
+
+
+
+
+ +
diff --git a/apps/page/remission/src/lib/modals/add-product-modal/add-product-modal.component.scss b/apps/page/remission/src/lib/modals/add-product-modal/add-product-modal.component.scss new file mode 100644 index 000000000..f4dcb297c --- /dev/null +++ b/apps/page/remission/src/lib/modals/add-product-modal/add-product-modal.component.scss @@ -0,0 +1,17 @@ +hr { + @apply text-branch -ml-4; + width: calc(100% + 2rem); + border-top-width: 3px; +} + +.thumbnail { + max-width: 75px; +} + +.actions { + @apply grid justify-end mt-8; + + .cta-add { + @apply border-2 border-solid border-brand rounded-full py-3 px-6 font-bold text-lg outline-none self-end whitespace-nowrap bg-brand text-white no-underline; + } +} diff --git a/apps/page/remission/src/lib/modals/add-product-modal/add-product-modal.component.ts b/apps/page/remission/src/lib/modals/add-product-modal/add-product-modal.component.ts new file mode 100644 index 000000000..5c53e3c2b --- /dev/null +++ b/apps/page/remission/src/lib/modals/add-product-modal/add-product-modal.component.ts @@ -0,0 +1,73 @@ +import { Component, ChangeDetectionStrategy, OnDestroy } from '@angular/core'; +import { FormControl, FormGroup } from '@angular/forms'; +import { DomainRemissionService } from '@domain/remission'; +import { ReturnItemDTO } from '@swagger/remi'; +import { UiErrorModalComponent, UiModalRef, UiModalService } from '@ui/modal'; +import { BehaviorSubject, Subject } from 'rxjs'; +import { first, map, takeUntil } from 'rxjs/operators'; +import { AddProductModalData } from './add-product-modal.data'; + +@Component({ + selector: 'page-add-product-modal', + templateUrl: 'add-product-modal.component.html', + styleUrls: ['add-product-modal.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class AddProductModalComponent implements OnDestroy { + private _onDestroy$ = new Subject(); + + message$ = new BehaviorSubject(undefined); + + item: ReturnItemDTO; + + form: FormGroup; + + get title() { + return [this.item.product.contributors, this.item.product.name].filter((f) => !!f).join(' - '); + } + + get returnReasons$() { + return this._remiService.getReturnReasons().pipe(map((reasons) => reasons.map((r) => r.value))); + } + + constructor( + private _modalRef: UiModalRef, + private _remiService: DomainRemissionService, + private _modal: UiModalService + ) { + this.item = this._modalRef.data.item; + + this.form = new FormGroup({ + quantity: new FormControl(this.item.predefinedReturnQuantity), + reason: new FormControl(this.item.returnReason), + }); + + this.form + .get('reason') + .valueChanges.pipe(takeUntil(this._onDestroy$)) + .subscribe((_) => this.message$.next(undefined)); + } + + ngOnDestroy() { + this._onDestroy$.next(); + this._onDestroy$.complete(); + } + + async addClick() { + if (!this.form.value.reason) { + this.message$.next('Bitte wählen Sie einen Remigrund'); + return; + } + + try { + await this._remiService.addProductToRemit(this.item, this.form.value.reason, this.form.value.quantity).pipe(first()).toPromise(); + this._modalRef.close(true); + } catch (err) { + this._modalRef.close(); + this._modal.open({ + content: UiErrorModalComponent, + data: err, + }); + } + } +} diff --git a/apps/page/remission/src/lib/modals/add-product-modal/add-product-modal.data.ts b/apps/page/remission/src/lib/modals/add-product-modal/add-product-modal.data.ts new file mode 100644 index 000000000..5e532390d --- /dev/null +++ b/apps/page/remission/src/lib/modals/add-product-modal/add-product-modal.data.ts @@ -0,0 +1,5 @@ +import { ReturnItemDTO } from '@swagger/remi'; + +export interface AddProductModalData { + item: ReturnItemDTO; +} diff --git a/apps/page/remission/src/lib/modals/add-product-modal/add-product-modal.module.ts b/apps/page/remission/src/lib/modals/add-product-modal/add-product-modal.module.ts new file mode 100644 index 000000000..a8b91d39a --- /dev/null +++ b/apps/page/remission/src/lib/modals/add-product-modal/add-product-modal.module.ts @@ -0,0 +1,30 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { AddProductModalComponent } from './add-product-modal.component'; +import { UiModalModule } from '@ui/modal'; +import { ProductImageModule } from '@cdn/product-image'; +import { RemissionPipeModule } from '../../pipes'; +import { UiSelectModule } from '@ui/select'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { UiQuantityDropdownModule } from '@ui/quantity-dropdown'; +import { UiTooltipModule } from '@ui/tooltip'; +import { UiCommonModule } from '@ui/common'; + +@NgModule({ + imports: [ + CommonModule, + UiCommonModule, + UiModalModule, + ProductImageModule, + RemissionPipeModule, + UiSelectModule, + UiQuantityDropdownModule, + FormsModule, + ReactiveFormsModule, + UiTooltipModule, + ], + exports: [AddProductModalComponent], + declarations: [AddProductModalComponent], +}) +export class AddProductModalModule {} diff --git a/apps/page/remission/src/lib/modals/add-product-modal/index.ts b/apps/page/remission/src/lib/modals/add-product-modal/index.ts new file mode 100644 index 000000000..56dd6170e --- /dev/null +++ b/apps/page/remission/src/lib/modals/add-product-modal/index.ts @@ -0,0 +1,3 @@ +export * from './add-product-modal.component'; +export * from './add-product-modal.data'; +export * from './add-product-modal.module'; diff --git a/apps/page/remission/src/lib/modals/add-product-to-shipping-document-modal/add-product-to-shipping-document-modal.component.html b/apps/page/remission/src/lib/modals/add-product-to-shipping-document-modal/add-product-to-shipping-document-modal.component.html new file mode 100644 index 000000000..3f483c2d1 --- /dev/null +++ b/apps/page/remission/src/lib/modals/add-product-to-shipping-document-modal/add-product-to-shipping-document-modal.component.html @@ -0,0 +1,27 @@ +
+

Wählen Sie die korrekte Platzierung aus

+ + + + + +

Wieviele Exemplare können remittiert werden?

+ + + + + +
+ + + +
+
diff --git a/apps/page/remission/src/lib/modals/add-product-to-shipping-document-modal/add-product-to-shipping-document-modal.component.scss b/apps/page/remission/src/lib/modals/add-product-to-shipping-document-modal/add-product-to-shipping-document-modal.component.scss new file mode 100644 index 000000000..3fad0e06f --- /dev/null +++ b/apps/page/remission/src/lib/modals/add-product-to-shipping-document-modal/add-product-to-shipping-document-modal.component.scss @@ -0,0 +1,3 @@ +button:disabled { + @apply cursor-not-allowed bg-disabled-branch; +} diff --git a/apps/page/remission/src/lib/modals/add-product-to-shipping-document-modal/add-product-to-shipping-document-modal.component.ts b/apps/page/remission/src/lib/modals/add-product-to-shipping-document-modal/add-product-to-shipping-document-modal.component.ts new file mode 100644 index 000000000..bc8bb4eec --- /dev/null +++ b/apps/page/remission/src/lib/modals/add-product-to-shipping-document-modal/add-product-to-shipping-document-modal.component.ts @@ -0,0 +1,36 @@ +import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; +import { FormControl, FormGroup, ValidationErrors, Validators } from '@angular/forms'; +import { RemissionPlacementType } from '@isa/remission'; +import { UiModalRef } from '@ui/modal'; + +@Component({ + selector: 'page-add-product-to-shipping-document-modal', + templateUrl: 'add-product-to-shipping-document-modal.component.html', + styleUrls: ['add-product-to-shipping-document-modal.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class AddProductToShippingDocumentModalComponent implements OnInit { + validateQuantity(c: FormControl): ValidationErrors | null { + return c.value < 0 || c.value > 1000 ? { invalid: 'Menge ist ungültig' } : null; + } + + remissionPlacementTypes: RemissionPlacementType[] = ['Stapel', 'Leistung']; + form: FormGroup; + + constructor(private _modalRef: UiModalRef<{ action: string; quantity?: number; placementType?: RemissionPlacementType }>) {} + + ngOnInit() { + this.form = new FormGroup({ + placementType: new FormControl(''), + quantity: new FormControl(1, [Validators.required, this.validateQuantity]), + }); + } + + async notFound() { + this._modalRef.close({ action: 'notFound' }); + } + + async remit() { + this._modalRef.close({ action: 'remit', quantity: this.form.value.quantity, placementType: this.form.value.placementType }); + } +} diff --git a/apps/page/remission/src/lib/modals/add-product-to-shipping-document-modal/add-product-to-shipping-document-modal.module.ts b/apps/page/remission/src/lib/modals/add-product-to-shipping-document-modal/add-product-to-shipping-document-modal.module.ts new file mode 100644 index 000000000..218a90736 --- /dev/null +++ b/apps/page/remission/src/lib/modals/add-product-to-shipping-document-modal/add-product-to-shipping-document-modal.module.ts @@ -0,0 +1,16 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { UiCommonModule } from '@ui/common'; +import { UiFormControlModule } from '@ui/form-control'; +import { UiInputModule } from '@ui/input'; +import { UiSelectModule } from '@ui/select'; +import { AddProductToShippingDocumentModalComponent } from './add-product-to-shipping-document-modal.component'; + +@NgModule({ + imports: [CommonModule, UiCommonModule, UiSelectModule, UiInputModule, UiFormControlModule, FormsModule, ReactiveFormsModule], + exports: [AddProductToShippingDocumentModalComponent], + declarations: [AddProductToShippingDocumentModalComponent], + providers: [], +}) +export class AddProductToShippingDocumentModalModule {} diff --git a/apps/page/remission/src/lib/pipes/assortment.pipe.ts b/apps/page/remission/src/lib/pipes/assortment.pipe.ts new file mode 100644 index 000000000..8eb9de351 --- /dev/null +++ b/apps/page/remission/src/lib/pipes/assortment.pipe.ts @@ -0,0 +1,16 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'assortment', + pure: true, +}) +export class AssortmentPipe implements PipeTransform { + transform(value: string): { key: string; label: string } | undefined { + const assortmentSplit = value?.split('|'); + if (assortmentSplit && assortmentSplit.length > 0) { + return { key: assortmentSplit[1], label: assortmentSplit[0] }; + } + + return undefined; + } +} diff --git a/apps/page/remission/src/lib/pipes/index.ts b/apps/page/remission/src/lib/pipes/index.ts new file mode 100644 index 000000000..645e68172 --- /dev/null +++ b/apps/page/remission/src/lib/pipes/index.ts @@ -0,0 +1,2 @@ +export * from './product-group.pipe'; +export * from './remission-pipe.module'; diff --git a/apps/page/remission/src/lib/pipes/product-group.pipe.ts b/apps/page/remission/src/lib/pipes/product-group.pipe.ts new file mode 100644 index 000000000..e2e9897a2 --- /dev/null +++ b/apps/page/remission/src/lib/pipes/product-group.pipe.ts @@ -0,0 +1,40 @@ +import { ChangeDetectorRef, OnDestroy, Pipe, PipeTransform } from '@angular/core'; +import { DomainRemissionService } from '@domain/remission'; +import { Subject } from 'rxjs'; +import { distinctUntilChanged, map, switchMap } from 'rxjs/operators'; + +@Pipe({ + name: 'productGroup', + pure: false, +}) +export class ProductGroupPipe implements PipeTransform, OnDestroy { + result: string; + + productGroup$ = new Subject(); + + productGroupSubscription = this.productGroup$ + .pipe( + distinctUntilChanged(), + switchMap((productGroup) => + this._domainRemission + .getProductGroups() + .pipe(map((productGroups) => productGroups.find((productGroupItem) => productGroupItem.key === productGroup))) + ) + ) + .subscribe((productGroup) => { + this.result = productGroup?.value; + this._cdr.markForCheck(); + }); + + constructor(private readonly _domainRemission: DomainRemissionService, private _cdr: ChangeDetectorRef) {} + + ngOnDestroy(): void { + this.productGroupSubscription.unsubscribe(); + } + + transform(value: string): any { + this.productGroup$.next(value); + + return this.result; + } +} diff --git a/apps/page/remission/src/lib/pipes/remission-pipe.module.ts b/apps/page/remission/src/lib/pipes/remission-pipe.module.ts new file mode 100644 index 000000000..a56520136 --- /dev/null +++ b/apps/page/remission/src/lib/pipes/remission-pipe.module.ts @@ -0,0 +1,11 @@ +import { NgModule } from '@angular/core'; +import { AssortmentPipe } from './assortment.pipe'; +import { ProductGroupPipe } from './product-group.pipe'; +import { ShortReceiptNumberPipe } from './short-receipt-number.pipe'; +import { SupplierPipe } from './supplier.pipe'; + +@NgModule({ + declarations: [ProductGroupPipe, AssortmentPipe, ShortReceiptNumberPipe, SupplierPipe], + exports: [ProductGroupPipe, AssortmentPipe, ShortReceiptNumberPipe, SupplierPipe], +}) +export class RemissionPipeModule {} diff --git a/apps/page/remission/src/lib/pipes/short-receipt-number.pipe.ts b/apps/page/remission/src/lib/pipes/short-receipt-number.pipe.ts new file mode 100644 index 000000000..678e17745 --- /dev/null +++ b/apps/page/remission/src/lib/pipes/short-receipt-number.pipe.ts @@ -0,0 +1,10 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'shortReceiptNumber', +}) +export class ShortReceiptNumberPipe implements PipeTransform { + transform(value: string): string { + return value.substring(6, 12); + } +} diff --git a/apps/page/remission/src/lib/pipes/supplier.pipe.ts b/apps/page/remission/src/lib/pipes/supplier.pipe.ts new file mode 100644 index 000000000..99ca10b12 --- /dev/null +++ b/apps/page/remission/src/lib/pipes/supplier.pipe.ts @@ -0,0 +1,31 @@ +import { ChangeDetectorRef, OnDestroy, Pipe, PipeTransform } from '@angular/core'; +import { DomainRemissionService } from '@domain/remission'; +import { combineLatest, Subject } from 'rxjs'; +import { map } from 'rxjs/operators'; + +@Pipe({ + name: 'supplier', + pure: false, +}) +export class SupplierPipe implements PipeTransform, OnDestroy { + result: string; + supplierId$ = new Subject(); + + supplierIdSubscription = combineLatest([this._remissionService.getSuppliers(), this.supplierId$]) + .pipe(map(([suppliers, supplierId]) => suppliers?.find((supplier) => supplier.id === supplierId)?.name)) + .subscribe((result) => { + this.result = result; + this._cdr.markForCheck(); + }); + + constructor(private _remissionService: DomainRemissionService, private _cdr: ChangeDetectorRef) {} + ngOnDestroy(): void { + this.supplierIdSubscription.unsubscribe(); + } + + transform(supplierId: number): string { + this.supplierId$.next(supplierId); + + return this.result; + } +} diff --git a/apps/page/remission/src/lib/providers/add-product-scan.provider.ts b/apps/page/remission/src/lib/providers/add-product-scan.provider.ts new file mode 100644 index 000000000..c96715ba8 --- /dev/null +++ b/apps/page/remission/src/lib/providers/add-product-scan.provider.ts @@ -0,0 +1,24 @@ +import { Injectable } from '@angular/core'; +import { UiFilterScanProvider } from '@ui/filter'; +import { NativeContainerService } from 'native-container'; +import { Observable } from 'rxjs'; +import { catchError, filter, map } from 'rxjs/operators'; + +@Injectable() +export class AddProductScanProviderService extends UiFilterScanProvider { + for = 'add-product'; + + constructor(private nativeContainer: NativeContainerService) { + super(); + } + + scan(): Observable { + return this.nativeContainer.openScanner('remissionProduct').pipe( + filter((result) => result.status !== 'IN_PROGRESS'), + map((result) => result?.data), + catchError((err) => { + return ''; + }) + ); + } +} diff --git a/apps/page/remission/src/lib/providers/create-remission-scan.provider.ts b/apps/page/remission/src/lib/providers/create-remission-scan.provider.ts new file mode 100644 index 000000000..25cbeed82 --- /dev/null +++ b/apps/page/remission/src/lib/providers/create-remission-scan.provider.ts @@ -0,0 +1,24 @@ +import { Injectable } from '@angular/core'; +import { UiFilterScanProvider } from '@ui/filter'; +import { NativeContainerService } from 'native-container'; +import { Observable } from 'rxjs'; +import { catchError, filter, map } from 'rxjs/operators'; + +@Injectable() +export class CreateRemissionScanProviderService extends UiFilterScanProvider { + for = 'create'; + + constructor(private nativeContainer: NativeContainerService) { + super(); + } + + scan(): Observable { + return this.nativeContainer.openScanner('remissionContainer').pipe( + filter((result) => result.status !== 'IN_PROGRESS'), + map((result) => result?.data), + catchError((err) => { + return ''; + }) + ); + } +} diff --git a/apps/page/remission/src/lib/providers/finish-shipping-document-scan.provider.ts b/apps/page/remission/src/lib/providers/finish-shipping-document-scan.provider.ts new file mode 100644 index 000000000..80d98341c --- /dev/null +++ b/apps/page/remission/src/lib/providers/finish-shipping-document-scan.provider.ts @@ -0,0 +1,24 @@ +import { Injectable } from '@angular/core'; +import { UiFilterScanProvider } from '@ui/filter'; +import { NativeContainerService } from 'native-container'; +import { Observable } from 'rxjs'; +import { catchError, filter, map } from 'rxjs/operators'; + +@Injectable() +export class FinishShippingDocumentScanProviderService extends UiFilterScanProvider { + for = 'shipping-document'; + + constructor(private nativeContainer: NativeContainerService) { + super(); + } + + scan(): Observable { + return this.nativeContainer.openScanner('shippingDocument').pipe( + filter((result) => result.status !== 'IN_PROGRESS'), + map((result) => result?.data), + catchError((err) => { + return ''; + }) + ); + } +} diff --git a/apps/page/remission/src/lib/providers/index.ts b/apps/page/remission/src/lib/providers/index.ts new file mode 100644 index 000000000..cbbd5171c --- /dev/null +++ b/apps/page/remission/src/lib/providers/index.ts @@ -0,0 +1,5 @@ +// start:ng42.barrel +export * from './add-product-scan.provider'; +export * from './create-remission-scan.provider'; +export * from './finish-shipping-document-scan.provider'; +// end:ng42.barrel diff --git a/apps/page/remission/src/lib/remission-list/index.ts b/apps/page/remission/src/lib/remission-list/index.ts new file mode 100644 index 000000000..980df458b --- /dev/null +++ b/apps/page/remission/src/lib/remission-list/index.ts @@ -0,0 +1,3 @@ +// start:ng42.barrel +export * from './remission-list.component'; +// end:ng42.barrel diff --git a/apps/page/remission/src/lib/remission-list/remission-filter/remission-filter.component.html b/apps/page/remission/src/lib/remission-list/remission-filter/remission-filter.component.html new file mode 100644 index 000000000..e319e7e76 --- /dev/null +++ b/apps/page/remission/src/lib/remission-list/remission-filter/remission-filter.component.html @@ -0,0 +1,25 @@ +
+ + +

Filter

+ + + + +
+ +
+
+
diff --git a/apps/page/remission/src/lib/remission-list/remission-filter/remission-filter.component.scss b/apps/page/remission/src/lib/remission-list/remission-filter/remission-filter.component.scss new file mode 100644 index 000000000..ebcbff47c --- /dev/null +++ b/apps/page/remission/src/lib/remission-list/remission-filter/remission-filter.component.scss @@ -0,0 +1,43 @@ +:host { + @apply block box-border w-full; +} + +.remission-filter-content { + @apply relative mx-auto p-4; + max-width: 916px; +} + +.btn-close { + @apply absolute text-cool-grey top-3 p-4 right-4 outline-none border-none bg-transparent; +} + +.sticky-cta-wrapper { + @apply fixed text-center inset-x-0 bottom-0; + bottom: 30px; +} + +button.apply-filter { + @apply border-none bg-brand text-white rounded-full py-cta-y-l px-cta-x-l text-cta-l font-bold; + min-width: 201px; +} + +button.apply-filter:disabled { + @apply bg-inactive-customer; +} + +button.apply-filter.loading { + padding-top: 15px; + padding-bottom: 17px; +} + +ui-selected-filter-options { + @apply my-px-8; +} + +ui-icon { + @apply inline-flex; +} + +.spin { + @apply animate-spin; +} diff --git a/apps/page/remission/src/lib/remission-list/remission-filter/remission-filter.component.ts b/apps/page/remission/src/lib/remission-list/remission-filter/remission-filter.component.ts new file mode 100644 index 000000000..17635dc40 --- /dev/null +++ b/apps/page/remission/src/lib/remission-list/remission-filter/remission-filter.component.ts @@ -0,0 +1,37 @@ +import { Component, ChangeDetectionStrategy, OnDestroy, Output, EventEmitter } from '@angular/core'; +import { UiFilter } from '@ui/filter'; +import { of, Subject } from 'rxjs'; +import { map, takeUntil } from 'rxjs/operators'; +import { RemissionListComponentStore } from '../remission-list.component-store'; + +@Component({ + selector: 'page-remission-filter', + templateUrl: 'remission-filter.component.html', + styleUrls: ['remission-filter.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class RemissionFilterComponent implements OnDestroy { + @Output() + close = new EventEmitter(); + + filter$ = this._store.filter$.pipe(map((filter) => UiFilter.create(filter))); + + private _onDestroy$ = new Subject(); + + fetching$ = of(false); + + constructor(private _store: RemissionListComponentStore) {} + + ngOnInit() { + this._store.searchCompleted.pipe(takeUntil(this._onDestroy$)).subscribe(() => this.close.emit()); + } + + ngOnDestroy() { + this._onDestroy$.next(); + this._onDestroy$.complete(); + } + + applyFilter(filter: UiFilter) { + this._store.setFilter(filter); + } +} diff --git a/apps/page/remission/src/lib/remission-list/remission-filter/remission-filter.module.ts b/apps/page/remission/src/lib/remission-list/remission-filter/remission-filter.module.ts new file mode 100644 index 000000000..6af0b35f8 --- /dev/null +++ b/apps/page/remission/src/lib/remission-list/remission-filter/remission-filter.module.ts @@ -0,0 +1,13 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { RemissionFilterComponent } from './remission-filter.component'; +import { UiIconModule } from '@ui/icon'; +import { UiFilterNextModule } from '@ui/filter'; + +@NgModule({ + imports: [CommonModule, UiIconModule, UiFilterNextModule], + exports: [RemissionFilterComponent], + declarations: [RemissionFilterComponent], +}) +export class RemissionFilterModule {} diff --git a/apps/page/remission/src/lib/remission-list/remission-list-item/index.ts b/apps/page/remission/src/lib/remission-list/remission-list-item/index.ts new file mode 100644 index 000000000..1856812a0 --- /dev/null +++ b/apps/page/remission/src/lib/remission-list/remission-list-item/index.ts @@ -0,0 +1,2 @@ +export * from './remission-list-item.component'; +export * from './remission-list-item.module'; diff --git a/apps/page/remission/src/lib/remission-list/remission-list-item/remission-list-item.component.html b/apps/page/remission/src/lib/remission-list/remission-list-item/remission-list-item.component.html new file mode 100644 index 000000000..0ebcc1d4f --- /dev/null +++ b/apps/page/remission/src/lib/remission-list/remission-list-item/remission-list-item.component.html @@ -0,0 +1,98 @@ +
+

{{ item.title }}

+ {{ item.productGroup }}:{{ item.productGroup | productGroup }} +
+
+ {{ item?.dto?.impediment?.comment ? item?.dto?.impediment?.comment : 'Restmenge' }} + ({{ item?.dto?.impediment?.attempts }}) +
+
+
+ item.dto.product.name +
+ +
+
+ + {{ item.formatDetail }} +
+
+ {{ item.ean }} +
+
+ {{ item.price.value.value | currency: item.price.value.currency:'code' }} +
+
+ + + {{ assortment.label }} + +
+
+ +
+
+
aktueller Bestand
+
{{ item.inStock }}x
+
+
+
Remi-Menge
+
{{ item.remissionQuantity }}x
+
+
+
übriger Bestand
+
{{ item.remainingQuantity }}x
+
+
+
Platz
+
{{ item.placementType }}
+
+
+
Remigrund
+
{{ item.remissionReason }}
+
+
+ +
+
+ {{ item.department }} +
+
+
+ + +
+ + +
+
+ + +
+ +
+
diff --git a/apps/page/remission/src/lib/remission-list/remission-list-item/remission-list-item.component.scss b/apps/page/remission/src/lib/remission-list/remission-list-item/remission-list-item.component.scss new file mode 100644 index 000000000..3f6685f0d --- /dev/null +++ b/apps/page/remission/src/lib/remission-list/remission-list-item/remission-list-item.component.scss @@ -0,0 +1,7 @@ +:host { + @apply grid grid-flow-row gap-2 p-4 bg-white rounded-card; +} + +.product-data-grid { + grid-template-columns: 90px 190px 340px auto; +} diff --git a/apps/page/remission/src/lib/remission-list/remission-list-item/remission-list-item.component.ts b/apps/page/remission/src/lib/remission-list/remission-list-item/remission-list-item.component.ts new file mode 100644 index 000000000..86454b160 --- /dev/null +++ b/apps/page/remission/src/lib/remission-list/remission-list-item/remission-list-item.component.ts @@ -0,0 +1,153 @@ +import { Component, ChangeDetectionStrategy, Input, OnDestroy, EventEmitter, Output } from '@angular/core'; +import { DomainRemissionService, RemissionListItem } from '@domain/remission'; +import { RemissionPlacementType } from '@isa/remission'; +import { ReturnDTO } from '@swagger/remi'; +import { UiDialogModalComponent, UiErrorModalComponent, UiModalService } from '@ui/modal'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; +import { AddProductToShippingDocumentModalComponent } from '../../modals/add-product-to-shipping-document-modal/add-product-to-shipping-document-modal.component'; +import { RemissionListComponentStore } from '../remission-list.component-store'; + +@Component({ + selector: 'page-remission-list-item', + templateUrl: 'remission-list-item.component.html', + styleUrls: ['remission-list-item.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class RemissionListItemComponent implements OnDestroy { + private _onDestroy$ = new Subject(); + + @Input() + item: RemissionListItem; + + @Input() + returnDto: ReturnDTO; + + get firstReceipt() { + return this.returnDto?.receipts?.find((_) => true)?.data; + } + + constructor( + private _modal: UiModalService, + private _remissionService: DomainRemissionService, + private _store: RemissionListComponentStore + ) {} + + ngOnDestroy() { + this._onDestroy$.next(); + this._onDestroy$.complete(); + } + + addProductToShippingDocument() { + const modal = this._modal.open({ + content: AddProductToShippingDocumentModalComponent, + title: 'Remimenge / Platzierung ändern', + data: { + item: this.item, + returnDto: this.returnDto, + }, + }); + + modal.afterClosed$.pipe(takeUntil(this._onDestroy$)).subscribe((result) => { + if (result?.data?.action === 'remit') { + this.addReturnItemOrSuggestion({ quantity: result.data.quantity, placementType: result.data.placementType }); + } else if (result?.data?.action === 'notFound') { + this.returnImpediment(); + } + }); + } + + remit() { + const modal = this._modal.open({ + content: UiDialogModalComponent, + title: 'Remittieren', + data: { + content: `Sie sind gerade dabei alle ${this.item.remissionQuantity} Exemplare von einem Leistungsplatz zu\nremittieren. Sind wirklich alle Exemplare in der Leistung?`, + actions: [ + { label: 'Ja', selected: true, command: 'remit' }, + { label: 'Abbrechen', command: 'close' }, + ], + }, + }); + + modal.afterClosed$.pipe(takeUntil(this._onDestroy$)).subscribe((result) => { + if (result?.data === 'remit') { + this.addReturnItemOrSuggestion({ quantity: this.item.remissionQuantity }); + } + }); + } + + async removeReturnItem() { + try { + await this._remissionService.removeReturnItemFromList({ itemId: this.item?.dto?.id }).toPromise(); + this.reload(); + } catch (err) { + this._modal.open({ + content: UiErrorModalComponent, + title: 'Fehler beim entfernen eines Artikels von der Remissionsliste', + data: err, + }); + } + } + + async addReturnItemOrSuggestion({ quantity, placementType }: { quantity?: number; placementType?: RemissionPlacementType }) { + try { + this._store.removeItem(this.item); + + // Pflichtremission + if (this.item.dtoType === 'return') { + await this._remissionService + .addReturnItem({ + returnId: this.returnDto.id, + receiptId: this.firstReceipt.id, + returnItemId: this.item.dto.id, + inStock: this.item.inStock, + quantity, + placementType, + }) + .toPromise(); + } + // Abteilungsremission + else if (this.item.dtoType === 'suggestion') { + await this._remissionService + .addReturnSuggestion({ + returnId: this.returnDto.id, + receiptId: this.firstReceipt.id, + returnSuggestionId: this.item.dto.id, + inStock: this.item.inStock, + quantity, + placementType, + }) + .toPromise(); + } + + this.reload(); + } catch (err) { + this._modal.open({ + content: UiErrorModalComponent, + title: 'Fehler beim remittieren', + data: err, + }); + } + } + + async returnImpediment() { + try { + this._store.removeItem(this.item); + + await this._remissionService.returnImpediment(this.item.dto?.id).toPromise(); + + this.reload(); + } catch (err) { + this._modal.open({ + content: UiErrorModalComponent, + title: 'Fehler bei Artikel nicht gefunden', + data: err, + }); + } + } + + reload() { + this._store.search({ newSearch: true }); + } +} diff --git a/apps/page/remission/src/lib/remission-list/remission-list-item/remission-list-item.module.ts b/apps/page/remission/src/lib/remission-list/remission-list-item/remission-list-item.module.ts new file mode 100644 index 000000000..5932518e2 --- /dev/null +++ b/apps/page/remission/src/lib/remission-list/remission-list-item/remission-list-item.module.ts @@ -0,0 +1,23 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { RemissionListItemComponent } from './remission-list-item.component'; +import { ProductImageModule } from '@cdn/product-image'; +import { RemissionPipeModule } from '../../pipes'; +import { UiTooltipModule } from '@ui/tooltip'; +import { UiCommonModule } from '@ui/common'; +import { AddProductToShippingDocumentModalModule } from '../../modals/add-product-to-shipping-document-modal/add-product-to-shipping-document-modal.module'; + +@NgModule({ + imports: [ + CommonModule, + UiCommonModule, + ProductImageModule, + RemissionPipeModule, + UiTooltipModule, + AddProductToShippingDocumentModalModule, + ], + exports: [RemissionListItemComponent], + declarations: [RemissionListItemComponent], +}) +export class RemissionListItemModule {} diff --git a/apps/page/remission/src/lib/remission-list/remission-list.component-store.ts b/apps/page/remission/src/lib/remission-list/remission-list.component-store.ts new file mode 100644 index 000000000..2693ab597 --- /dev/null +++ b/apps/page/remission/src/lib/remission-list/remission-list.component-store.ts @@ -0,0 +1,307 @@ +import { Injectable, OnDestroy } from '@angular/core'; +import { DomainRemissionService, RemissionListItem } from '@domain/remission'; +import { ComponentStore, tapResponse } from '@ngrx/component-store'; +import { SupplierDTO } from '@swagger/remi'; +import { UiFilter } from '@ui/filter'; +import { combineLatest, Observable, Subject } from 'rxjs'; +import { debounceTime, filter, switchMap, takeUntil, tap, withLatestFrom } from 'rxjs/operators'; + +export interface RemissionState { + suppliers: SupplierDTO[]; + selectedSupplierId?: number; + sources: string[]; + selectedSource?: string; + filter?: UiFilter; + hits: number; + searchOptions?: { take?: number; skip?: number }; + items?: RemissionListItem[]; + requiredCapacities: []; + fetching?: boolean; +} + +@Injectable() +export class RemissionListComponentStore extends ComponentStore implements OnDestroy { + private _onDestroy$ = new Subject(); + + private _searchCompleted = new Subject(); + + searchCompleted = this._searchCompleted.asObservable(); + + get suppliers() { + return this.get((s) => s.suppliers); + } + + get suppliers$() { + return this.select((s) => s.suppliers); + } + + get selectedSupplier() { + return this.get((s) => s.suppliers?.find((si) => si.id === s.selectedSupplierId)); + } + + get selectedSupplier$() { + return this.select((s) => s.suppliers?.find((si) => si.id === s.selectedSupplierId)); + } + + get sources() { + return this.get((s) => s.sources); + } + + get sources$() { + return this.select((s) => s.sources); + } + + get selectedSource() { + return this.get((s) => s.selectedSource); + } + + get selectedSource$() { + return this.select((s) => s.selectedSource); + } + + get filter() { + return this.get((s) => s.filter); + } + + get filter$() { + return this.select((s) => s.filter); + } + + get items() { + return this.get((s) => s.items); + } + + get items$() { + return this.select((s) => s.items); + } + + get requiredCapacities$() { + return this.select((s) => s.requiredCapacities); + } + + get fetching() { + return this.get((s) => s.fetching); + } + + get fetching$() { + return this.select((s) => s.fetching); + } + + get hits() { + return this.get((s) => s.hits); + } + + get hits$() { + return this.select((s) => s.hits); + } + + get searchOptions() { + return this.get((s) => s.searchOptions); + } + + get searchOptions$() { + return this.select((s) => s.searchOptions); + } + + constructor(private readonly _domainRemissionService: DomainRemissionService) { + super({ + suppliers: [], + sources: [], + hits: 0, + items: [], + requiredCapacities: undefined, + }); + + this.loadSuppliers(); + this.loadSources(); + this.initLoadFilter(); + this.initSearch(); + } + + ngOnDestroy(): void { + this._onDestroy$.next(); + this._onDestroy$.complete(); + } + + initLoadFilter() { + combineLatest([this.selectedSource$, this.selectedSupplier$]) + .pipe(takeUntil(this._onDestroy$)) + .subscribe(() => { + this.loadFilter(); + }); + } + + initSearch() { + combineLatest([this.selectedSource$, this.selectedSupplier$, this.filter$]) + .pipe(debounceTime(500), takeUntil(this._onDestroy$)) + .subscribe(() => this.search({})); + } + + // tslint:disable: member-ordering + loadSuppliers = this.effect(($) => + $.pipe( + tap((_) => this.setFetching(true)), + switchMap((_) => + this._domainRemissionService.getSuppliers().pipe( + tapResponse( + (suppliers) => this.setSuppliers(suppliers), + (err) => {} + ) + ) + ) + ) + ); + + loadSources = this.effect(($) => + $.pipe( + tap((_) => this.setFetching(true)), + switchMap((_) => + this._domainRemissionService.getSources().pipe( + tapResponse( + (sources) => this.setSources(sources), + (err) => {} + ) + ) + ) + ) + ); + + loadRequiredCapacities = this.effect(($) => + $.pipe( + tap((_) => this.setFetching(true)), + withLatestFrom(this.selectedSupplier$, this.filter$), + switchMap(([_, supplier, filter]) => + this._domainRemissionService.getRequiredCapacities({ supplierId: supplier?.id }).pipe( + tapResponse( + (response) => this.setRequiredCapacities(response), + (err) => {} + ) + ) + ) + ) + ); + + loadFilter = this.effect(($) => + $.pipe( + tap((_) => this.setFetching(true)), + withLatestFrom(this.selectedSupplier$, this.selectedSource$), + filter(([, selectedSupplier, selectedSource]) => !!selectedSupplier?.id && !!selectedSource), + switchMap(([, selectedSupplier, selectedSource]) => + this._domainRemissionService + .getQuerySettings({ + supplierId: selectedSupplier.id, + source: selectedSource, + }) + .pipe( + tapResponse( + (settings) => { + // Filtergruppen ausblenden, wenn keine Werte innerhalb des Filters für die Anzeige vorhanden sind + settings?.filter?.forEach((filter) => (filter.input = filter.input?.filter((input) => input.options?.values?.length > 0))); + + this.setFilter(UiFilter.create(settings)); + }, + (err) => {} + ) + ) + ) + ) + ); + + search = this.effect((options$: Observable<{ newSearch?: boolean }>) => + options$.pipe( + tap((_) => this.setFetching(true)), + withLatestFrom(this.filter$, this.selectedSource$, this.selectedSupplier$, this.searchOptions$, this.items$), + filter(([, filter, source, supplier]) => !!supplier?.id && !!source && !!filter), + switchMap(([options, filter, source, supplier, searchOptions, items]) => + this._domainRemissionService + .getItems({ + queryToken: { + ...filter?.getQueryToken(), + skip: options?.newSearch ? 0 : searchOptions?.skip ?? items?.length, + take: searchOptions?.take ?? 20, + }, + source, + supplierId: supplier.id, + }) + .pipe( + tapResponse( + (res) => { + const results = options?.newSearch + ? { result: [...res.result], hits: res.hits } + : { result: [...(items ?? []), ...(res.result ?? [])], hits: res.hits }; + this.setSearchResult(results); + this._searchCompleted.next(this.get()); + }, + (err) => {} + ) + ) + ) + ) + ); + + setFetching = this.updater((state, fetching) => ({ + ...state, + fetching, + })); + + setSearchResult = this.updater<{ hits: number; result: RemissionListItem[] }>((state, { hits, result }) => ({ + ...state, + items: result, + hits, + fetching: false, + })); + + setSuppliers = this.updater((state, suppliers) => ({ + ...state, + suppliers, + selectedSupplierId: state.selectedSupplierId ?? suppliers[0]?.id, + items: [], + })); + + setSelectedSupplierId = this.updater((state, supplierId) => { + if (state.selectedSupplierId === supplierId) { + return { ...state }; + } + return { + ...state, + selectedSupplierId: supplierId ?? state.selectedSupplierId ?? state.suppliers[0]?.id, + items: [], + }; + }); + + setSources = this.updater((state, sources) => { + return { + ...state, + sources, + selectedSource: state.selectedSource ? sources.find((source) => source === state.selectedSource) : sources[0], + items: [], + }; + }); + + setSelectedSource = this.updater((state, source) => ({ + ...state, + selectedSource: state.sources.find((s) => s === source) ?? state.selectedSource, + })); + + setFilter = this.updater((state, filter) => ({ + ...state, + filter, + items: [], + })); + + setItems = this.updater((state, items) => ({ + ...state, + items, + })); + + removeItem = this.updater((state, item) => ({ + ...state, + items: state.items.filter((i) => i.dto?.id !== item.dto?.id), + })); + + setRequiredCapacities = this.updater((state, requiredCapacities) => ({ + ...state, + requiredCapacities, + fetching: false, + })); +} diff --git a/apps/page/remission/src/lib/remission-list/remission-list.component.html b/apps/page/remission/src/lib/remission-list/remission-list.component.html new file mode 100644 index 000000000..874d4c2fb --- /dev/null +++ b/apps/page/remission/src/lib/remission-list/remission-list.component.html @@ -0,0 +1,132 @@ + + + + + + + +

Remission

+
+

+ Wählen Sie den Bereich aus dem
+ Sie Artikel remittieren möchten. +

+ +
+ +
+
+
+ +
+
+ +

+ + Artikel hinzufügen + + +

+ +

Wanne befüllen

+ +
+ +
+ +
+
+ +
+ +
+

{{ (hits$ | async) || 0 }} Titel

+
+ +
+
+
+ + +
+ + diff --git a/apps/page/remission/src/lib/remission-list/remission-list.component.scss b/apps/page/remission/src/lib/remission-list/remission-list.component.scss new file mode 100644 index 000000000..4116307a9 --- /dev/null +++ b/apps/page/remission/src/lib/remission-list/remission-list.component.scss @@ -0,0 +1,32 @@ +:host { + @apply flex flex-col w-full box-content relative; +} + +.filter { + @apply absolute font-sans flex items-center font-bold bg-inactive-branch border-0 text-regular -top-12 right-0 py-px-8 px-px-15 rounded-filter justify-center z-sticky; + + min-width: 106px; + + .label { + @apply ml-px-5; + } + + &.active { + @apply bg-active-branch text-white ml-px-5; + } +} + +.actions { + @apply fixed inline-grid grid-flow-col gap-7; + bottom: 7.25rem; + left: 50%; + transform: translateX(-50%); + + button:disabled { + @apply cursor-not-allowed bg-inactive-branch; + } +} + +::ng-deep page-remission-list ui-scroll-container .cta-scroll { + bottom: 38px !important; +} diff --git a/apps/page/remission/src/lib/remission-list/remission-list.component.ts b/apps/page/remission/src/lib/remission-list/remission-list.component.ts new file mode 100644 index 000000000..408fc54ab --- /dev/null +++ b/apps/page/remission/src/lib/remission-list/remission-list.component.ts @@ -0,0 +1,256 @@ +import { animate, AnimationBuilder, state, style, transition, trigger } from '@angular/animations'; +import { Component, ChangeDetectionStrategy, OnInit, OnDestroy, ViewChild, ElementRef } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { ApplicationService } from '@core/application'; +import { BreadcrumbService } from '@core/breadcrumb'; +import { Config } from '@core/config'; +import { DomainRemissionService } from '@domain/remission'; +import { SupplierDTO } from '@swagger/remi'; +import { Subject, Observable, combineLatest } from 'rxjs'; +import { map, takeUntil } from 'rxjs/operators'; +import { RemissionListComponentStore } from './remission-list.component-store'; +import { RemissionComponentStore } from './remission.component-store'; + +@Component({ + selector: 'page-remission-list', + templateUrl: 'remission-list.component.html', + styleUrls: ['remission-list.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, + providers: [RemissionListComponentStore, RemissionComponentStore], + animations: [ + trigger('shippingDocument', [ + state( + 'shipping-document', + style({ + opacity: 1, + transform: 'translateX(0)', + }) + ), + state( + 'remission-list', + style({ + opacity: 0, + transform: 'translateX(100%)', + position: 'absolute', + left: 0, + right: 0, + top: 0, + }) + ), + transition('shipping-document => remission-list', [animate('500ms ease-in-out')]), + transition('remission-list => shipping-document', [animate('500ms 500ms ease-in-out')]), + ]), + trigger('remissionList', [ + state( + 'shipping-document', + style({ + opacity: 0, + transform: 'translateX(-100%)', + display: 'none', + }) + ), + state( + 'remission-list', + style({ + opacity: 1, + transform: 'translateX(0)', + }) + ), + transition('shipping-document => remission-list', [animate('500ms 500ms ease-in-out')]), + transition('remission-list => shipping-document', [animate('500ms ease-in-out')]), + ]), + trigger('action', [ + transition(':enter', [ + style({ + opacity: 0, + transform: 'translateY(-100%)', + }), + animate('500ms 500ms ease-in-out'), + ]), + ]), + ], +}) +export class RemissionListComponent implements OnInit, OnDestroy { + get processId() { + return this._config.get('process.ids.remission'); + } + + @ViewChild('shippingDocument', { read: ElementRef }) + shippingDocumentElementRef: ElementRef; + + @ViewChild('remissionList', { read: ElementRef }) + remissionListElementRef: ElementRef; + + private _onDestroy$ = new Subject(); + + returnCount$: Observable; + + get suppliers$() { + return this._remissionListStore.suppliers$; + } + + get selectedSupplier$() { + return this._remissionListStore.selectedSupplier$; + } + + get selectedSupplierId$() { + return this._remissionListStore.selectedSupplier$.pipe(map((s) => s?.id)); + } + + get sources$() { + return this._remissionListStore.sources$; + } + + get selectedSource$() { + return this._remissionListStore.selectedSource$; + } + + get items$() { + return this._remissionListStore.items$; + } + + get requiredCapacities$() { + return this._remissionListStore.requiredCapacities$; + } + + get hits$() { + return this._remissionListStore.hits$; + } + + get fetching$() { + return this._remissionListStore.fetching$; + } + + get return$() { + return this._remissionStore.return$; + } + + get firstReceiptId$() { + return this.return$.pipe(map((returnDto) => returnDto?.receipts?.find((_) => true)?.data?.id)); + } + + showShippingDocument$ = this._activatedRoute.fragment.pipe(map((fragment) => fragment === 'shipping-document')); + + showContent$ = this._activatedRoute.fragment.pipe( + map((fragment) => (fragment === 'shipping-document' ? 'shipping-document' : 'remission-list')) + ); + + showStartRemissionAction$ = combineLatest([this.fetching$, this.hits$, this.return$]).pipe( + map(([fetching, hits, r]) => !fetching && hits > 0 && !r) + ); + + finishShippingDocumentDisabled$ = this.return$.pipe( + map((returnDto) => returnDto?.receipts?.find((_) => true)?.data?.items?.length === 0) + ); + + filteredSuppliers$ = combineLatest([this._remissionStore.uncompleted$, this.suppliers$, this.selectedSupplier$]).pipe( + map(([uncompleted, suppliers, selectedSupplier]) => { + if (!uncompleted) { + return suppliers; + } + return suppliers.filter((supplier) => supplier?.id === selectedSupplier?.id); + }) + ); + + constructor( + private readonly _remissionListStore: RemissionListComponentStore, + private readonly _remissionStore: RemissionComponentStore, + private readonly _remissionService: DomainRemissionService, + private readonly _router: Router, + private readonly _activatedRoute: ActivatedRoute, + private readonly _breadcrumb: BreadcrumbService, + private readonly _config: Config, + private readonly _applicationService: ApplicationService + ) {} + + ngOnInit() { + this.removeBreadcrumbs(); + + this._activatedRoute.queryParams.pipe(takeUntil(this._onDestroy$)).subscribe((params) => { + const { supplier, source } = params; + if (supplier) { + this._remissionListStore.setSelectedSupplierId(+supplier); + } + if (source) { + this._remissionListStore.setSelectedSource(source); + + if (source === 'Abteilungsremission') { + this._remissionListStore.loadRequiredCapacities(); + } + } + }); + + this.returnCount$ = this._remissionService.getReturns(false).pipe(map((items) => items?.length || 0)); + + this._activatedRoute.params.pipe(takeUntil(this._onDestroy$)).subscribe((params) => { + const id = +params.returnId; + if (!!id) { + this._remissionStore.getReturn(id); + } + }); + + this._remissionStore.return$.pipe(takeUntil(this._onDestroy$)).subscribe((r) => { + if (!!r) { + this._remissionListStore.setSelectedSupplierId(r.supplier.id); + } + }); + + this._remissionStore.getReturnCompleted.pipe(takeUntil(this._onDestroy$)).subscribe((returnDto) => { + if (!returnDto || !!returnDto.completed) { + this._applicationService.patchProcessData(this.processId, { + active: undefined, + }); + this._router.navigate(['./'], { relativeTo: this._activatedRoute.parent }); + } + }); + } + + ngOnDestroy(): void { + this._onDestroy$.next(); + this._onDestroy$.complete(); + } + + setSupplier(supplier: SupplierDTO) { + this._router.navigate([], { + queryParams: { + supplier: supplier.id, + }, + queryParamsHandling: 'merge', + }); + } + + setSource(source: string) { + this._router.navigate([], { + queryParams: { + source, + }, + queryParamsHandling: 'merge', + }); + } + + removeBreadcrumbs() { + this._breadcrumb.removeBreadcrumbsByKeyAndTags(this.processId, ['remission', 'shipping-documents']); + this._breadcrumb.removeBreadcrumbsByKeyAndTags(this.processId, ['remission', 'add-product']); + this._breadcrumb.removeBreadcrumbsByKeyAndTags(this.processId, ['remission', 'create']); + this._breadcrumb.removeBreadcrumbsByKeyAndTags(this.processId, ['remission', 'finish-shipping-document']); + this._breadcrumb.removeBreadcrumbsByKeyAndTags(this.processId, ['remission', 'finish-remission']); + } + + loadMore() { + if (this._activatedRoute.snapshot.fragment === 'shipping-document') { + return; + } + + if (this._remissionListStore.hits > this._remissionListStore.items.length && !this._remissionListStore.fetching) { + this._remissionListStore.search({}); + } + } + + reloadReturn() { + this._remissionStore.reloadReturn(); + } + + shippingDocumentDeleted() { + this._router.navigate(['/filiale', 'remission', 'list']); + } +} diff --git a/apps/page/remission/src/lib/remission-list/remission-list.module.ts b/apps/page/remission/src/lib/remission-list/remission-list.module.ts new file mode 100644 index 000000000..f43966c87 --- /dev/null +++ b/apps/page/remission/src/lib/remission-list/remission-list.module.ts @@ -0,0 +1,31 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { RemissionListComponent } from './remission-list.component'; +import { ShellFilterOverlayModule } from '@shell/filter-overlay'; +import { UiIconModule } from '@ui/icon'; +import { RemissionFilterModule } from './remission-filter/remission-filter.module'; +import { RemissionListItemModule } from './remission-list-item'; +import { RouterModule } from '@angular/router'; +import { RequiredCapacitiesModule } from '../required-capacities/required-capacities.module'; +import { UiSpinnerModule } from '@ui/spinner'; +import { UiScrollContainerModule } from '@ui/scroll-container'; +import { SharedShippingDocumentDetailsModule } from '../shared/shipping-document-details/shipping-document-details.module'; + +@NgModule({ + imports: [ + CommonModule, + ShellFilterOverlayModule, + UiIconModule, + RemissionFilterModule, + RemissionListItemModule, + RouterModule, + RequiredCapacitiesModule, + UiSpinnerModule, + UiScrollContainerModule, + SharedShippingDocumentDetailsModule, + ], + exports: [RemissionListComponent], + declarations: [RemissionListComponent], +}) +export class RemissionListModule {} diff --git a/apps/page/remission/src/lib/remission-list/remission.component-store.ts b/apps/page/remission/src/lib/remission-list/remission.component-store.ts new file mode 100644 index 000000000..506dbf598 --- /dev/null +++ b/apps/page/remission/src/lib/remission-list/remission.component-store.ts @@ -0,0 +1,62 @@ +import { Injectable } from '@angular/core'; +import { DomainRemissionService } from '@domain/remission'; +import { ComponentStore, tapResponse } from '@ngrx/component-store'; +import { ReturnDTO } from '@swagger/remi'; +import { Observable, Subject } from 'rxjs'; +import { switchMap, tap, withLatestFrom } from 'rxjs/operators'; + +export interface RemissionComponentStoreState { + return?: ReturnDTO; +} + +@Injectable() +export class RemissionComponentStore extends ComponentStore { + getReturnCompleted = new Subject(); + + get return() { + return this.get((s) => s.return); + } + + get return$() { + return this.select((s) => s.return); + } + + get uncompleted() { + return this.get((s) => !!s?.return && !s?.return?.completed); + } + + get uncompleted$() { + return this.select((s) => !!s?.return && !s?.return?.completed); + } + + constructor(private readonly _domainRemissionService: DomainRemissionService) { + super({}); + } + + getReturn = this.effect((returnId$: Observable) => + returnId$.pipe( + switchMap((id) => this._domainRemissionService.getReturn(id)), + tapResponse( + (r: ReturnDTO) => { + this.setReturn(r); + this.getReturnCompleted.next(r); + }, + (err) => { + this.getReturnCompleted.next(undefined); + } + ) + ) + ); + + setReturn = this.updater((state, r) => ({ + ...state, + return: r, + })); + + reloadReturn = this.effect(($) => + $.pipe( + withLatestFrom(this.return$), + tap(([_, returnDto]) => this.getReturn(returnDto.id)) + ) + ); +} diff --git a/apps/page/remission/src/lib/remission-routing.module.ts b/apps/page/remission/src/lib/remission-routing.module.ts new file mode 100644 index 000000000..b8e610ce9 --- /dev/null +++ b/apps/page/remission/src/lib/remission-routing.module.ts @@ -0,0 +1,78 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { AddProductComponent, AddProductModule } from './add-product'; +import { RemissionListComponent } from './remission-list'; +import { RemissionListModule } from './remission-list/remission-list.module'; +import { RemissionComponent } from './remission.component'; +import { ShippingDocumentDetailsComponent } from './shipping-document-list/shipping-document-details/shipping-document-details.component'; +import { ShippingDocumentListComponent } from './shipping-document-list/shipping-document-list.component'; +import { ShippingDocumentListModule } from './shipping-document-list/shipping-document-list.module'; +import { CreateRemissionModule } from './create-remission/create-remission.module'; +import { CreateRemissionComponent } from './create-remission/create-remission.component'; +import { FinishShippingDocumentComponent } from './finish-shipping-document/finish-shipping-document.component'; +import { FinishShippingDocumentModule } from './finish-shipping-document/finish-shipping-document.module'; +import { FinishRemissionModule } from './finish-remission/finish-remission.module'; +import { FinishRemissionComponent } from './finish-remission/finish-remission.component'; + +const routes: Routes = [ + { + path: '', + component: RemissionComponent, + children: [ + { + path: 'list', + component: RemissionListComponent, + }, + { + path: ':returnId/list', + component: RemissionListComponent, + }, + { + path: 'add-product', + component: AddProductComponent, + }, + { + path: 'shipping-documents', + component: ShippingDocumentListComponent, + }, + { + path: 'shipping-documents/:id', + component: ShippingDocumentDetailsComponent, + }, + { + path: 'create', + component: CreateRemissionComponent, + }, + { + path: 'create/:returnGroup', + component: CreateRemissionComponent, + }, + { + path: ':returnId/finish-shipping-document/:receiptId', + component: FinishShippingDocumentComponent, + }, + { + path: ':returnId/finish-remission', + component: FinishRemissionComponent, + }, + { + path: '', + redirectTo: 'list', + }, + ], + }, +]; + +@NgModule({ + imports: [ + RouterModule.forChild(routes), + RemissionListModule, + AddProductModule, + ShippingDocumentListModule, + CreateRemissionModule, + FinishShippingDocumentModule, + FinishRemissionModule, + ], + exports: [RouterModule], +}) +export class RemissionRoutingModule {} diff --git a/apps/page/remission/src/lib/remission.component.html b/apps/page/remission/src/lib/remission.component.html new file mode 100644 index 000000000..fe765d4d9 --- /dev/null +++ b/apps/page/remission/src/lib/remission.component.html @@ -0,0 +1,2 @@ + + diff --git a/apps/page/remission/src/lib/remission.component.scss b/apps/page/remission/src/lib/remission.component.scss new file mode 100644 index 000000000..675cdf653 --- /dev/null +++ b/apps/page/remission/src/lib/remission.component.scss @@ -0,0 +1,7 @@ +:host { + @apply block box-content; +} + +shell-breadcrumb { + @apply sticky top-0 left-0 right-0 z-sticky; +} diff --git a/apps/page/remission/src/lib/remission.component.spec.ts b/apps/page/remission/src/lib/remission.component.spec.ts new file mode 100644 index 000000000..6e63c5863 --- /dev/null +++ b/apps/page/remission/src/lib/remission.component.spec.ts @@ -0,0 +1,23 @@ +import { ActivatedRoute, Router } from '@angular/router'; +import { ApplicationService } from '@core/application'; +import { BreadcrumbService } from '@core/breadcrumb'; +import { Config } from '@core/config'; +import { createComponentFactory, Spectator } from '@ngneat/spectator'; + +import { RemissionComponent } from './remission.component'; + +describe('RemissionComponent', () => { + let spectator: Spectator; + const createComponent = createComponentFactory({ + component: RemissionComponent, + mocks: [BreadcrumbService, Config, ActivatedRoute, ApplicationService, Router], + }); + + beforeEach(() => { + spectator = createComponent(); + }); + + it('should create', () => { + expect(spectator.component).toBeTruthy(); + }); +}); diff --git a/apps/page/remission/src/lib/remission.component.ts b/apps/page/remission/src/lib/remission.component.ts new file mode 100644 index 000000000..78fa920f6 --- /dev/null +++ b/apps/page/remission/src/lib/remission.component.ts @@ -0,0 +1,98 @@ +import { Component, OnDestroy, OnInit } from '@angular/core'; +import { ActivatedRoute, NavigationEnd, Router } from '@angular/router'; +import { ApplicationService } from '@core/application'; +import { BreadcrumbService } from '@core/breadcrumb'; +import { Config } from '@core/config'; +import { UiFilterScanProvider } from '@ui/filter'; +import { Subject } from 'rxjs'; +import { filter, first, takeUntil } from 'rxjs/operators'; +import { AddProductScanProviderService, CreateRemissionScanProviderService, FinishShippingDocumentScanProviderService } from './providers'; + +@Component({ + selector: 'page-remission', + templateUrl: './remission.component.html', + styleUrls: ['./remission.component.scss'], + providers: [ + { + provide: UiFilterScanProvider, + useClass: AddProductScanProviderService, + multi: true, + }, + { + provide: UiFilterScanProvider, + useClass: CreateRemissionScanProviderService, + multi: true, + }, + { + provide: UiFilterScanProvider, + useClass: FinishShippingDocumentScanProviderService, + multi: true, + }, + ], +}) +export class RemissionComponent implements OnInit, OnDestroy { + private _onDestroy$ = new Subject(); + + get processId() { + return this._config.get('process.ids.remission'); + } + + constructor( + private readonly _breadcrumb: BreadcrumbService, + private readonly _config: Config, + private _activatedRoute: ActivatedRoute, + private _applicationService: ApplicationService, + private _router: Router + ) {} + + ngOnInit(): void { + this.addBreadcrumbIfNotExists(); + + this._router.events + ?.pipe( + filter((event) => event instanceof NavigationEnd), + takeUntil(this._onDestroy$) + ) + .subscribe(() => { + this.updateProcess(); + }); + + this.updateProcess(); + } + + ngOnDestroy(): void { + this._onDestroy$.next(); + this._onDestroy$.complete(); + } + + addBreadcrumbIfNotExists() { + this._breadcrumb.addBreadcrumbIfNotExists({ + key: this.processId, + name: 'Remission', + path: '/filiale/remission', + section: 'branch', + tags: ['remission'], + }); + } + + async updateProcess() { + const params = this._activatedRoute.snapshot.firstChild.params; + const process = await this._applicationService.getProcessById$(this.processId).pipe(first()).toPromise(); + + if (!!params?.returnId) { + if (process?.data?.active !== +params.returnId) { + this._applicationService.patchProcess(this.processId, { + closeable: false, + data: { active: +params.returnId }, + }); + } + } else if (!!process?.data?.active) { + this._router.navigate(['./', process?.data?.active, 'list'], { relativeTo: this._activatedRoute }); + } else { + this._applicationService.patchProcess(this.processId, { + closeable: true, + data: {}, + }); + } + } +} diff --git a/apps/page/remission/src/lib/remission.module.ts b/apps/page/remission/src/lib/remission.module.ts new file mode 100644 index 000000000..b4023f6be --- /dev/null +++ b/apps/page/remission/src/lib/remission.module.ts @@ -0,0 +1,11 @@ +import { NgModule } from '@angular/core'; +import { ShellBreadcrumbModule } from '@shell/breadcrumb'; +import { RemissionRoutingModule } from './remission-routing.module'; +import { RemissionComponent } from './remission.component'; + +@NgModule({ + declarations: [RemissionComponent], + imports: [RemissionRoutingModule, ShellBreadcrumbModule], + exports: [RemissionComponent], +}) +export class PageRemissionModule {} diff --git a/apps/page/remission/src/lib/required-capacities/required-capacities.component.html b/apps/page/remission/src/lib/required-capacities/required-capacities.component.html new file mode 100644 index 000000000..18576b28e --- /dev/null +++ b/apps/page/remission/src/lib/required-capacities/required-capacities.component.html @@ -0,0 +1,13 @@ + +
+
+
+
Leistungsplätze: {{ capacity }} von {{ maxCapacity }} Exemplaren
+ +
+
+
+
Stapelplätze: {{ staple }} von {{ maxStaple }} Titel
+ +
Wählen Sie die Abteilung aus, die Sie
remittieren möchten.
+
diff --git a/apps/page/remission/src/lib/required-capacities/required-capacities.component.scss b/apps/page/remission/src/lib/required-capacities/required-capacities.component.scss new file mode 100644 index 000000000..a7a8de8f9 --- /dev/null +++ b/apps/page/remission/src/lib/required-capacities/required-capacities.component.scss @@ -0,0 +1,22 @@ +:host { + @apply flex flex-col items-center; +} + +.progress-outer { + width: 214px; + margin: 10px 2%; + height: 12px; + background-color: #cfd4d8; + border: 1px solid #dcdcdc; + border-radius: 20px; + text-align: center; +} +.progress-inner { + min-width: 12px; + min-height: 12px; + white-space: nowrap; + overflow: hidden; + padding: 0px; + border-radius: 20px; + background-image: linear-gradient(to right, #83ae77, #26830c); +} diff --git a/apps/page/remission/src/lib/required-capacities/required-capacities.component.ts b/apps/page/remission/src/lib/required-capacities/required-capacities.component.ts new file mode 100644 index 000000000..2c9b94472 --- /dev/null +++ b/apps/page/remission/src/lib/required-capacities/required-capacities.component.ts @@ -0,0 +1,51 @@ +import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; +import { ValueTupleOfStringAndIntegerAndIntegerAndNullableIntegerAndString } from '@swagger/remi'; + +@Component({ + selector: 'page-required-capacities', + templateUrl: 'required-capacities.component.html', + styleUrls: ['required-capacities.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class RequiredCapacitiesComponent { + @Input() + requiredCapacities: ValueTupleOfStringAndIntegerAndIntegerAndNullableIntegerAndString[]; + + get capacity() { + return this.requiredCapacities?.find((_) => true)?.item3 || 0; + } + + get maxCapacity() { + return Math.max(this.capacity, this.requiredCapacities?.find((_) => true)?.item2); + } + + get capacityPercentage() { + if (this.maxCapacity > 0) { + return (this.capacity / this.maxCapacity) * 100; + } + return 0; + } + + get staple() { + if (this.requiredCapacities?.length > 0) { + return this.requiredCapacities[1].item3; + } + return 0; + } + + get maxStaple() { + if (this.requiredCapacities?.length > 0) { + return Math.max(this.staple, this.requiredCapacities[1].item2); + } + return 0; + } + + get staplePercentage() { + if (this.maxStaple > 0) { + return (this.staple / this.maxStaple) * 100; + } + return 0; + } + + constructor() {} +} diff --git a/apps/page/remission/src/lib/required-capacities/required-capacities.module.ts b/apps/page/remission/src/lib/required-capacities/required-capacities.module.ts new file mode 100644 index 000000000..213c7c7a7 --- /dev/null +++ b/apps/page/remission/src/lib/required-capacities/required-capacities.module.ts @@ -0,0 +1,12 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; + +import { RequiredCapacitiesComponent } from './required-capacities.component'; + +@NgModule({ + imports: [CommonModule], + exports: [RequiredCapacitiesComponent], + declarations: [RequiredCapacitiesComponent], + providers: [], +}) +export class RequiredCapacitiesModule {} diff --git a/apps/page/remission/src/lib/shared/shipping-document-details/shipping-document-details-item/shipping-document-details-item.component.html b/apps/page/remission/src/lib/shared/shipping-document-details/shipping-document-details-item/shipping-document-details-item.component.html new file mode 100644 index 000000000..d6cb5f76c --- /dev/null +++ b/apps/page/remission/src/lib/shared/shipping-document-details/shipping-document-details-item/shipping-document-details-item.component.html @@ -0,0 +1,32 @@ +
+
+ +
+ +
+
+

{{ title }}

+ {{ item.product.productGroup }}:{{ item.product.productGroup | productGroup }} +
+
+
Remi-Menge
+
{{ item.quantity }}x
+
+
+
Platz
+
Leistung
+ +
+ +
+
+
+
diff --git a/apps/page/remission/src/lib/shared/shipping-document-details/shipping-document-details-item/shipping-document-details-item.component.scss b/apps/page/remission/src/lib/shared/shipping-document-details/shipping-document-details-item/shipping-document-details-item.component.scss new file mode 100644 index 000000000..fc01d83c4 --- /dev/null +++ b/apps/page/remission/src/lib/shared/shipping-document-details/shipping-document-details-item/shipping-document-details-item.component.scss @@ -0,0 +1,3 @@ +.product-data-grid { + grid-template-columns: 60px 1fr; +} diff --git a/apps/page/remission/src/lib/shared/shipping-document-details/shipping-document-details-item/shipping-document-details-item.component.ts b/apps/page/remission/src/lib/shared/shipping-document-details/shipping-document-details-item/shipping-document-details-item.component.ts new file mode 100644 index 000000000..2d1a166a7 --- /dev/null +++ b/apps/page/remission/src/lib/shared/shipping-document-details/shipping-document-details-item/shipping-document-details-item.component.ts @@ -0,0 +1,28 @@ +import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { DomainRemissionService } from '@domain/remission'; +import { ReceiptItemDTO } from '@swagger/remi'; + +@Component({ + selector: 'page-shared-shipping-document-details-item', + templateUrl: 'shipping-document-details-item.component.html', + styleUrls: ['shipping-document-details-item.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class SharedShippingDocumentDetailsItemComponent implements OnInit { + @Input() + item: ReceiptItemDTO; + + @Input() + canRemoveItem: boolean = false; + + @Output() + removeItem = new EventEmitter(); + + get title() { + return [this.item.product.contributors, this.item.product.name].filter((items) => !!items).join(' - '); + } + + constructor(private _remissionService: DomainRemissionService) {} + + ngOnInit() {} +} diff --git a/apps/page/remission/src/lib/shared/shipping-document-details/shipping-document-details.component.html b/apps/page/remission/src/lib/shared/shipping-document-details/shipping-document-details.component.html new file mode 100644 index 000000000..f52407b2e --- /dev/null +++ b/apps/page/remission/src/lib/shared/shipping-document-details/shipping-document-details.component.html @@ -0,0 +1,72 @@ +
+
+ + +

+ Warenbegleitschein + #{{ firstReceipt.receiptNumber | shortReceiptNumber }} +

+ +
+
+ Status +
+
+ {{ completed ? 'Abgeschlossen' : 'Offen' }} +
+
+ Lieferant +
+
+ {{ return.supplier?.id | supplier }} +
+
+ +
+
+ Anzahl Positionen +
+
+ {{ itemsCount }} +
+
+ Remissionsdatum +
+
+ {{ return.created | date: 'dd.MM.YY' }} +
+
+ +
+
+ Wannnennummer +
+
+ {{ firstReceiptPackageNumber }} +
+
+
+
+ + +
+

Neue Artikel aus der Remi-Liste hinzufügen

+
+
+ + + + + diff --git a/apps/page/remission/src/lib/shared/shipping-document-details/shipping-document-details.component.scss b/apps/page/remission/src/lib/shared/shipping-document-details/shipping-document-details.component.scss new file mode 100644 index 000000000..067510820 --- /dev/null +++ b/apps/page/remission/src/lib/shared/shipping-document-details/shipping-document-details.component.scss @@ -0,0 +1,3 @@ +:host { + @apply grid grid-flow-row gap-px-2; +} diff --git a/apps/page/remission/src/lib/shared/shipping-document-details/shipping-document-details.component.ts b/apps/page/remission/src/lib/shared/shipping-document-details/shipping-document-details.component.ts new file mode 100644 index 000000000..d2c4604e1 --- /dev/null +++ b/apps/page/remission/src/lib/shared/shipping-document-details/shipping-document-details.component.ts @@ -0,0 +1,87 @@ +import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { DomainRemissionService } from '@domain/remission'; +import { ReceiptItemDTO, ReturnDTO } from '@swagger/remi'; +import { UiErrorModalComponent, UiMessageModalComponent, UiModalService } from '@ui/modal'; + +@Component({ + selector: 'page-shared-shipping-document-details', + templateUrl: 'shipping-document-details.component.html', + styleUrls: ['shipping-document-details.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class SharedShippingDocumentDetailsComponent implements OnInit { + @Input() + return: ReturnDTO; + + @Input() + canRemoveItems: boolean = false; + + @Input() + showAddToRemiHint: boolean = false; + + @Output() + deleted = new EventEmitter(); + + @Output() + itemDeleted = new EventEmitter(); + + get firstReceipt() { + return this.return.receipts?.find((_) => true)?.data; + } + + get completed() { + return !!this.return.completed; + } + + get itemsCount() { + return this.firstReceipt?.items?.length ?? 0; + } + + get firstReceiptPackageNumber() { + return this.firstReceipt?.packages?.find((_) => true)?.data?.packageNumber; + } + + constructor(private _remissionService: DomainRemissionService, private _modal: UiModalService) {} + + ngOnInit() {} + + async deleteReturn() { + if (this.itemsCount > 0) { + const modal = this._modal.open({ + content: UiMessageModalComponent, + title: 'Warenbegleitschein löschen', + data: { + message: + 'Mit Abbrechen der Remission werden alle Titel vom\nWarenbegleitschein entfernt und die Remission beendet. Entnehmen\nSie die Artikel auch physisch aus der Wanne.', + closeAction: 'Erledigt', + }, + }); + const result = await modal.afterClosed$.toPromise(); + if (!result.data) return; + } + + try { + await this._remissionService.deleteReturn(this.return.id); + this.deleted.emit(this.return); + } catch (err) { + this._modal.open({ content: UiErrorModalComponent, title: 'Fehler beim Löschen des Warenbegleitscheins', data: err }); + } + } + + async removeItem(item: ReceiptItemDTO) { + try { + await this._remissionService + .removeReturnItemFromReceipt({ + returnId: this.return.id, + receiptId: this.firstReceipt.id, + receiptItemId: item.id, + }) + .toPromise(); + + this.firstReceipt.items = this.firstReceipt.items.filter((i) => i.id !== item.id); + this.itemDeleted.emit(item); + } catch (err) { + this._modal.open({ content: UiErrorModalComponent, title: 'Fehler beim Löschen des Artikels', data: err }); + } + } +} diff --git a/apps/page/remission/src/lib/shared/shipping-document-details/shipping-document-details.module.ts b/apps/page/remission/src/lib/shared/shipping-document-details/shipping-document-details.module.ts new file mode 100644 index 000000000..21a2473ac --- /dev/null +++ b/apps/page/remission/src/lib/shared/shipping-document-details/shipping-document-details.module.ts @@ -0,0 +1,14 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { ProductImageModule } from '@cdn/product-image'; +import { RemissionPipeModule } from '../../pipes'; +import { SharedShippingDocumentDetailsItemComponent } from './shipping-document-details-item/shipping-document-details-item.component'; +import { SharedShippingDocumentDetailsComponent } from './shipping-document-details.component'; + +@NgModule({ + imports: [CommonModule, RemissionPipeModule, ProductImageModule], + exports: [SharedShippingDocumentDetailsComponent, SharedShippingDocumentDetailsItemComponent], + declarations: [SharedShippingDocumentDetailsComponent, SharedShippingDocumentDetailsItemComponent], + providers: [], +}) +export class SharedShippingDocumentDetailsModule {} diff --git a/apps/page/remission/src/lib/shipping-document-list/shipping-document-details/shipping-document-details.component.html b/apps/page/remission/src/lib/shipping-document-list/shipping-document-details/shipping-document-details.component.html new file mode 100644 index 000000000..b2e0d55dd --- /dev/null +++ b/apps/page/remission/src/lib/shipping-document-list/shipping-document-details/shipping-document-details.component.html @@ -0,0 +1,22 @@ + + + +

+ Bitte prüfen Sie, ob die Artikel in der Wanne
+ mit den Titeln auf dem Warenbegleitschein
+ identisch sind. +

+ + +
diff --git a/apps/page/remission/src/lib/shipping-document-list/shipping-document-details/shipping-document-details.component.scss b/apps/page/remission/src/lib/shipping-document-list/shipping-document-details/shipping-document-details.component.scss new file mode 100644 index 000000000..249c71ad1 --- /dev/null +++ b/apps/page/remission/src/lib/shipping-document-list/shipping-document-details/shipping-document-details.component.scss @@ -0,0 +1,3 @@ +:host { + @apply block; +} diff --git a/apps/page/remission/src/lib/shipping-document-list/shipping-document-details/shipping-document-details.component.ts b/apps/page/remission/src/lib/shipping-document-list/shipping-document-details/shipping-document-details.component.ts new file mode 100644 index 000000000..e14412729 --- /dev/null +++ b/apps/page/remission/src/lib/shipping-document-list/shipping-document-details/shipping-document-details.component.ts @@ -0,0 +1,80 @@ +import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { BreadcrumbService } from '@core/breadcrumb'; +import { Config } from '@core/config'; +import { DomainRemissionService } from '@domain/remission'; +import { ReturnDTO } from '@swagger/remi'; +import { NEVER, Observable } from 'rxjs'; +import { catchError, filter, map, shareReplay, switchMap, tap } from 'rxjs/operators'; +import { ShortReceiptNumberPipe } from '../../pipes/short-receipt-number.pipe'; + +@Component({ + selector: 'page-shipping-document-details', + templateUrl: 'shipping-document-details.component.html', + styleUrls: ['shipping-document-details.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, + providers: [ShortReceiptNumberPipe], +}) +export class ShippingDocumentDetailsComponent implements OnInit { + return$: Observable; + + notCompleted$: Observable; + + returnId$: Observable; + + hasItems$: Observable; + + constructor( + private _activatedRoute: ActivatedRoute, + private _remissionService: DomainRemissionService, + private _breadcrumb: BreadcrumbService, + private _config: Config, + private _shortReceiptNumberPipe: ShortReceiptNumberPipe, + private _router: Router + ) {} + + ngOnInit() { + this.return$ = this._activatedRoute.params.pipe( + map((params) => params?.id), + filter((id) => !!id), + switchMap((id) => + this._remissionService.getReturn(id).pipe( + catchError(() => { + this.navigateBack(); + return NEVER; + }) + ) + ), + tap((returnDto) => this.addOrUpdateBreadcrumbIfNotExists(returnDto)), + shareReplay(1) + ); + + this.notCompleted$ = this.return$.pipe(map((returnDto) => !returnDto.completed)); + + this.returnId$ = this.return$.pipe(map((returnDto) => returnDto?.id)); + + this.hasItems$ = this.return$.pipe(map((returnDto) => returnDto?.receipts?.find((_) => true)?.data?.items?.length > 0)); + } + + navigateBack() { + this._router.navigate(['..'], { relativeTo: this._activatedRoute }); + } + + async addOrUpdateBreadcrumbIfNotExists(returnDto: ReturnDTO) { + const packageNumber = returnDto?.receipts?.find((_) => true)?.data?.receiptNumber; + if (packageNumber) { + const shortPackageNumber = this._shortReceiptNumberPipe.transform(packageNumber); + this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({ + key: this._config.get('process.ids.remission'), + name: `#${shortPackageNumber}`, + path: `/filiale/remission/shipping-documents/${returnDto.id}`, + section: 'branch', + tags: ['remission', 'shipping-documents', 'details'], + }); + } + } + + deleted() { + this.navigateBack(); + } +} diff --git a/apps/page/remission/src/lib/shipping-document-list/shipping-document-list-item/shipping-document-list-item.component.html b/apps/page/remission/src/lib/shipping-document-list/shipping-document-list-item/shipping-document-list-item.component.html new file mode 100644 index 000000000..d46f88236 --- /dev/null +++ b/apps/page/remission/src/lib/shipping-document-list/shipping-document-list-item/shipping-document-list-item.component.html @@ -0,0 +1,45 @@ +
+

+
+ #{{ firstReceipt.receiptNumber | shortReceiptNumber }} +

+ +
+
+ Status +
+
+ {{ completed ? 'Abgeschlossen' : 'Offen' }} +
+
+ Lieferant +
+
+ {{ return.supplier?.id | supplier }} +
+
+ +
+
+ Anzahl Positionen +
+
+ {{ itemsCount }} +
+
+ Remissionsdatum +
+
+ {{ return.created | date: 'dd.MM.YY' }} +
+
+ +
+
+ Wannnennummer +
+
+ {{ firstReceiptPackageNumber }} +
+
+
diff --git a/apps/page/remission/src/lib/shipping-document-list/shipping-document-list-item/shipping-document-list-item.component.scss b/apps/page/remission/src/lib/shipping-document-list/shipping-document-list-item/shipping-document-list-item.component.scss new file mode 100644 index 000000000..f4402e8e3 --- /dev/null +++ b/apps/page/remission/src/lib/shipping-document-list/shipping-document-list-item/shipping-document-list-item.component.scss @@ -0,0 +1,3 @@ +:host { + @apply block bg-white rounded-card p-4; +} diff --git a/apps/page/remission/src/lib/shipping-document-list/shipping-document-list-item/shipping-document-list-item.component.ts b/apps/page/remission/src/lib/shipping-document-list/shipping-document-list-item/shipping-document-list-item.component.ts new file mode 100644 index 000000000..ab97ae044 --- /dev/null +++ b/apps/page/remission/src/lib/shipping-document-list/shipping-document-list-item/shipping-document-list-item.component.ts @@ -0,0 +1,33 @@ +import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; +import { ReturnDTO } from '@swagger/remi'; + +@Component({ + selector: 'page-shipping-document-list-item', + templateUrl: 'shipping-document-list-item.component.html', + styleUrls: ['shipping-document-list-item.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ShippingDocumentListItemComponent implements OnInit { + @Input() + return: ReturnDTO; + + get firstReceipt() { + return this.return.receipts?.find((_) => true)?.data; + } + + get completed() { + return !!this.return.completed; + } + + get itemsCount() { + return this.firstReceipt?.items?.length ?? 0; + } + + get firstReceiptPackageNumber() { + return this.firstReceipt?.packages?.find((_) => true)?.data?.packageNumber; + } + + constructor() {} + + ngOnInit() {} +} diff --git a/apps/page/remission/src/lib/shipping-document-list/shipping-document-list.component.html b/apps/page/remission/src/lib/shipping-document-list/shipping-document-list.component.html new file mode 100644 index 000000000..492345d12 --- /dev/null +++ b/apps/page/remission/src/lib/shipping-document-list/shipping-document-list.component.html @@ -0,0 +1,7 @@ +

Warenbegleitscheine

+ + diff --git a/apps/page/remission/src/lib/shipping-document-list/shipping-document-list.component.scss b/apps/page/remission/src/lib/shipping-document-list/shipping-document-list.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/apps/page/remission/src/lib/shipping-document-list/shipping-document-list.component.ts b/apps/page/remission/src/lib/shipping-document-list/shipping-document-list.component.ts new file mode 100644 index 000000000..6c0a40658 --- /dev/null +++ b/apps/page/remission/src/lib/shipping-document-list/shipping-document-list.component.ts @@ -0,0 +1,42 @@ +import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; +import { BreadcrumbService } from '@core/breadcrumb'; +import { Config } from '@core/config'; +import { DomainRemissionService } from '@domain/remission'; +import { ReturnDTO } from '@swagger/remi'; +import { Observable } from 'rxjs'; + +@Component({ + selector: 'page-shipping-document-list', + templateUrl: 'shipping-document-list.component.html', + styleUrls: ['shipping-document-list.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ShippingDocumentListComponent implements OnInit { + returns$: Observable; + + constructor(private _remissionService: DomainRemissionService, private _breadcrumb: BreadcrumbService, private _config: Config) {} + + ngOnInit() { + this.addBreadcrumbIfNotExists(); + this.removeDetailBreadcrumbs(); + this.returns$ = this._remissionService.getReturns(); + } + + addBreadcrumbIfNotExists() { + this._breadcrumb.addBreadcrumbIfNotExists({ + key: this._config.get('process.ids.remission'), + name: 'Offene Warenbegleitscheine', + path: '/filiale/remission/shipping-documents', + section: 'branch', + tags: ['remission', 'shipping-documents'], + }); + } + + removeDetailBreadcrumbs() { + this._breadcrumb.removeBreadcrumbsByKeyAndTags(this._config.get('process.ids.remission'), [ + 'remission', + 'shipping-documents', + 'details', + ]); + } +} diff --git a/apps/page/remission/src/lib/shipping-document-list/shipping-document-list.module.ts b/apps/page/remission/src/lib/shipping-document-list/shipping-document-list.module.ts new file mode 100644 index 000000000..96d179ee1 --- /dev/null +++ b/apps/page/remission/src/lib/shipping-document-list/shipping-document-list.module.ts @@ -0,0 +1,16 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { RemissionPipeModule } from '../pipes'; +import { ShippingDocumentListItemComponent } from './shipping-document-list-item/shipping-document-list-item.component'; +import { ShippingDocumentListComponent } from './shipping-document-list.component'; +import { SharedShippingDocumentDetailsModule } from '../shared/shipping-document-details/shipping-document-details.module'; +import { ShippingDocumentDetailsComponent } from './shipping-document-details/shipping-document-details.component'; +import { RouterModule } from '@angular/router'; + +@NgModule({ + imports: [CommonModule, RemissionPipeModule, SharedShippingDocumentDetailsModule, RouterModule], + exports: [], + declarations: [ShippingDocumentListComponent, ShippingDocumentListItemComponent, ShippingDocumentDetailsComponent], + providers: [], +}) +export class ShippingDocumentListModule {} diff --git a/apps/page/remission/src/public-api.ts b/apps/page/remission/src/public-api.ts new file mode 100644 index 000000000..2a829a4e9 --- /dev/null +++ b/apps/page/remission/src/public-api.ts @@ -0,0 +1,5 @@ +/* + * Public API Surface of remission + */ + +export * from './lib'; diff --git a/apps/page/remission/src/test.ts b/apps/page/remission/src/test.ts new file mode 100644 index 000000000..b22c5d4af --- /dev/null +++ b/apps/page/remission/src/test.ts @@ -0,0 +1,25 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import 'zone.js'; +import 'zone.js/testing'; +import { getTestBed } from '@angular/core/testing'; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; + +declare const require: { + context( + path: string, + deep?: boolean, + filter?: RegExp + ): { + keys(): string[]; + (id: string): T; + }; +}; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), { teardown: { destroyAfterEach: true } }); + +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().map(context); diff --git a/apps/page/remission/tsconfig.lib.json b/apps/page/remission/tsconfig.lib.json new file mode 100644 index 000000000..a7141c543 --- /dev/null +++ b/apps/page/remission/tsconfig.lib.json @@ -0,0 +1,20 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "outDir": "../../../out-tsc/lib", + "target": "es2015", + "declaration": true, + "declarationMap": true, + "inlineSources": true, + "types": [], + "lib": [ + "dom", + "es2018" + ] + }, + "exclude": [ + "src/test.ts", + "**/*.spec.ts" + ] +} diff --git a/apps/page/remission/tsconfig.lib.prod.json b/apps/page/remission/tsconfig.lib.prod.json new file mode 100644 index 000000000..06de549e1 --- /dev/null +++ b/apps/page/remission/tsconfig.lib.prod.json @@ -0,0 +1,10 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.lib.json", + "compilerOptions": { + "declarationMap": false + }, + "angularCompilerOptions": { + "compilationMode": "partial" + } +} diff --git a/apps/page/remission/tsconfig.spec.json b/apps/page/remission/tsconfig.spec.json new file mode 100644 index 000000000..85392ee8f --- /dev/null +++ b/apps/page/remission/tsconfig.spec.json @@ -0,0 +1,17 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "outDir": "../../../out-tsc/spec", + "types": [ + "jasmine" + ] + }, + "files": [ + "src/test.ts" + ], + "include": [ + "**/*.spec.ts", + "**/*.d.ts" + ] +} diff --git a/apps/page/task-calendar/src/lib/components/task-info/task-info.component.ts b/apps/page/task-calendar/src/lib/components/task-info/task-info.component.ts index f525d4818..3508830ea 100644 --- a/apps/page/task-calendar/src/lib/components/task-info/task-info.component.ts +++ b/apps/page/task-calendar/src/lib/components/task-info/task-info.component.ts @@ -21,6 +21,7 @@ import { WebViewerModalComponent } from '../../modals/web-viewer/web-viewer-moda templateUrl: 'task-info.component.html', styleUrls: ['task-info.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, + providers: [DatePipe], }) export class TaskInfoComponent implements OnChanges { @Input() diff --git a/apps/page/task-calendar/src/lib/components/task-list/task-list.component.scss b/apps/page/task-calendar/src/lib/components/task-list/task-list.component.scss index cc9e4d2ad..fea004b68 100644 --- a/apps/page/task-calendar/src/lib/components/task-list/task-list.component.scss +++ b/apps/page/task-calendar/src/lib/components/task-list/task-list.component.scss @@ -25,7 +25,7 @@ } h4 { - @apply m-0; + @apply m-0 font-bold; } .muted { diff --git a/apps/page/task-calendar/src/lib/components/task-list/task-list.component.ts b/apps/page/task-calendar/src/lib/components/task-list/task-list.component.ts index 269b33299..502d3af8c 100644 --- a/apps/page/task-calendar/src/lib/components/task-list/task-list.component.ts +++ b/apps/page/task-calendar/src/lib/components/task-list/task-list.component.ts @@ -14,6 +14,7 @@ import { first, map, withLatestFrom } from 'rxjs/operators'; templateUrl: 'task-list.component.html', styleUrls: ['task-list.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, + providers: [DatePipe], }) export class TaskListComponent { today = this.dateAdapter.today(); diff --git a/apps/page/task-calendar/src/lib/containers/task-calendar-filter/task-calendar-filter.component.html b/apps/page/task-calendar/src/lib/containers/task-calendar-filter/task-calendar-filter.component.html index 709936de9..dd4df4f2d 100644 --- a/apps/page/task-calendar/src/lib/containers/task-calendar-filter/task-calendar-filter.component.html +++ b/apps/page/task-calendar/src/lib/containers/task-calendar-filter/task-calendar-filter.component.html @@ -1,22 +1,28 @@ - - - - - -
- + +

+ Filter +

+ + + + +
+ +
+
diff --git a/apps/page/task-calendar/src/lib/containers/task-calendar-filter/task-calendar-filter.component.scss b/apps/page/task-calendar/src/lib/containers/task-calendar-filter/task-calendar-filter.component.scss index a465cc407..3cfc1139b 100644 --- a/apps/page/task-calendar/src/lib/containers/task-calendar-filter/task-calendar-filter.component.scss +++ b/apps/page/task-calendar/src/lib/containers/task-calendar-filter/task-calendar-filter.component.scss @@ -1,10 +1,18 @@ :host { - @apply flex flex-col box-border w-full; + @apply block box-border w-full; +} - ui-filter-group ::ng-deep .select-filter-options { - overflow: scroll; - max-height: 400px; - } +.filter-content { + @apply relative mx-auto p-4; + max-width: 916px; +} + +.filter-header { + @apply text-3xl font-bold text-center py-4; +} + +.btn-close { + @apply absolute text-cool-grey top-3 p-4 right-4 outline-none border-none bg-transparent; } .sticky-cta-wrapper { diff --git a/apps/page/task-calendar/src/lib/containers/task-calendar-filter/task-calendar-filter.component.ts b/apps/page/task-calendar/src/lib/containers/task-calendar-filter/task-calendar-filter.component.ts index cca9b6e19..03d43d9ab 100644 --- a/apps/page/task-calendar/src/lib/containers/task-calendar-filter/task-calendar-filter.component.ts +++ b/apps/page/task-calendar/src/lib/containers/task-calendar-filter/task-calendar-filter.component.ts @@ -1,6 +1,5 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, OnDestroy, OnInit, Output } from '@angular/core'; -import { Filter } from '@ui/filter'; -import { first } from 'rxjs/operators'; +import { ChangeDetectionStrategy, Component, EventEmitter, Output } from '@angular/core'; +import { UiFilter } from '@ui/filter'; import { TaskCalendarStore } from '../../task-calendar.store'; @Component({ @@ -9,40 +8,20 @@ import { TaskCalendarStore } from '../../task-calendar.store'; styleUrls: ['task-calendar-filter.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class TaskCalendarFilterComponent implements OnInit, OnDestroy { +export class TaskCalendarFilterComponent { @Output() exitFilter = new EventEmitter(); - selectedFilters: Filter[]; - initialFilter: Filter[]; - updatedFilter: Filter[]; - readonly initialFilters$ = this.taskCalendarStore.selectInitialFilter; - readonly filters$ = this.taskCalendarStore.selectFilter; - readonly displayInfos$ = this.taskCalendarStore.selectDisplayInfos; + filter$ = this.taskCalendarStore.selectFilter; - constructor(private cdr: ChangeDetectorRef, private taskCalendarStore: TaskCalendarStore) {} + fetching$ = this.taskCalendarStore.selectFetching; - async ngOnInit() { - this.initialFilter = await this.initialFilters$.pipe(first()).toPromise(); - const filters = await this.filters$.pipe(first()).toPromise(); - this.selectedFilters = filters; - this.updatedFilter = filters; - this.cdr.markForCheck(); - } + message$ = this.taskCalendarStore.selectMessage; - ngOnDestroy() { - const filters = this.updatedFilter; + constructor(private taskCalendarStore: TaskCalendarStore) {} + + applyFilter(filters: UiFilter) { this.taskCalendarStore.setFilter({ filters }); - } - - applyFilters() { this.taskCalendarStore.loadItems(); - this.updatedFilter = this.selectedFilters; this.exitFilter.emit(); } - - updateFilter(filters: Filter[]) { - this.taskCalendarStore.setFilter({ filters }); - this.selectedFilters = filters; - this.cdr.markForCheck(); - } } diff --git a/apps/page/task-calendar/src/lib/modals/article-list/article-list-modal.component.ts b/apps/page/task-calendar/src/lib/modals/article-list/article-list-modal.component.ts index 7d808e5b8..fc66f7388 100644 --- a/apps/page/task-calendar/src/lib/modals/article-list/article-list-modal.component.ts +++ b/apps/page/task-calendar/src/lib/modals/article-list/article-list-modal.component.ts @@ -57,6 +57,6 @@ export class ArticleListModalComponent { const articles = await this.articles$.toPromise(); const taskCalendarSearch: string = articles.map((article: ArticleDTO) => article.ean).join(';'); this.modalRef.close('closeAll'); - this.router.navigate(['/product', 'search', 'results'], { queryParams: { main_qs: taskCalendarSearch } }); + this.router.navigate(['/kunde', 'product', 'search', 'results'], { queryParams: { main_qs: taskCalendarSearch } }); } } diff --git a/apps/page/task-calendar/src/lib/modals/task/task-modal.component.ts b/apps/page/task-calendar/src/lib/modals/task/task-modal.component.ts index e92e85742..2fba6191d 100644 --- a/apps/page/task-calendar/src/lib/modals/task/task-modal.component.ts +++ b/apps/page/task-calendar/src/lib/modals/task/task-modal.component.ts @@ -170,8 +170,8 @@ export class TaskModalComponent { this.uiModal.open({ content: UiMessageModalComponent, + title: 'Fotonachweis - bitte wechseln Sie auf ein mobiles Gerät', data: { - title: 'Fotonachweis - bitte wechseln Sie auf ein mobiles Gerät', message: 'Wechseln Sie auf ein mobiles Gerät, um die Aufgabe mit Fotonachweis bearbeiten zu können.', closeAction: 'Ok', }, diff --git a/apps/page/task-calendar/src/lib/page-task-calendar.component.html b/apps/page/task-calendar/src/lib/page-task-calendar.component.html index 6f5aed35b..3792fad37 100644 --- a/apps/page/task-calendar/src/lib/page-task-calendar.component.html +++ b/apps/page/task-calendar/src/lib/page-task-calendar.component.html @@ -1,11 +1,11 @@ - + - -
+
-
-
-
- -
-

- Filter -

- -
-
+ + + + diff --git a/apps/page/task-calendar/src/lib/page-task-calendar.component.scss b/apps/page/task-calendar/src/lib/page-task-calendar.component.scss index b3880780d..787b90ad0 100644 --- a/apps/page/task-calendar/src/lib/page-task-calendar.component.scss +++ b/apps/page/task-calendar/src/lib/page-task-calendar.component.scss @@ -1,10 +1,13 @@ +:host { + @apply flex flex-col w-full box-content relative; +} + shell-breadcrumb { - margin-top: -5px; - @apply mb-px-10 pb-px-10; + @apply sticky z-sticky top-0 py-4; } .filter { - @apply absolute font-sans flex items-center font-bold bg-gray-400 border-0 text-regular py-px-8 px-px-15 rounded-filter justify-center; + @apply absolute font-sans flex items-center font-bold bg-gray-400 border-0 text-regular -top-12 right-0 py-px-8 px-px-15 rounded-filter justify-center z-sticky; right: 0; top: 10px; @@ -19,37 +22,6 @@ shell-breadcrumb { } } -.filter-content { - @apply max-w-content mx-auto mt-px-25 px-px-15; - - .filter-close-right { - @apply pr-px-10 text-right; - } - - .filter-header { - @apply text-center text-page-heading mt-0; - } - - button.filter-close { - @apply border-0 bg-transparent text-ucla-blue; - } -} - -.filter-overlay { - @apply fixed bg-munsell z-fixed; - - transform: translatex(100%); - transition: transform 0.5s ease-out; - top: 135px; - right: 0; - bottom: 0; - left: 0; - - &.active { - transform: translatex(0%); - } -} - .content-container { max-height: calc(100vh - 267px); overflow: scroll; diff --git a/apps/page/task-calendar/src/lib/page-task-calendar.component.ts b/apps/page/task-calendar/src/lib/page-task-calendar.component.ts index 31e5b5f55..ab1f5a596 100644 --- a/apps/page/task-calendar/src/lib/page-task-calendar.component.ts +++ b/apps/page/task-calendar/src/lib/page-task-calendar.component.ts @@ -1,8 +1,8 @@ -import { Component, OnInit } from '@angular/core'; -import { BreadcrumbService } from '@core/breadcrumb'; -import { isSelectFilter } from 'apps/ui/filter/src/lib/type-guards'; -import { BehaviorSubject, Observable, of } from 'rxjs'; -import { delay, map, switchMap } from 'rxjs/operators'; +import { Component } from '@angular/core'; +import { Config } from '@core/config'; +import { isEqual } from 'lodash'; +import { BehaviorSubject, combineLatest } from 'rxjs'; +import { map, shareReplay } from 'rxjs/operators'; import { TaskCalendarStore } from './task-calendar.store'; @Component({ @@ -11,35 +11,22 @@ import { TaskCalendarStore } from './task-calendar.store'; styleUrls: ['page-task-calendar.component.scss'], providers: [TaskCalendarStore], }) -export class PageTaskCalendarComponent implements OnInit { +export class PageTaskCalendarComponent { filterActive$ = new BehaviorSubject(false); - showMainContent$ = this.getShowMainContent(); + taskCalendarKey = this._config.get('process.ids.taskCalendar'); - hasFilter$: Observable = this.taskCalendarStore.selectFilter.pipe( - map((filter) => { - for (const f of filter) { - if (isSelectFilter(f)) { - return !!f.options.find((filterOption) => filterOption.selected); - } - } - }) + hasFilter$ = combineLatest([ + this.taskCalendarStore.selectFetching, + this.taskCalendarStore.selectFilter, + this.taskCalendarStore.selectInitialFilter, + ]).pipe( + map(([_, filter, initialFilter]) => { + return !isEqual(filter?.getQueryParams(), initialFilter?.getQueryParams()); + }), + shareReplay() ); - constructor(private taskCalendarStore: TaskCalendarStore) { + constructor(private taskCalendarStore: TaskCalendarStore, private readonly _config: Config) { this.taskCalendarStore.loadFilter(); } - - ngOnInit(): void {} - - getShowMainContent(animationDelayInMs: number = 500): Observable { - return this.filterActive$.pipe( - switchMap((filterActive) => { - const onExitMainContent = filterActive; - if (onExitMainContent) { - return of(!filterActive).pipe(delay(animationDelayInMs)); - } - return of(!filterActive); - }) - ); - } } diff --git a/apps/page/task-calendar/src/lib/page-task-calendar.module.ts b/apps/page/task-calendar/src/lib/page-task-calendar.module.ts index 4dce42ef9..a67eb5ce5 100644 --- a/apps/page/task-calendar/src/lib/page-task-calendar.module.ts +++ b/apps/page/task-calendar/src/lib/page-task-calendar.module.ts @@ -1,7 +1,8 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { ShellBreadcrumbModule } from '@shell/breadcrumb'; -import { UiFilterModule } from '@ui/filter'; +import { ShellFilterOverlayModule } from '@shell/filter-overlay'; +import { UiFilterNextModule } from '@ui/filter'; import { UiIconModule } from '@ui/icon'; import { TaskCalendarFilterComponent } from './containers/task-calendar-filter/task-calendar-filter.component'; import { ModalsModule } from './modals/modals.module'; @@ -10,7 +11,15 @@ import { PageTaskCalendarComponent } from './page-task-calendar.component'; @NgModule({ declarations: [PageTaskCalendarComponent, TaskCalendarFilterComponent], - imports: [CommonModule, PageTaskCalendarRoutingModule, ShellBreadcrumbModule, UiIconModule, ModalsModule, UiFilterModule], + imports: [ + CommonModule, + PageTaskCalendarRoutingModule, + ShellBreadcrumbModule, + UiIconModule, + ModalsModule, + UiFilterNextModule, + ShellFilterOverlayModule, + ], exports: [PageTaskCalendarComponent], }) export class PageTaskCalendarModule {} diff --git a/apps/page/task-calendar/src/lib/pages/calendar/calendar.component.ts b/apps/page/task-calendar/src/lib/pages/calendar/calendar.component.ts index 98a73c10f..695fd3411 100644 --- a/apps/page/task-calendar/src/lib/pages/calendar/calendar.component.ts +++ b/apps/page/task-calendar/src/lib/pages/calendar/calendar.component.ts @@ -1,6 +1,7 @@ import { Component, ChangeDetectionStrategy, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { BreadcrumbService } from '@core/breadcrumb'; +import { Config } from '@core/config'; import { DateAdapter } from '@ui/common'; import { TaskCalendarStore } from '../../task-calendar.store'; @@ -26,7 +27,8 @@ export class CalendarComponent implements OnInit { private activatedRoute: ActivatedRoute, private router: Router, private dateAdapter: DateAdapter, - private breadcrumb: BreadcrumbService + private breadcrumb: BreadcrumbService, + private readonly _config: Config ) {} ngOnInit() { @@ -61,9 +63,9 @@ export class CalendarComponent implements OnInit { updateBreadcrumb(date?: Date) { this.breadcrumb.addOrUpdateBreadcrumbIfNotExists({ - key: 'task-calendar', + key: this._config.get('process.ids.taskCalendar'), name: 'Tätigkeitskalender', - path: '/task-calendar/calendar', + path: '/filiale/task-calendar/calendar', tags: ['task-calendar'], section: 'branch', params: { diff --git a/apps/page/task-calendar/src/lib/pages/tasks/tasks.component.ts b/apps/page/task-calendar/src/lib/pages/tasks/tasks.component.ts index 65c1983d8..740024ef2 100644 --- a/apps/page/task-calendar/src/lib/pages/tasks/tasks.component.ts +++ b/apps/page/task-calendar/src/lib/pages/tasks/tasks.component.ts @@ -1,6 +1,7 @@ import { Component, ChangeDetectionStrategy, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { BreadcrumbService } from '@core/breadcrumb'; +import { Config } from '@core/config'; import { DisplayInfoDTO } from '@swagger/eis'; import { DateAdapter } from '@ui/common'; import { TaskCalendarStore } from '../../task-calendar.store'; @@ -29,7 +30,8 @@ export class TasksComponent implements OnInit { private router: Router, private activatedRoute: ActivatedRoute, private dateAdapter: DateAdapter, - private breadcrumb: BreadcrumbService + private breadcrumb: BreadcrumbService, + private readonly _config: Config ) {} ngOnInit() { @@ -87,9 +89,9 @@ export class TasksComponent implements OnInit { updateBreadcrumb({ displayDate, selectedDate }: { displayDate?: Date; selectedDate?: Date }) { const queryParams = this.activatedRoute.snapshot.queryParams; this.breadcrumb.addOrUpdateBreadcrumbIfNotExists({ - key: 'task-calendar', + key: this._config.get('process.ids.taskCalendar'), name: 'Tätigkeitskalender', - path: '/task-calendar/tasks', + path: '/filiale/task-calendar/tasks', tags: ['task-calendar'], section: 'branch', params: { diff --git a/apps/page/task-calendar/src/lib/task-calendar.store.ts b/apps/page/task-calendar/src/lib/task-calendar.store.ts index b8134d76f..76375f3fa 100644 --- a/apps/page/task-calendar/src/lib/task-calendar.store.ts +++ b/apps/page/task-calendar/src/lib/task-calendar.store.ts @@ -1,14 +1,14 @@ import { Injectable } from '@angular/core'; import { DomainTaskCalendarService } from '@domain/task-calendar'; import { ComponentStore, tapResponse } from '@ngrx/component-store'; -import { DisplayInfoDTO, InputDTO, ResponseArgsOfIEnumerableOfInputDTO } from '@swagger/eis'; +import { DisplayInfoDTO, InputDTO, QuerySettingsDTO, ResponseArgsOfIEnumerableOfInputDTO } from '@swagger/eis'; import { CalendarIndicator } from '@ui/calendar'; import { DateAdapter } from '@ui/common'; -import { Filter, FilterOption, SelectFilter, UiFilterMappingService } from '@ui/filter'; +import { Filter, FilterOption, SelectFilter, UiFilter, UiFilterMappingService } from '@ui/filter'; import { UiMessageModalComponent, UiModalRef, UiModalResult, UiModalService } from '@ui/modal'; import { clone } from 'lodash'; import { Observable } from 'rxjs'; -import { debounceTime, map, switchMap, withLatestFrom } from 'rxjs/operators'; +import { debounceTime, map, switchMap, tap, withLatestFrom } from 'rxjs/operators'; import { InfoModalComponent } from './modals/info/info-modal.component'; import { PreInfoModalComponent } from './modals/preinfo/preinfo-modal.component'; import { TaskModalComponent } from './modals/task/task-modal.component'; @@ -18,8 +18,10 @@ export interface TaskCalendarState { selectedDate: Date; displayedDate: Date; displayInfos: DisplayInfoDTO[]; - initialFilter: Filter[]; - filter: Filter[]; + initialFilter: UiFilter; + filter: UiFilter; + message: string; + fetching: boolean; } @Injectable() @@ -57,6 +59,10 @@ export class TaskCalendarStore extends ComponentStore { readonly selectFilter = this.select((s) => s.filter); + readonly selectFetching = this.select((s) => s.fetching); + + readonly selectMessage = this.select((s) => s.message); + readonly selectStartStop = this.select((s) => { const { mode, displayedDate } = s; @@ -93,8 +99,10 @@ export class TaskCalendarStore extends ComponentStore { selectedDate: dateAdapter.today(), displayedDate: dateAdapter.today(), displayInfos: [], - initialFilter: [], - filter: [], + initialFilter: undefined, + filter: undefined, + message: undefined, + fetching: false, }); } @@ -108,7 +116,7 @@ export class TaskCalendarStore extends ComponentStore { displayedDate: date, })); - readonly setFilter = this.updater((s, { filters }: { filters: Filter[] }) => ({ + readonly setFilter = this.updater((s, { filters }: { filters: UiFilter }) => ({ ...s, filter: filters, })); @@ -120,15 +128,18 @@ export class TaskCalendarStore extends ComponentStore { readonly loadItems = this.effect(($: Observable) => $.pipe( + tap(() => this.patchState({ fetching: true })), debounceTime(500), withLatestFrom(this.domainTaskCalendarService.currentBranchId$, this.selectStartStop, this.selectFilter), - switchMap(([_, branchId, date, filter]) => - this.domainTaskCalendarService + switchMap(([_, branchId, date, filter]) => { + const querytoken = { ...filter?.getQueryToken() }; + return this.domainTaskCalendarService .getInfos({ + ...querytoken, filter: { + ...querytoken?.filter, branch_id: String(branchId), timespan: `"${date?.start?.toISOString()}"-"${date.stop?.toISOString()}"`, - ...this.mapFilterArrayToStringDictionary(filter), }, }) .pipe( @@ -142,36 +153,35 @@ export class TaskCalendarStore extends ComponentStore { response.result.push(preInfoTask); }); - this.patchState({ displayInfos: response.result }); + this.patchState({ displayInfos: response.result, fetching: false, message: undefined }); } else { this.uiModal.open({ content: UiMessageModalComponent, data: response, }); - this.patchState({ displayInfos: [] }); + this.patchState({ displayInfos: [], fetching: false, message: 'Keine Suchergebnisse' }); } }, (error) => { console.error(error); - this.patchState({ displayInfos: [] }); + this.patchState({ displayInfos: [], fetching: false, message: 'Keine Suchergebnisse' }); } ) - ) - ) + ); + }) ) ); readonly loadFilter = this.effect(($: Observable) => $.pipe( - switchMap((_) => this.domainTaskCalendarService.getFilters()), + switchMap((_) => this.domainTaskCalendarService.getSettings()), tapResponse( - (response: ResponseArgsOfIEnumerableOfInputDTO) => { - const filter = this.mapInputArrayToFilterArray(response.result as InputDTO[]); - this.patchState({ filter, initialFilter: filter }); + (response: QuerySettingsDTO) => { + this.patchState({ filter: UiFilter.create(response), initialFilter: UiFilter.create(response) }); }, (error) => { console.error(error); - this.patchState({ filter: [], initialFilter: [] }); + this.patchState({ filter: undefined, initialFilter: undefined }); } ) ) diff --git a/apps/page/task-calendar/src/test.ts b/apps/page/task-calendar/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/page/task-calendar/src/test.ts +++ b/apps/page/task-calendar/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/sales/src/app/app-routing.module.ts b/apps/sales/src/app/app-routing.module.ts index 1c92e187f..5dfa28a21 100644 --- a/apps/sales/src/app/app-routing.module.ts +++ b/apps/sales/src/app/app-routing.module.ts @@ -86,6 +86,7 @@ const routes: Routes = [ RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules, scrollPositionRestoration: 'enabled', + relativeLinkResolution: 'legacy', }), ], exports: [RouterModule], diff --git a/apps/sales/src/app/app-store.module.ts b/apps/sales/src/app/app-store.module.ts index e13c63d71..6eefd5f8d 100644 --- a/apps/sales/src/app/app-store.module.ts +++ b/apps/sales/src/app/app-store.module.ts @@ -9,7 +9,7 @@ import { EffectsModule } from '@ngrx/effects'; import { SearchEffects } from './store/customer'; import { HistoryEffects } from '@shelf-store/history'; import { DetailsEffects } from '@shelf-store/details'; -import { version } from 'package'; +import packageInfo from 'package'; // TODO: In Service Speichern export function storeInLocalStorage(reducer: ActionReducer): ActionReducer { @@ -18,12 +18,12 @@ export function storeInLocalStorage(reducer: ActionReducer): ActionReducer< return function (state, action) { if (action.type === INIT) { const storedState = JSON.parse(localStorage.getItem(lsKey)); - if (storedState?.version === version) { + if (storedState?.version === packageInfo.version) { return reducer(storedState, action); } } const nextState = reducer(state, action); - localStorage.setItem(lsKey, JSON.stringify({ ...nextState, version })); + localStorage.setItem(lsKey, JSON.stringify({ ...nextState, version: packageInfo.version })); return nextState; }; } diff --git a/apps/sales/src/app/app.component.spec.ts b/apps/sales/src/app/app.component.spec.ts index d06e6a350..311e5443f 100644 --- a/apps/sales/src/app/app.component.spec.ts +++ b/apps/sales/src/app/app.component.spec.ts @@ -1,12 +1,14 @@ -import { TestBed, async } from '@angular/core/testing'; +import { TestBed, waitForAsync } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; import { AppComponent } from './app.component'; describe('AppComponent', () => { - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [RouterTestingModule], - declarations: [AppComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [RouterTestingModule], + declarations: [AppComponent], + }).compileComponents(); + }) + ); }); diff --git a/apps/sales/src/app/components/breadcrumbs/breadcrumbs.component.spec.ts b/apps/sales/src/app/components/breadcrumbs/breadcrumbs.component.spec.ts index 19886309f..2ff6e166e 100644 --- a/apps/sales/src/app/components/breadcrumbs/breadcrumbs.component.spec.ts +++ b/apps/sales/src/app/components/breadcrumbs/breadcrumbs.component.spec.ts @@ -1,11 +1,13 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { BreadcrumbsComponent } from './breadcrumbs.component'; describe('BreadcrumbsComponent', () => { - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [BreadcrumbsComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [BreadcrumbsComponent], + }).compileComponents(); + }) + ); }); diff --git a/apps/sales/src/app/components/header/header.component.spec.ts b/apps/sales/src/app/components/header/header.component.spec.ts index 466e46986..33cbd87a3 100644 --- a/apps/sales/src/app/components/header/header.component.spec.ts +++ b/apps/sales/src/app/components/header/header.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { HeaderComponent } from './header.component'; @@ -6,11 +6,13 @@ describe('HeaderComponent', () => { let component: HeaderComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [HeaderComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [HeaderComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(HeaderComponent); diff --git a/apps/sales/src/app/components/log-out/log-out.component.spec.ts b/apps/sales/src/app/components/log-out/log-out.component.spec.ts index 2f417c562..725d06363 100644 --- a/apps/sales/src/app/components/log-out/log-out.component.spec.ts +++ b/apps/sales/src/app/components/log-out/log-out.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { LogOutComponent } from './log-out.component'; @@ -6,11 +6,13 @@ describe('LogOutComponent', () => { let component: LogOutComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [LogOutComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [LogOutComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(LogOutComponent); diff --git a/apps/sales/src/app/components/menu/menu.component.spec.ts b/apps/sales/src/app/components/menu/menu.component.spec.ts index df1337e52..d84bace1b 100644 --- a/apps/sales/src/app/components/menu/menu.component.spec.ts +++ b/apps/sales/src/app/components/menu/menu.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { MenuComponent } from './menu.component'; @@ -6,11 +6,13 @@ describe('MenuComponent', () => { let component: MenuComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [MenuComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [MenuComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(MenuComponent); diff --git a/apps/sales/src/app/components/printer-selection/printer-selection.component.spec.ts b/apps/sales/src/app/components/printer-selection/printer-selection.component.spec.ts index 2cc14c9a3..f383e6d53 100644 --- a/apps/sales/src/app/components/printer-selection/printer-selection.component.spec.ts +++ b/apps/sales/src/app/components/printer-selection/printer-selection.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { PrinterSelectionComponent } from './printer-selection.component'; @@ -6,11 +6,13 @@ describe('PrinterSelectionComponent', () => { let component: PrinterSelectionComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [PrinterSelectionComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [PrinterSelectionComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(PrinterSelectionComponent); diff --git a/apps/sales/src/app/components/process-header/process-header.component.spec.ts b/apps/sales/src/app/components/process-header/process-header.component.spec.ts index fc1a09340..001da295c 100644 --- a/apps/sales/src/app/components/process-header/process-header.component.spec.ts +++ b/apps/sales/src/app/components/process-header/process-header.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { ProcessHeaderComponent } from './process-header.component'; @@ -6,11 +6,13 @@ describe('ProcessHeaderComponent', () => { let component: ProcessHeaderComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ProcessHeaderComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [ProcessHeaderComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(ProcessHeaderComponent); diff --git a/apps/sales/src/app/components/process-tab/process-tab.component.spec.ts b/apps/sales/src/app/components/process-tab/process-tab.component.spec.ts index e57520e62..2ccdf92e2 100644 --- a/apps/sales/src/app/components/process-tab/process-tab.component.spec.ts +++ b/apps/sales/src/app/components/process-tab/process-tab.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { ProcessTabComponent } from './process-tab.component'; @@ -6,11 +6,13 @@ describe('ProcessTabComponent', () => { let component: ProcessTabComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ProcessTabComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [ProcessTabComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(ProcessTabComponent); diff --git a/apps/sales/src/app/core/error/component/error.component.spec.ts b/apps/sales/src/app/core/error/component/error.component.spec.ts index 1b4063c45..1f215a999 100644 --- a/apps/sales/src/app/core/error/component/error.component.spec.ts +++ b/apps/sales/src/app/core/error/component/error.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { ErrorComponent } from './error.component'; @@ -6,11 +6,13 @@ describe('ErrorComponent', () => { let component: ErrorComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ErrorComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [ErrorComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(ErrorComponent); diff --git a/apps/sales/src/app/core/error/component/error.component.ts b/apps/sales/src/app/core/error/component/error.component.ts index 8031917c7..a8008367d 100644 --- a/apps/sales/src/app/core/error/component/error.component.ts +++ b/apps/sales/src/app/core/error/component/error.component.ts @@ -4,8 +4,8 @@ import { UserStateService } from '../../services/user-state.service'; import { Subject } from 'rxjs'; import { Router } from '@angular/router'; import { SsoService } from 'sso'; -import { isNullOrUndefined } from 'util'; import { ErrorService } from './error.service'; +import { isNullOrUndefined } from '@utils/common'; @Component({ selector: 'app-error', diff --git a/apps/sales/src/app/core/interceptors/http-error-handler.interceptor.ts b/apps/sales/src/app/core/interceptors/http-error-handler.interceptor.ts index 72c5b85e4..eaed6a957 100644 --- a/apps/sales/src/app/core/interceptors/http-error-handler.interceptor.ts +++ b/apps/sales/src/app/core/interceptors/http-error-handler.interceptor.ts @@ -39,7 +39,8 @@ export class HttpErrorHandlerInterceptor implements HttpInterceptor { if (error.status === 0) { this.modal.open({ content: UiMessageModalComponent, - data: { title: 'Die Netzwerkverbindung wurde unterbrochen', message: 'Bitte überprüfen Sie Ihre Netzwerkverbindung.' }, + title: 'Die Netzwerkverbindung wurde unterbrochen', + data: { message: 'Bitte überprüfen Sie Ihre Netzwerkverbindung.' }, }); return throwError(error); } diff --git a/apps/sales/src/app/core/mappings/customer.mapping.ts b/apps/sales/src/app/core/mappings/customer.mapping.ts index be524b1d5..0260cafc2 100644 --- a/apps/sales/src/app/core/mappings/customer.mapping.ts +++ b/apps/sales/src/app/core/mappings/customer.mapping.ts @@ -1,4 +1,3 @@ -import { isNullOrUndefined } from 'util'; import { Injectable } from '@angular/core'; import { User, Address, Features, Organisation } from '../models/user.model'; import { @@ -20,6 +19,7 @@ import { DatePipe } from '@angular/common'; import { BACKEND_API_TIMESTAMP_FORMAT } from '../utils/app.formats'; import { KeyValueDTOOfStringAndString } from '@swagger/cat'; import { CustomerTypeInternal } from '../models/customer-type.model'; +import { isNullOrUndefined } from '@utils/common'; @Injectable({ providedIn: 'root' }) export class CustomerMapping { diff --git a/apps/sales/src/app/core/mappings/product.mapping.ts b/apps/sales/src/app/core/mappings/product.mapping.ts index 7a7802217..4577261fc 100644 --- a/apps/sales/src/app/core/mappings/product.mapping.ts +++ b/apps/sales/src/app/core/mappings/product.mapping.ts @@ -1,8 +1,7 @@ import { Product } from '../models/product.model'; -import { isNullOrUndefined } from 'util'; -import { ItemDTO } from '@swagger/cat'; +import { ItemDTO, PriceDTO } from '@swagger/cat'; import { Injectable } from '@angular/core'; -import { PriceDTO } from '@cmf/trade-api'; +import { isNullOrUndefined } from '@utils/common'; @Injectable({ providedIn: 'root' }) export class ProductMapping { diff --git a/apps/sales/src/app/core/mappings/recommendation.mapping.ts b/apps/sales/src/app/core/mappings/recommendation.mapping.ts index 4f4331ea1..3a274ccba 100644 --- a/apps/sales/src/app/core/mappings/recommendation.mapping.ts +++ b/apps/sales/src/app/core/mappings/recommendation.mapping.ts @@ -1,7 +1,7 @@ -import { isNullOrUndefined } from 'util'; import { ItemDTO } from '@swagger/cat'; import { Injectable } from '@angular/core'; import { RecommendationItem } from '../models/recommendation.model'; +import { isNullOrUndefined } from '@utils/common'; @Injectable({ providedIn: 'root' }) export class RecommendationMapping { diff --git a/apps/sales/src/app/core/mappings/shelf.mapping.ts b/apps/sales/src/app/core/mappings/shelf.mapping.ts index 76c63da39..308587be5 100644 --- a/apps/sales/src/app/core/mappings/shelf.mapping.ts +++ b/apps/sales/src/app/core/mappings/shelf.mapping.ts @@ -1,9 +1,9 @@ import { Injectable } from '@angular/core'; import { OrderItemListItemDTO, OrderDTO } from '@swagger/oms'; import { ShelfOrder } from '../models/shelf-order.model'; -import { isNullOrUndefined } from 'util'; import { CollectingShelfOrder } from '../models/collecting-shelf-order.model'; import { CollectingShelfService } from '../services/collecting-shelf.service'; +import { isNullOrUndefined } from '@utils/common'; export const orderStatusMapper: { [id: number]: string } = { 0: '-', // 'Not Set' diff --git a/apps/sales/src/app/core/models/customer-features.model.ts b/apps/sales/src/app/core/models/customer-features.model.ts index 5085f43b0..eac8809b3 100644 --- a/apps/sales/src/app/core/models/customer-features.model.ts +++ b/apps/sales/src/app/core/models/customer-features.model.ts @@ -1,4 +1,4 @@ -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@utils/common'; import { Features } from './user.model'; export class CustomerFeatures { diff --git a/apps/sales/src/app/core/overlay/strategies/container-position-strategy.ts b/apps/sales/src/app/core/overlay/strategies/container-position-strategy.ts index 74d61d180..f57be32f3 100644 --- a/apps/sales/src/app/core/overlay/strategies/container-position-strategy.ts +++ b/apps/sales/src/app/core/overlay/strategies/container-position-strategy.ts @@ -1,6 +1,6 @@ import { PositionStrategy, OverlayRef } from '@angular/cdk/overlay'; +import { isNumber } from '@utils/common'; import { fromEvent, Subscription, merge } from 'rxjs'; -import { isNumber } from 'util'; export class ContainerPositionStrategy implements PositionStrategy { windowScroll$ = fromEvent(window, 'scroll'); diff --git a/apps/sales/src/app/core/services/app.service.ts b/apps/sales/src/app/core/services/app.service.ts index 9249ff138..916866ae4 100644 --- a/apps/sales/src/app/core/services/app.service.ts +++ b/apps/sales/src/app/core/services/app.service.ts @@ -11,7 +11,7 @@ import { UserStateService } from './user-state.service'; import { Meta, MetaDefinition } from '@angular/platform-browser'; import { ActivatedRoute, Router, RoutesRecognized } from '@angular/router'; import { HttpClient } from '@angular/common/http'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@utils/common'; import smoothscroll from 'smoothscroll-polyfill'; import { WindowRef } from './window-ref.service'; import { LoadVats } from '../store/actions/vat.actions'; @@ -164,13 +164,13 @@ export class AppService implements OnDestroy { private checkRemissionReminder(status) { const isNotAtRemission = !this.router.url.includes('remission'); - (!!status && isNotAtRemission).ifTrue(() => { - this.remissionReminder$.next(); - return true; - }); - isNotAtRemission.ifFalse(() => { - this.timerClear$.next(); - }); + // (!!status && isNotAtRemission).ifTrue(() => { + // this.remissionReminder$.next(); + // return true; + // }); + // isNotAtRemission.ifFalse(() => { + // this.timerClear$.next(); + // }); return false; } @@ -207,12 +207,12 @@ export class AppService implements OnDestroy { ) .subscribe((status) => { const isNotAtRemission = !this.router.url.includes('remission'); - (status && isNotAtRemission).ifTrue(() => { - this.registerNotIdleRemissionReminder(); - }); - isNotAtRemission.ifFalse(() => { - this.timerClear$.next(); - }); + // (status && isNotAtRemission).ifTrue(() => { + // this.registerNotIdleRemissionReminder(); + // }); + // isNotAtRemission.ifFalse(() => { + // this.timerClear$.next(); + // }); }); } diff --git a/apps/sales/src/app/core/services/branch.service.ts b/apps/sales/src/app/core/services/branch.service.ts index 444996716..246ce58d7 100644 --- a/apps/sales/src/app/core/services/branch.service.ts +++ b/apps/sales/src/app/core/services/branch.service.ts @@ -2,9 +2,9 @@ import { Injectable } from '@angular/core'; import { BranchQueryTokenDTO, StoreCheckoutService, BranchDTO } from '@swagger/checkout'; import { map } from 'rxjs/operators'; import { Observable } from 'rxjs'; -import { isNullOrUndefined } from 'util'; import { OrderService } from '@swagger/oms'; import { SsoService } from 'sso'; +import { isNullOrUndefined } from '@utils/common'; @Injectable({ providedIn: 'root', diff --git a/apps/sales/src/app/core/services/collecting-shelf.service.ts b/apps/sales/src/app/core/services/collecting-shelf.service.ts index e8928a606..5c6da9e9b 100644 --- a/apps/sales/src/app/core/services/collecting-shelf.service.ts +++ b/apps/sales/src/app/core/services/collecting-shelf.service.ts @@ -23,9 +23,9 @@ import { ShelfMapping } from '../mappings/shelf.mapping'; import { CollectingShelfOrder } from '../models/collecting-shelf-order.model'; import { DatePipe } from '@angular/common'; import { BACKEND_API_TIMESTAMP_FORMAT } from '../utils/app.formats'; -import { isNullOrUndefined } from 'util'; import { Store } from '@ngxs/store'; import { CollectingShelfSelectors } from '../store/selectors/collecting-shelf.selector'; +import { isNullOrUndefined } from '@utils/common'; @Injectable({ providedIn: 'root', diff --git a/apps/sales/src/app/core/services/content-header.service.ts b/apps/sales/src/app/core/services/content-header.service.ts index 9fa1958c0..81bf589f3 100644 --- a/apps/sales/src/app/core/services/content-header.service.ts +++ b/apps/sales/src/app/core/services/content-header.service.ts @@ -9,7 +9,7 @@ import { ShelfOverlayService } from '../../modules/shelf/services'; import { RemissionSelectors } from '../store/selectors/remission.selectors'; import { SharedSelectors } from '../store/selectors/shared.selectors'; import { SearchStateFacade } from '../../store/customer'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@utils/common'; @Injectable({ providedIn: 'root' }) export class ContentHeaderService { diff --git a/apps/sales/src/app/core/services/location-service.ts b/apps/sales/src/app/core/services/location-service.ts index f1dc57a89..13f1c8832 100644 --- a/apps/sales/src/app/core/services/location-service.ts +++ b/apps/sales/src/app/core/services/location-service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs/internal/Observable'; import { Router, RouterEvent } from '@angular/router'; import { map, distinctUntilChanged, filter, shareReplay } from 'rxjs/operators'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@utils/common'; @Injectable({ providedIn: 'root' }) export class LocationService { diff --git a/apps/sales/src/app/core/store/selectors/forms.selectors.ts b/apps/sales/src/app/core/store/selectors/forms.selectors.ts index ac879a454..74812e0b8 100644 --- a/apps/sales/src/app/core/store/selectors/forms.selectors.ts +++ b/apps/sales/src/app/core/store/selectors/forms.selectors.ts @@ -1,8 +1,7 @@ import { Selector } from '@ngxs/store'; +import { isNullOrUndefined } from '@utils/common'; import { AppState, AppStateModel } from '../state/app.state'; import { FormsState, FormsStateModel } from '../state/forms.state'; -import { isNullOrUndefined } from 'util'; -import { USER_EXTRAS_FORM_STATE_KEY } from '../../utils/app.constants'; export class FormsSelectors { @Selector([AppState, FormsState]) diff --git a/apps/sales/src/app/core/store/selectors/remission.selectors.ts b/apps/sales/src/app/core/store/selectors/remission.selectors.ts index 8f6f7ad2f..71c8c791e 100644 --- a/apps/sales/src/app/core/store/selectors/remission.selectors.ts +++ b/apps/sales/src/app/core/store/selectors/remission.selectors.ts @@ -1,10 +1,10 @@ import { Selector } from '@ngxs/store'; import { RemissionState, RemissionStateModel } from '../state/remission.state'; import { RemissionProcessStatuses } from '../../models/remission-process-statuses.model'; -import { isNullOrUndefined } from 'util'; import { RemissionResourceType } from '../../../modules/remission/models/remission-resource-type.model'; import { RemissionFinishingProcessStatus } from '../../../modules/remission/models/remission-finishing-process-status.enum'; import { RemissionSourceType } from '@isa/remission'; +import { isNullOrUndefined } from '@utils/common'; export class RemissionSelectors { @Selector([RemissionState]) diff --git a/apps/sales/src/app/core/store/state/app.state.ts b/apps/sales/src/app/core/store/state/app.state.ts index 962efcb7f..91bc59989 100644 --- a/apps/sales/src/app/core/store/state/app.state.ts +++ b/apps/sales/src/app/core/store/state/app.state.ts @@ -9,7 +9,6 @@ import { } from '../actions/app.actions'; import { LoadBranches, LoadUserBranch } from '../actions/branch.actions'; import { BranchSelectors } from '../selectors/branch.selector'; -import { isNullOrUndefined } from 'util'; import { RemoveProcessNewState, ReloadProcessData } from '../actions/process.actions'; import { UserStateService } from '../../services/user-state.service'; import { UserStateSyncData } from '../../models/user-state-sync.model'; @@ -31,6 +30,7 @@ import { ReloadRemission } from '../actions/remission.actions'; import { ReloadFormState } from '../actions/forms.actions'; import { FILIALE_LANDING_PAGE } from '../../utils/app.constants'; import { Injectable } from '@angular/core'; +import { isNullOrUndefined } from '@utils/common'; export const SYNC_DATA_VERSION = 222; diff --git a/apps/sales/src/app/core/store/state/process.state.ts b/apps/sales/src/app/core/store/state/process.state.ts index afaf6b3af..1aa5b2354 100644 --- a/apps/sales/src/app/core/store/state/process.state.ts +++ b/apps/sales/src/app/core/store/state/process.state.ts @@ -15,7 +15,7 @@ import { UserStateSyncData } from '../../models/user-state-sync.model'; import { RadioButtonGroup } from '@libs/ui/lib/radio-button-group'; import { RadioButton } from '@libs/ui'; import { SetPreviusPath } from '../actions/process.actions'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@utils/common'; import { DeleteCollectingShelfForProcess } from '../actions/collecting-shelf.action'; import { FilterType } from '../../models/filter-type.enum'; import { Injectable } from '@angular/core'; diff --git a/apps/sales/src/app/core/store/state/remission.state.ts b/apps/sales/src/app/core/store/state/remission.state.ts index 412cffd07..c9bd20b78 100644 --- a/apps/sales/src/app/core/store/state/remission.state.ts +++ b/apps/sales/src/app/core/store/state/remission.state.ts @@ -5,12 +5,12 @@ import { State, Action, StateContext, Store } from '@ngxs/store'; import * as actions from '../actions/remission.actions'; import { UserStateSyncData } from '../../models/user-state-sync.model'; import { AppUserDataSync } from '../actions/app.actions'; -import { isNullOrUndefined } from 'util'; import { RemissionResourceType, RemissionTargetType } from '../../../modules/remission/models/remission-resource-type.model'; import { RemissionService, RemissionProcess } from '@isa/remission'; import { RemissionFinishingProcessStatus } from '../../../modules/remission/models/remission-finishing-process-status.enum'; import get from 'lodash/get'; import { RemissionActiveView } from '../../../modules/remission/models'; +import { isNullOrUndefined } from '@utils/common'; export class RemissionStateModel { remission: Remission; diff --git a/apps/sales/src/app/core/utils/app.utils.ts b/apps/sales/src/app/core/utils/app.utils.ts index 2afb04e6c..0009ab970 100644 --- a/apps/sales/src/app/core/utils/app.utils.ts +++ b/apps/sales/src/app/core/utils/app.utils.ts @@ -1,4 +1,4 @@ -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@utils/common'; /* * method to map associative arrays to iterable arrays diff --git a/apps/sales/src/app/modules/branch/pages/branch-main/branch-main.component.spec.ts b/apps/sales/src/app/modules/branch/pages/branch-main/branch-main.component.spec.ts index d9653cb29..0204c3d86 100644 --- a/apps/sales/src/app/modules/branch/pages/branch-main/branch-main.component.spec.ts +++ b/apps/sales/src/app/modules/branch/pages/branch-main/branch-main.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { BranchMainComponent } from './branch-main.component'; @@ -6,11 +6,13 @@ describe('BranchMainComponent', () => { let component: BranchMainComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [BranchMainComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [BranchMainComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(BranchMainComponent); diff --git a/apps/sales/src/app/modules/dashboard/components/book-card/book-card.component.spec.ts b/apps/sales/src/app/modules/dashboard/components/book-card/book-card.component.spec.ts index 74e32434b..1fa4f0c5c 100644 --- a/apps/sales/src/app/modules/dashboard/components/book-card/book-card.component.spec.ts +++ b/apps/sales/src/app/modules/dashboard/components/book-card/book-card.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { BookCardComponent } from './book-card.component'; @@ -6,11 +6,13 @@ describe('BookCardComponent', () => { let component: BookCardComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [BookCardComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [BookCardComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(BookCardComponent); diff --git a/apps/sales/src/app/modules/dashboard/components/event-card/event-card.component.spec.ts b/apps/sales/src/app/modules/dashboard/components/event-card/event-card.component.spec.ts index a03281b42..70b4fc3b3 100644 --- a/apps/sales/src/app/modules/dashboard/components/event-card/event-card.component.spec.ts +++ b/apps/sales/src/app/modules/dashboard/components/event-card/event-card.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { EventCardComponent } from './event-card.component'; @@ -6,11 +6,13 @@ describe('EventCardComponent', () => { let component: EventCardComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [EventCardComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [EventCardComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(EventCardComponent); diff --git a/apps/sales/src/app/modules/dashboard/components/kpi-card/kpi-card.component.spec.ts b/apps/sales/src/app/modules/dashboard/components/kpi-card/kpi-card.component.spec.ts index 1862960c7..96449d61a 100644 --- a/apps/sales/src/app/modules/dashboard/components/kpi-card/kpi-card.component.spec.ts +++ b/apps/sales/src/app/modules/dashboard/components/kpi-card/kpi-card.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { KpiCardComponent } from './kpi-card.component'; @@ -6,11 +6,13 @@ describe('KpiCardComponent', () => { let component: KpiCardComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [KpiCardComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [KpiCardComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(KpiCardComponent); diff --git a/apps/sales/src/app/modules/dashboard/components/news-card/news-card.component.spec.ts b/apps/sales/src/app/modules/dashboard/components/news-card/news-card.component.spec.ts index 202af22c9..22af278ca 100644 --- a/apps/sales/src/app/modules/dashboard/components/news-card/news-card.component.spec.ts +++ b/apps/sales/src/app/modules/dashboard/components/news-card/news-card.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { NewsCardComponent } from './news-card.component'; @@ -6,11 +6,13 @@ describe('NewsCardComponent', () => { let component: NewsCardComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [NewsCardComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [NewsCardComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(NewsCardComponent); diff --git a/apps/sales/src/app/modules/dashboard/components/recommandation-card/recommandation-card.component.spec.ts b/apps/sales/src/app/modules/dashboard/components/recommandation-card/recommandation-card.component.spec.ts index c71df7636..78c0414fb 100644 --- a/apps/sales/src/app/modules/dashboard/components/recommandation-card/recommandation-card.component.spec.ts +++ b/apps/sales/src/app/modules/dashboard/components/recommandation-card/recommandation-card.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { RecommandationCardComponent } from './recommandation-card.component'; @@ -6,11 +6,13 @@ describe('RecommandationCardComponent', () => { let component: RecommandationCardComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [RecommandationCardComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [RecommandationCardComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(RecommandationCardComponent); diff --git a/apps/sales/src/app/modules/dashboard/pages/dashboard.component.spec.ts b/apps/sales/src/app/modules/dashboard/pages/dashboard.component.spec.ts index ddaa58fc9..6067e17ca 100644 --- a/apps/sales/src/app/modules/dashboard/pages/dashboard.component.spec.ts +++ b/apps/sales/src/app/modules/dashboard/pages/dashboard.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { DashboardComponent } from './dashboard.component'; @@ -6,11 +6,13 @@ describe('DashboardComponent', () => { let component: DashboardComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [DashboardComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [DashboardComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(DashboardComponent); diff --git a/apps/sales/src/app/modules/goods-in/components/goods-in-article-details/goods-in-article-details.component.spec.ts b/apps/sales/src/app/modules/goods-in/components/goods-in-article-details/goods-in-article-details.component.spec.ts index 338f71c1c..5af9842c6 100644 --- a/apps/sales/src/app/modules/goods-in/components/goods-in-article-details/goods-in-article-details.component.spec.ts +++ b/apps/sales/src/app/modules/goods-in/components/goods-in-article-details/goods-in-article-details.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { GoodsInArticleDetailsComponent } from './goods-in-article-details.component'; @@ -6,11 +6,13 @@ describe('GoodsInArticleDetailsComponent', () => { let component: GoodsInArticleDetailsComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [GoodsInArticleDetailsComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [GoodsInArticleDetailsComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(GoodsInArticleDetailsComponent); diff --git a/apps/sales/src/app/modules/goods-in/components/goods-in-article-details/goods-in-article-details.component.ts b/apps/sales/src/app/modules/goods-in/components/goods-in-article-details/goods-in-article-details.component.ts index 31d08f303..9456009d5 100644 --- a/apps/sales/src/app/modules/goods-in/components/goods-in-article-details/goods-in-article-details.component.ts +++ b/apps/sales/src/app/modules/goods-in/components/goods-in-article-details/goods-in-article-details.component.ts @@ -5,11 +5,11 @@ import { SupplierState } from 'apps/sales/src/app/core/store/state/supplier.stat import { filter, take, takeUntil } from 'rxjs/operators'; import { objectNotNull } from 'apps/sales/src/app/core/utils/app.utils'; import { OrderStatus, orderChannelMapper, receiptType } from 'apps/sales/src/app/core/mappings/shelf.mapping'; -import { isNullOrUndefined } from 'util'; import { BranchSelectors } from 'apps/sales/src/app/core/store/selectors/branch.selector'; import { Store } from '@ngxs/store'; import { CollectingShelfService } from 'apps/sales/src/app/core/services/collecting-shelf.service'; import { DatePipe } from '@angular/common'; +import { isNullOrUndefined } from '@utils/common'; @Component({ selector: 'app-goods-in-article-details', diff --git a/apps/sales/src/app/modules/goods-in/components/goods-in-order-card/goods-in-order-card.component.spec.ts b/apps/sales/src/app/modules/goods-in/components/goods-in-order-card/goods-in-order-card.component.spec.ts index a026817be..9fc994423 100644 --- a/apps/sales/src/app/modules/goods-in/components/goods-in-order-card/goods-in-order-card.component.spec.ts +++ b/apps/sales/src/app/modules/goods-in/components/goods-in-order-card/goods-in-order-card.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { GoodsInOrderCardComponent } from './goods-in-order-card.component'; @@ -6,11 +6,13 @@ describe('GoodsInOrderCardComponent', () => { let component: GoodsInOrderCardComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [GoodsInOrderCardComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [GoodsInOrderCardComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(GoodsInOrderCardComponent); diff --git a/apps/sales/src/app/modules/goods-in/components/goods-in-order-card/goods-in-order-card.component.ts b/apps/sales/src/app/modules/goods-in/components/goods-in-order-card/goods-in-order-card.component.ts index 42c521786..52edc78d4 100644 --- a/apps/sales/src/app/modules/goods-in/components/goods-in-order-card/goods-in-order-card.component.ts +++ b/apps/sales/src/app/modules/goods-in/components/goods-in-order-card/goods-in-order-card.component.ts @@ -1,13 +1,13 @@ import { Component, OnInit, Input } from '@angular/core'; import { OrderItemListItemDTO } from '@swagger/oms'; import { orderStatusMapper } from 'apps/sales/src/app/core/mappings/shelf.mapping'; -import { isNullOrUndefined } from 'util'; import { Store } from '@ngxs/store'; import { Router } from '@angular/router'; import { AddBreadcrumb } from 'apps/sales/src/app/core/store/actions/breadcrumb.actions'; import { SetBranchProcessCurrentPath } from 'apps/sales/src/app/core/store/actions/branch-process.actions'; import { GOODS_IN_SCROLL_INDEX } from 'apps/sales/src/app/core/utils/app.constants'; import { AppState } from 'apps/sales/src/app/core/store/state/app.state'; +import { isNullOrUndefined } from '@utils/common'; @Component({ selector: 'app-goods-in-order-card', diff --git a/apps/sales/src/app/modules/goods-in/components/goods-in-order-tag/goods-in-order-tag.component.spec.ts b/apps/sales/src/app/modules/goods-in/components/goods-in-order-tag/goods-in-order-tag.component.spec.ts index 42e28963c..c58bd7fcd 100644 --- a/apps/sales/src/app/modules/goods-in/components/goods-in-order-tag/goods-in-order-tag.component.spec.ts +++ b/apps/sales/src/app/modules/goods-in/components/goods-in-order-tag/goods-in-order-tag.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { GoodsInOrderTagComponent } from './goods-in-order-tag.component'; @@ -6,11 +6,13 @@ describe('GoodsInOrderTagComponent', () => { let component: GoodsInOrderTagComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [GoodsInOrderTagComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [GoodsInOrderTagComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(GoodsInOrderTagComponent); diff --git a/apps/sales/src/app/modules/goods-in/components/order-loading/goods-in-order-loading.component.spec.ts b/apps/sales/src/app/modules/goods-in/components/order-loading/goods-in-order-loading.component.spec.ts index 8f7f8b36f..720151978 100644 --- a/apps/sales/src/app/modules/goods-in/components/order-loading/goods-in-order-loading.component.spec.ts +++ b/apps/sales/src/app/modules/goods-in/components/order-loading/goods-in-order-loading.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { GoodsInOrderLoadingComponent } from './goods-in-order-loading.component'; @@ -6,11 +6,13 @@ describe('OrderLoadingComponent', () => { let component: GoodsInOrderLoadingComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [GoodsInOrderLoadingComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [GoodsInOrderLoadingComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(GoodsInOrderLoadingComponent); diff --git a/apps/sales/src/app/modules/goods-in/pages/goods-in-order-details/goods-in-order-details.component.spec.ts b/apps/sales/src/app/modules/goods-in/pages/goods-in-order-details/goods-in-order-details.component.spec.ts index cd18aba77..926d3f81e 100644 --- a/apps/sales/src/app/modules/goods-in/pages/goods-in-order-details/goods-in-order-details.component.spec.ts +++ b/apps/sales/src/app/modules/goods-in/pages/goods-in-order-details/goods-in-order-details.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { GoodsInOrderDetailsComponent } from './goods-in-order-details.component'; @@ -6,11 +6,13 @@ describe('GoodsInOrderDetailsComponent', () => { let component: GoodsInOrderDetailsComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [GoodsInOrderDetailsComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [GoodsInOrderDetailsComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(GoodsInOrderDetailsComponent); diff --git a/apps/sales/src/app/modules/goods-in/pages/goods-in-order-details/goods-in-order-details.component.ts b/apps/sales/src/app/modules/goods-in/pages/goods-in-order-details/goods-in-order-details.component.ts index 8515fe33a..e224b3106 100644 --- a/apps/sales/src/app/modules/goods-in/pages/goods-in-order-details/goods-in-order-details.component.ts +++ b/apps/sales/src/app/modules/goods-in/pages/goods-in-order-details/goods-in-order-details.component.ts @@ -12,8 +12,6 @@ import { import { ActivatedRoute, Params, Router } from '@angular/router'; import { take, switchMap, filter, takeUntil, concatMap, catchError, map } from 'rxjs/operators'; import { of, Observable, Subject } from 'rxjs'; -import { CollectingShelfService } from 'apps/sales/src/app/core/services/collecting-shelf.service'; -import { isNullOrUndefined } from 'util'; import { OrderDTO, OrderItemDTO, OrderItemSubsetDTO, QuantityDTO } from '@swagger/oms'; import { orderStatusMapper, orderChannelMapper, OrderStatus } from 'apps/sales/src/app/core/mappings/shelf.mapping'; import { Store } from '@ngxs/store'; @@ -23,6 +21,8 @@ import { Breadcrumb } from 'apps/sales/src/app/core/models/breadcrumb.model'; import { SetEditOrder } from 'apps/sales/src/app/core/store/actions/collecting-shelf.action'; import { PrinterService } from 'apps/sales/src/app/core/services/printer.service'; import { AppService } from 'apps/sales/src/app/core/services/app.service'; +import { CollectingShelfService } from '@sales/core-services'; +import { isNullOrUndefined } from '@utils/common'; enum Tag { Maxi = 0, diff --git a/apps/sales/src/app/modules/goods-in/pages/goods-in-search-results/goods-in-search-results.component.spec.ts b/apps/sales/src/app/modules/goods-in/pages/goods-in-search-results/goods-in-search-results.component.spec.ts index 9f171805a..fb5713a3c 100644 --- a/apps/sales/src/app/modules/goods-in/pages/goods-in-search-results/goods-in-search-results.component.spec.ts +++ b/apps/sales/src/app/modules/goods-in/pages/goods-in-search-results/goods-in-search-results.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { GoodsInSearchResultsComponent } from './goods-in-search-results.component'; @@ -6,11 +6,13 @@ describe('GoodsInSearchResultsComponent', () => { let component: GoodsInSearchResultsComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [GoodsInSearchResultsComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [GoodsInSearchResultsComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(GoodsInSearchResultsComponent); diff --git a/apps/sales/src/app/modules/goods-in/pages/goods-in-search-results/goods-in-search-results.component.ts b/apps/sales/src/app/modules/goods-in/pages/goods-in-search-results/goods-in-search-results.component.ts index 2a8cd15a3..9f610b952 100644 --- a/apps/sales/src/app/modules/goods-in/pages/goods-in-search-results/goods-in-search-results.component.ts +++ b/apps/sales/src/app/modules/goods-in/pages/goods-in-search-results/goods-in-search-results.component.ts @@ -9,12 +9,12 @@ import { AppState } from 'apps/sales/src/app/core/store/state/app.state'; import { takeUntil, filter, switchMap, tap } from 'rxjs/operators'; import { ModuleSwitcher } from 'apps/sales/src/app/core/models/app-switcher.enum'; import { GoodsInSelectors } from 'apps/sales/src/app/core/store/selectors/goods-in.selectors'; -import { isNullOrUndefined } from 'util'; import { OrderItemListItemDTO } from '@swagger/oms'; import { SetGoodsInUseCache } from 'apps/sales/src/app/core/store/actions/goods-in.actions'; import { GOODS_IN_SCROLL_INDEX } from 'apps/sales/src/app/core/utils/app.constants'; import { ListRange } from '@angular/cdk/collections'; import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling'; +import { isNullOrUndefined } from '@utils/common'; @Component({ selector: 'app-goods-in-search-results', diff --git a/apps/sales/src/app/modules/goods-in/pages/goods-in-search/goods-in-search.component.spec.ts b/apps/sales/src/app/modules/goods-in/pages/goods-in-search/goods-in-search.component.spec.ts index d33e42f7e..af3dccffe 100644 --- a/apps/sales/src/app/modules/goods-in/pages/goods-in-search/goods-in-search.component.spec.ts +++ b/apps/sales/src/app/modules/goods-in/pages/goods-in-search/goods-in-search.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { GoodsInSearchComponent } from './goods-in-search.component'; @@ -6,11 +6,13 @@ describe('GoodsInSearchComponent', () => { let component: GoodsInSearchComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [GoodsInSearchComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [GoodsInSearchComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(GoodsInSearchComponent); diff --git a/apps/sales/src/app/modules/process/components/process-delete-dialog/process-delete-dialog.component.spec.ts b/apps/sales/src/app/modules/process/components/process-delete-dialog/process-delete-dialog.component.spec.ts index e5fb65cee..6e8d7b3a9 100644 --- a/apps/sales/src/app/modules/process/components/process-delete-dialog/process-delete-dialog.component.spec.ts +++ b/apps/sales/src/app/modules/process/components/process-delete-dialog/process-delete-dialog.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { ProcessDeleteDialogComponent } from './process-delete-dialog.component'; @@ -6,11 +6,13 @@ describe('ProcessDeleteDialogComponent', () => { let component: ProcessDeleteDialogComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ProcessDeleteDialogComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [ProcessDeleteDialogComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(ProcessDeleteDialogComponent); diff --git a/apps/sales/src/app/modules/product/pages/product-details/product-details.component.ts b/apps/sales/src/app/modules/product/pages/product-details/product-details.component.ts index c1fcac123..6abb3c198 100644 --- a/apps/sales/src/app/modules/product/pages/product-details/product-details.component.ts +++ b/apps/sales/src/app/modules/product/pages/product-details/product-details.component.ts @@ -29,7 +29,7 @@ // import { RecommendationsComponent } from '../../../recommendations/recommendations.component'; // import { Features } from '../../../../core/models/user.model'; // import { SharedSelectors } from 'apps/sales/src/app/core/store/selectors/shared.selectors'; -// import { isNullOrUndefined } from 'util'; +// import { isNullOrUndefined } from '@utils/common'; // import { SetSelectedItemIfMissingOrChanged } from 'apps/sales/src/app/core/store/actions/process.actions'; // import { ProductOtherFormatsComponent } from '../../components/product-other-formats/product-other-formats.component'; // import { allowedAvailabilityStatusCodes } from 'apps/sales/src/app/core/utils/product.util'; diff --git a/apps/sales/src/app/modules/remission/components/add-product-to-remission-dialog/add-product-to-remission-dialog.component.spec.ts b/apps/sales/src/app/modules/remission/components/add-product-to-remission-dialog/add-product-to-remission-dialog.component.spec.ts index a9c5202ec..dd9ad9381 100644 --- a/apps/sales/src/app/modules/remission/components/add-product-to-remission-dialog/add-product-to-remission-dialog.component.spec.ts +++ b/apps/sales/src/app/modules/remission/components/add-product-to-remission-dialog/add-product-to-remission-dialog.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { AddProductToRemissionDialogComponent } from './add-product-to-remission-dialog.component'; @@ -6,11 +6,13 @@ describe('AddProductToRemissionDialogComponent', () => { let component: AddProductToRemissionDialogComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [AddProductToRemissionDialogComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [AddProductToRemissionDialogComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(AddProductToRemissionDialogComponent); diff --git a/apps/sales/src/app/modules/remission/components/add-product-to-remission-dialog/add-product-to-remission-dialog.component.ts b/apps/sales/src/app/modules/remission/components/add-product-to-remission-dialog/add-product-to-remission-dialog.component.ts index b3c877322..ac5b0d8e2 100644 --- a/apps/sales/src/app/modules/remission/components/add-product-to-remission-dialog/add-product-to-remission-dialog.component.ts +++ b/apps/sales/src/app/modules/remission/components/add-product-to-remission-dialog/add-product-to-remission-dialog.component.ts @@ -2,11 +2,11 @@ import { Component, OnInit, Input, EventEmitter, Output, OnDestroy, ViewChild } import { RemissionProduct, RemissionService } from '@isa/remission'; import { ModalService, ButtonComponent } from '@libs/ui'; import { filter, take, catchError, debounceTime, delay } from 'rxjs/operators'; -import { isNullOrUndefined } from 'util'; import { NO_RESMISSION_REASON_SELECTED } from '../../constants/remission.constants'; import { RemissionHelperService } from '../../services/remission-helper.service'; import { Store } from '@ngxs/store'; import { SetRemissionIsLoading } from 'apps/sales/src/app/core/store/actions/remission.actions'; +import { isNullOrUndefined } from '@utils/common'; @Component({ selector: 'app-add-product-to-remission-dialog', diff --git a/apps/sales/src/app/modules/remission/components/remission-add-product-to-shipping-document-dialog/remission-add-product-to-shipping-document-dialog.component.spec.ts b/apps/sales/src/app/modules/remission/components/remission-add-product-to-shipping-document-dialog/remission-add-product-to-shipping-document-dialog.component.spec.ts index 5f76892c7..4faf997b4 100644 --- a/apps/sales/src/app/modules/remission/components/remission-add-product-to-shipping-document-dialog/remission-add-product-to-shipping-document-dialog.component.spec.ts +++ b/apps/sales/src/app/modules/remission/components/remission-add-product-to-shipping-document-dialog/remission-add-product-to-shipping-document-dialog.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { RemissionAddProductToShippingDocumentDialogComponent } from './remission-add-product-to-shipping-document-dialog.component'; @@ -6,11 +6,13 @@ describe('RemissionAddProductToShippingDocumentDialogComponent', () => { let component: RemissionAddProductToShippingDocumentDialogComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [RemissionAddProductToShippingDocumentDialogComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [RemissionAddProductToShippingDocumentDialogComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(RemissionAddProductToShippingDocumentDialogComponent); diff --git a/apps/sales/src/app/modules/remission/components/remission-add-product-to-shipping-document-partially-dialog/remission-add-product-to-shipping-document-partially-dialog.component.spec.ts b/apps/sales/src/app/modules/remission/components/remission-add-product-to-shipping-document-partially-dialog/remission-add-product-to-shipping-document-partially-dialog.component.spec.ts index 21d1caa09..1e73f9c17 100644 --- a/apps/sales/src/app/modules/remission/components/remission-add-product-to-shipping-document-partially-dialog/remission-add-product-to-shipping-document-partially-dialog.component.spec.ts +++ b/apps/sales/src/app/modules/remission/components/remission-add-product-to-shipping-document-partially-dialog/remission-add-product-to-shipping-document-partially-dialog.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; // tslint:disable-next-line: max-line-length import { RemissionAddProductToShippingDocumentPartiallyDialogComponent } from './remission-add-product-to-shipping-document-partially-dialog.component'; @@ -7,11 +7,13 @@ describe('RemissionAddProductToShippingDocumentPartiallyDialogComponent', () => let component: RemissionAddProductToShippingDocumentPartiallyDialogComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [RemissionAddProductToShippingDocumentPartiallyDialogComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [RemissionAddProductToShippingDocumentPartiallyDialogComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(RemissionAddProductToShippingDocumentPartiallyDialogComponent); diff --git a/apps/sales/src/app/modules/remission/components/remission-add-product-to-shipping-document-partially-dialog/remission-add-product-to-shipping-document-partially-dialog.component.ts b/apps/sales/src/app/modules/remission/components/remission-add-product-to-shipping-document-partially-dialog/remission-add-product-to-shipping-document-partially-dialog.component.ts index c7830d4b1..239943d66 100644 --- a/apps/sales/src/app/modules/remission/components/remission-add-product-to-shipping-document-partially-dialog/remission-add-product-to-shipping-document-partially-dialog.component.ts +++ b/apps/sales/src/app/modules/remission/components/remission-add-product-to-shipping-document-partially-dialog/remission-add-product-to-shipping-document-partially-dialog.component.ts @@ -2,8 +2,8 @@ import { Component, OnInit, Input, Output, EventEmitter, OnChanges, SimpleChange import { ModalService } from '@libs/ui'; import { RemissionService, RemissionPlacementType } from '@isa/remission'; import { take, filter } from 'rxjs/operators'; -import { isNullOrUndefined } from 'util'; import { FormControl, Validators } from '@angular/forms'; +import { isNullOrUndefined } from '@utils/common'; @Component({ selector: 'app-remission-add-product-to-shipping-document-partially-dialog', diff --git a/apps/sales/src/app/modules/remission/components/remission-filter-item/remission-filter-item.component.spec.ts b/apps/sales/src/app/modules/remission/components/remission-filter-item/remission-filter-item.component.spec.ts index 57debbcdd..d8e91c621 100644 --- a/apps/sales/src/app/modules/remission/components/remission-filter-item/remission-filter-item.component.spec.ts +++ b/apps/sales/src/app/modules/remission/components/remission-filter-item/remission-filter-item.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { RemissionFilterItemComponent } from './remission-filter-item.component'; @@ -6,11 +6,13 @@ describe('RemissionFilterItemComponent', () => { let component: RemissionFilterItemComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [RemissionFilterItemComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [RemissionFilterItemComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(RemissionFilterItemComponent); diff --git a/apps/sales/src/app/modules/remission/components/remission-filter-item/remission-filter-item.component.ts b/apps/sales/src/app/modules/remission/components/remission-filter-item/remission-filter-item.component.ts index e54b4eaa1..df0e157cf 100644 --- a/apps/sales/src/app/modules/remission/components/remission-filter-item/remission-filter-item.component.ts +++ b/apps/sales/src/app/modules/remission/components/remission-filter-item/remission-filter-item.component.ts @@ -6,10 +6,10 @@ import { RemissionResourceType } from '../../models/remission-resource-type.mode import { Store } from '@ngxs/store'; import { RemissionSelectors } from 'apps/sales/src/app/core/store/selectors/remission.selectors'; import { Subject } from 'rxjs'; -import { isNullOrUndefined } from 'util'; import { filter, take, takeUntil } from 'rxjs/operators'; import { RemissionHelperService } from '../../services/remission-helper.service'; import isEqual from 'lodash/isEqual'; +import { isNullOrUndefined } from '@utils/common'; @Component({ selector: 'app-remission-filter-item', diff --git a/apps/sales/src/app/modules/remission/components/remission-filters/remission-filters.component.spec.ts b/apps/sales/src/app/modules/remission/components/remission-filters/remission-filters.component.spec.ts index 21da7a3d2..b8ce11d0f 100644 --- a/apps/sales/src/app/modules/remission/components/remission-filters/remission-filters.component.spec.ts +++ b/apps/sales/src/app/modules/remission/components/remission-filters/remission-filters.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { RemissionFiltersComponent } from './remission-filters.component'; @@ -6,11 +6,13 @@ describe('RemissionFiltersComponent', () => { let component: RemissionFiltersComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [RemissionFiltersComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [RemissionFiltersComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(RemissionFiltersComponent); diff --git a/apps/sales/src/app/modules/remission/components/remission-generate-shipping-document/remission-generate-shipping-document.component.spec.ts b/apps/sales/src/app/modules/remission/components/remission-generate-shipping-document/remission-generate-shipping-document.component.spec.ts index 90df978ae..c1d3abc8f 100644 --- a/apps/sales/src/app/modules/remission/components/remission-generate-shipping-document/remission-generate-shipping-document.component.spec.ts +++ b/apps/sales/src/app/modules/remission/components/remission-generate-shipping-document/remission-generate-shipping-document.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { RemissionGenerateShippingDocumentComponent } from './remission-generate-shipping-document.component'; @@ -6,11 +6,13 @@ describe('RemissionGenerateShippingDocumentComponent', () => { let component: RemissionGenerateShippingDocumentComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [RemissionGenerateShippingDocumentComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [RemissionGenerateShippingDocumentComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(RemissionGenerateShippingDocumentComponent); diff --git a/apps/sales/src/app/modules/remission/components/remission-generate-shipping-document/remission-generate-shipping-document.component.ts b/apps/sales/src/app/modules/remission/components/remission-generate-shipping-document/remission-generate-shipping-document.component.ts index f6b8713a5..17e4d4980 100644 --- a/apps/sales/src/app/modules/remission/components/remission-generate-shipping-document/remission-generate-shipping-document.component.ts +++ b/apps/sales/src/app/modules/remission/components/remission-generate-shipping-document/remission-generate-shipping-document.component.ts @@ -3,7 +3,7 @@ import { Observable } from 'rxjs'; import { Select, Store } from '@ngxs/store'; import { RemissionSelectors } from 'apps/sales/src/app/core/store/selectors/remission.selectors'; import { filter, take } from 'rxjs/operators'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@utils/common'; import { AddBreadcrumb } from 'apps/sales/src/app/core/store/actions/breadcrumb.actions'; import { SetBranchProcessCurrentPath } from 'apps/sales/src/app/core/store/actions/branch-process.actions'; import { Router } from '@angular/router'; diff --git a/apps/sales/src/app/modules/remission/components/remission-leave-dialog/remission-leave-dialog.component.spec.ts b/apps/sales/src/app/modules/remission/components/remission-leave-dialog/remission-leave-dialog.component.spec.ts index 6bcb00591..4ef86b125 100644 --- a/apps/sales/src/app/modules/remission/components/remission-leave-dialog/remission-leave-dialog.component.spec.ts +++ b/apps/sales/src/app/modules/remission/components/remission-leave-dialog/remission-leave-dialog.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { RemissionLeaveDialogComponent } from './remission-leave-dialog.component'; @@ -6,11 +6,13 @@ describe('RemissionLeaveDialogComponent', () => { let component: RemissionLeaveDialogComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [RemissionLeaveDialogComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [RemissionLeaveDialogComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(RemissionLeaveDialogComponent); diff --git a/apps/sales/src/app/modules/remission/components/remission-list-card-loading/remission-list-card-loading.component.spec.ts b/apps/sales/src/app/modules/remission/components/remission-list-card-loading/remission-list-card-loading.component.spec.ts index a82a3a950..7467d3ce8 100644 --- a/apps/sales/src/app/modules/remission/components/remission-list-card-loading/remission-list-card-loading.component.spec.ts +++ b/apps/sales/src/app/modules/remission/components/remission-list-card-loading/remission-list-card-loading.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { RemissionListCardLoadingComponent } from './remission-list-card-loading.component'; @@ -6,11 +6,13 @@ describe('RemissionListCardLoadingComponent', () => { let component: RemissionListCardLoadingComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [RemissionListCardLoadingComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [RemissionListCardLoadingComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(RemissionListCardLoadingComponent); diff --git a/apps/sales/src/app/modules/remission/components/remission-list-card-started/remission-list-card-started.component.spec.ts b/apps/sales/src/app/modules/remission/components/remission-list-card-started/remission-list-card-started.component.spec.ts index e6a8a0e97..b115feae7 100644 --- a/apps/sales/src/app/modules/remission/components/remission-list-card-started/remission-list-card-started.component.spec.ts +++ b/apps/sales/src/app/modules/remission/components/remission-list-card-started/remission-list-card-started.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { RemissionListCardStartedComponent } from './remission-list-card-started.component'; import { By } from '@angular/platform-browser'; import { remissionProducts } from '../../mocks'; @@ -11,15 +11,17 @@ describe('RemissionListCardStartedComponent', () => { let component: RemissionListCardStartedComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [RemissionClientModule, NgxsModule.forRoot(), HttpClientTestingModule], - providers: [RemissionService], - }).compileComponents(); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [RemissionClientModule, NgxsModule.forRoot(), HttpClientTestingModule], + providers: [RemissionService], + }).compileComponents(); - fixture = TestBed.createComponent(RemissionListCardStartedComponent); - component = fixture.componentInstance; - })); + fixture = TestBed.createComponent(RemissionListCardStartedComponent); + component = fixture.componentInstance; + }) + ); it('should show Restmenge if it is a residual product ', () => { const expectedColorCode = '#c9962d'; diff --git a/apps/sales/src/app/modules/remission/components/remission-list-card/card-header/card-header.component.spec.ts b/apps/sales/src/app/modules/remission/components/remission-list-card/card-header/card-header.component.spec.ts index cca1cd8a8..d7780a897 100644 --- a/apps/sales/src/app/modules/remission/components/remission-list-card/card-header/card-header.component.spec.ts +++ b/apps/sales/src/app/modules/remission/components/remission-list-card/card-header/card-header.component.spec.ts @@ -1,4 +1,4 @@ -import { TestBed, ComponentFixture, async } from '@angular/core/testing'; +import { TestBed, ComponentFixture, waitForAsync } from '@angular/core/testing'; import { RemissionListCardHeaderComponent } from './card-header.component'; import { remissionProducts } from '../../../mocks'; @@ -7,19 +7,21 @@ describe('CardHeaderComponent', () => { let component: RemissionListCardHeaderComponent; let element: HTMLElement; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [RemissionListCardHeaderComponent], - }).compileComponents(); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [RemissionListCardHeaderComponent], + }).compileComponents(); - fixture = TestBed.createComponent(RemissionListCardHeaderComponent); - component = fixture.componentInstance; - element = fixture.nativeElement; + fixture = TestBed.createComponent(RemissionListCardHeaderComponent); + component = fixture.componentInstance; + element = fixture.nativeElement; - component.product = remissionProducts[0]; + component.product = remissionProducts[0]; - fixture.detectChanges(); - })); + fixture.detectChanges(); + }) + ); it('should show the authors', () => { const authors = element.querySelector('.authors'); diff --git a/apps/sales/src/app/modules/remission/components/remission-list-card/remission-list-card.component.spec.ts b/apps/sales/src/app/modules/remission/components/remission-list-card/remission-list-card.component.spec.ts index bc45d58b1..80b064e09 100644 --- a/apps/sales/src/app/modules/remission/components/remission-list-card/remission-list-card.component.spec.ts +++ b/apps/sales/src/app/modules/remission/components/remission-list-card/remission-list-card.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { RemissionListCardComponent } from './remission-list-card.component'; import { remissionProducts } from '../../mocks'; import { By } from '@angular/platform-browser'; @@ -12,12 +12,14 @@ describe('RemissionListCardComponent', () => { let component: RemissionListCardComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [CommonModule, HttpClientTestingModule, RemissionClientModule], - providers: [RemissionService], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [CommonModule, HttpClientTestingModule, RemissionClientModule], + providers: [RemissionService], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(RemissionListCardComponent); diff --git a/apps/sales/src/app/modules/remission/components/remission-list/remission-list.component.spec.ts b/apps/sales/src/app/modules/remission/components/remission-list/remission-list.component.spec.ts index 9a1af0449..dcc0ffdd6 100644 --- a/apps/sales/src/app/modules/remission/components/remission-list/remission-list.component.spec.ts +++ b/apps/sales/src/app/modules/remission/components/remission-list/remission-list.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; +import { ComponentFixture, TestBed, fakeAsync, tick, waitForAsync } from '@angular/core/testing'; import { RemissionListComponent } from './remission-list.component'; import { of } from 'rxjs'; import { RemissionClientModule } from '../../remission-client.module'; @@ -15,12 +15,14 @@ describe('RemissionListComponent', () => { let component: RemissionListComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [RemissionClientModule], - providers: [{ provide: RemissionService, useClass: MockRemissionService }], - }).overrideTemplate(RemissionListComponent, `
`); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [RemissionClientModule], + providers: [{ provide: RemissionService, useClass: MockRemissionService }], + }).overrideTemplate(RemissionListComponent, `
`); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(RemissionListComponent); diff --git a/apps/sales/src/app/modules/remission/components/remission-list/remission-list.component.ts b/apps/sales/src/app/modules/remission/components/remission-list/remission-list.component.ts index 36a99119b..4ec9a6e1c 100644 --- a/apps/sales/src/app/modules/remission/components/remission-list/remission-list.component.ts +++ b/apps/sales/src/app/modules/remission/components/remission-list/remission-list.component.ts @@ -1,14 +1,13 @@ import { Component, OnInit, Input, OnDestroy, ViewChild, Output, EventEmitter, ElementRef, ChangeDetectorRef } from '@angular/core'; -import { RemissionListDataSource } from './remission-list.datasource'; import { RemissionService, RemissionProcess, RemissionProduct, RemissionFilter } from '@isa/remission'; import { RemissionHelperService } from '../../services/remission-helper.service'; import { Subject, of, Subscription, Observable } from 'rxjs'; import { takeUntil, filter, take, catchError, share, debounceTime, first, map, switchMap, tap } from 'rxjs/operators'; -import { isNullOrUndefined } from 'util'; import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling'; import { Select, Store } from '@ngxs/store'; import { RemissionSelectors } from 'apps/sales/src/app/core/store/selectors/remission.selectors'; import { SetRemissionIsLoading } from 'apps/sales/src/app/core/store/actions/remission.actions'; +import { isNullOrUndefined } from '@utils/common'; @Component({ selector: 'app-remission-list', diff --git a/apps/sales/src/app/modules/remission/components/remission-list/remission-list.datasource.ts b/apps/sales/src/app/modules/remission/components/remission-list/remission-list.datasource.ts index 071a0f78f..5ee8080c4 100644 --- a/apps/sales/src/app/modules/remission/components/remission-list/remission-list.datasource.ts +++ b/apps/sales/src/app/modules/remission/components/remission-list/remission-list.datasource.ts @@ -5,7 +5,7 @@ import { take, takeUntil, debounceTime, switchMap, filter } from 'rxjs/operators import { RemissionProduct, RemissionService } from '@isa/remission'; import { SetRemissionProducts } from 'apps/sales/src/app/core/store/actions/remission.actions'; import { RemissionHelperService } from '../../services/remission-helper.service'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@utils/common'; export class RemissionListDataSource extends DataSource { private pageSize = 10; diff --git a/apps/sales/src/app/modules/remission/components/remission-open-shipping-documents-widget/remission-open-shipping-document-widget.component.ts b/apps/sales/src/app/modules/remission/components/remission-open-shipping-documents-widget/remission-open-shipping-document-widget.component.ts index 381242362..ebbd2a07b 100644 --- a/apps/sales/src/app/modules/remission/components/remission-open-shipping-documents-widget/remission-open-shipping-document-widget.component.ts +++ b/apps/sales/src/app/modules/remission/components/remission-open-shipping-documents-widget/remission-open-shipping-document-widget.component.ts @@ -4,10 +4,10 @@ import { RemissionSelectors } from 'apps/sales/src/app/core/store/selectors/remi import { RemissionProcess } from '@isa/remission'; import { Observable } from 'rxjs'; import { map, filter } from 'rxjs/operators'; -import { isNullOrUndefined } from 'util'; import { Router } from '@angular/router'; import { AddBreadcrumb } from 'apps/sales/src/app/core/store/actions/breadcrumb.actions'; import { SetBranchProcessCurrentPath } from 'apps/sales/src/app/core/store/actions/branch-process.actions'; +import { isNullOrUndefined } from '@utils/common'; @Component({ selector: 'app-remission-open-shipping-documents-widget', diff --git a/apps/sales/src/app/modules/remission/components/remission-reminder-dialog/remission-reminder-dialog.component.spec.ts b/apps/sales/src/app/modules/remission/components/remission-reminder-dialog/remission-reminder-dialog.component.spec.ts index 84fab5028..01dcdb748 100644 --- a/apps/sales/src/app/modules/remission/components/remission-reminder-dialog/remission-reminder-dialog.component.spec.ts +++ b/apps/sales/src/app/modules/remission/components/remission-reminder-dialog/remission-reminder-dialog.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { RemissionReminderDialogComponent } from './remission-reminder-dialog.component'; @@ -6,11 +6,13 @@ describe('RemissionReminderDialogComponent', () => { let component: RemissionReminderDialogComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [RemissionReminderDialogComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [RemissionReminderDialogComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(RemissionReminderDialogComponent); diff --git a/apps/sales/src/app/modules/remission/components/remission-scan-product-invalid-barcode/remission-scan-product-invalid-barcode.component.spec.ts b/apps/sales/src/app/modules/remission/components/remission-scan-product-invalid-barcode/remission-scan-product-invalid-barcode.component.spec.ts index bfbc9f312..732a9a31e 100644 --- a/apps/sales/src/app/modules/remission/components/remission-scan-product-invalid-barcode/remission-scan-product-invalid-barcode.component.spec.ts +++ b/apps/sales/src/app/modules/remission/components/remission-scan-product-invalid-barcode/remission-scan-product-invalid-barcode.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { RemissionScanProductInvalidBarcodeComponent } from './remission-scan-product-invalid-barcode.component'; import { RemissionClientModule } from '../../remission-client.module'; @@ -6,11 +6,13 @@ describe('RemissionScanProductInvalidBarcodeComponent', () => { let component: RemissionScanProductInvalidBarcodeComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [RemissionClientModule], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [RemissionClientModule], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(RemissionScanProductInvalidBarcodeComponent); diff --git a/apps/sales/src/app/modules/remission/components/remission-scan-shipping-document-closed/remission-scan-shipping-document-closed.component.spec.ts b/apps/sales/src/app/modules/remission/components/remission-scan-shipping-document-closed/remission-scan-shipping-document-closed.component.spec.ts index 513130aed..bef55dc68 100644 --- a/apps/sales/src/app/modules/remission/components/remission-scan-shipping-document-closed/remission-scan-shipping-document-closed.component.spec.ts +++ b/apps/sales/src/app/modules/remission/components/remission-scan-shipping-document-closed/remission-scan-shipping-document-closed.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; // tslint:disable-next-line: max-line-length import { RemissionScanProductInvalidBarcodeComponent } from '../remission-scan-product-invalid-barcode/remission-scan-product-invalid-barcode.component'; import { CommonModule } from '@angular/common'; @@ -8,11 +8,13 @@ describe('RemissionScanProductInvalidBarcodeComponent', () => { let component: RemissionScanProductInvalidBarcodeComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [CommonModule, RemissionClientModule], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [CommonModule, RemissionClientModule], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(RemissionScanProductInvalidBarcodeComponent); diff --git a/apps/sales/src/app/modules/remission/components/remission-selected-filter-items/remission-selected-filter-items.component.spec.ts b/apps/sales/src/app/modules/remission/components/remission-selected-filter-items/remission-selected-filter-items.component.spec.ts index e2662997f..ed45c1dfb 100644 --- a/apps/sales/src/app/modules/remission/components/remission-selected-filter-items/remission-selected-filter-items.component.spec.ts +++ b/apps/sales/src/app/modules/remission/components/remission-selected-filter-items/remission-selected-filter-items.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { RemissionSelectedFilterItemsComponent } from './remission-selected-filter-items.component'; @@ -6,11 +6,13 @@ describe('RemissionSelectedFilterItemsComponent', () => { let component: RemissionSelectedFilterItemsComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [RemissionSelectedFilterItemsComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [RemissionSelectedFilterItemsComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(RemissionSelectedFilterItemsComponent); diff --git a/apps/sales/src/app/modules/remission/components/remission-selected-filter-items/remission-selected-filter-items.component.ts b/apps/sales/src/app/modules/remission/components/remission-selected-filter-items/remission-selected-filter-items.component.ts index 8f214f1a1..ad0a8bcdf 100644 --- a/apps/sales/src/app/modules/remission/components/remission-selected-filter-items/remission-selected-filter-items.component.ts +++ b/apps/sales/src/app/modules/remission/components/remission-selected-filter-items/remission-selected-filter-items.component.ts @@ -5,7 +5,7 @@ import { Select } from '@ngxs/store'; import { RemissionSelectors } from 'apps/sales/src/app/core/store/selectors/remission.selectors'; import { filter, takeUntil, distinctUntilChanged } from 'rxjs/operators'; import { Subject, Observable } from 'rxjs'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@utils/common'; import { RemissionHelperService } from '../../services/remission-helper.service'; @Component({ diff --git a/apps/sales/src/app/modules/remission/components/remission-shipping-document-card/remission-shipping-document-card.component.spec.ts b/apps/sales/src/app/modules/remission/components/remission-shipping-document-card/remission-shipping-document-card.component.spec.ts index 83adc2517..40e8f7102 100644 --- a/apps/sales/src/app/modules/remission/components/remission-shipping-document-card/remission-shipping-document-card.component.spec.ts +++ b/apps/sales/src/app/modules/remission/components/remission-shipping-document-card/remission-shipping-document-card.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { RemissionShippingDocumentCardComponent } from './remission-shipping-document-card.component'; @@ -6,11 +6,13 @@ describe('RemissionShippingDocumentCardComponent', () => { let component: RemissionShippingDocumentCardComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [RemissionShippingDocumentCardComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [RemissionShippingDocumentCardComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(RemissionShippingDocumentCardComponent); diff --git a/apps/sales/src/app/modules/remission/components/remission-shipping-document/remission-shipping-document.component.spec.ts b/apps/sales/src/app/modules/remission/components/remission-shipping-document/remission-shipping-document.component.spec.ts index e6aad9fe0..d27b11e07 100644 --- a/apps/sales/src/app/modules/remission/components/remission-shipping-document/remission-shipping-document.component.spec.ts +++ b/apps/sales/src/app/modules/remission/components/remission-shipping-document/remission-shipping-document.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { RemissionShippingDocumentComponent } from './remission-shipping-document.component'; @@ -6,11 +6,13 @@ describe('RemissionShippingDocumentComponent', () => { let component: RemissionShippingDocumentComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [RemissionShippingDocumentComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [RemissionShippingDocumentComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(RemissionShippingDocumentComponent); diff --git a/apps/sales/src/app/modules/remission/components/remission-shipping-document/remission-shipping-document.component.ts b/apps/sales/src/app/modules/remission/components/remission-shipping-document/remission-shipping-document.component.ts index fa6b1d760..9fa2c7f78 100644 --- a/apps/sales/src/app/modules/remission/components/remission-shipping-document/remission-shipping-document.component.ts +++ b/apps/sales/src/app/modules/remission/components/remission-shipping-document/remission-shipping-document.component.ts @@ -2,7 +2,6 @@ import { Component, OnInit, Input, ViewChild, OnDestroy, Output, EventEmitter } import { Store, Select } from '@ngxs/store'; import { RemissionService, ShippingDocument } from '@isa/remission'; import { take, filter, takeUntil, map } from 'rxjs/operators'; -import { isNullOrUndefined } from 'util'; import { SetRemissionShippingDocument, DeleteRemissionShippingDocument, @@ -18,6 +17,7 @@ import { AddBreadcrumb, ResetBreadcrumbsTo } from 'apps/sales/src/app/core/store import { Router } from '@angular/router'; import { SetBranchProcessCurrentPath } from 'apps/sales/src/app/core/store/actions/branch-process.actions'; import { RemissionFinishingProcessStatus } from '../../models/remission-finishing-process-status.enum'; +import { isNullOrUndefined } from '@utils/common'; @Component({ selector: 'app-remission-shipping-document', diff --git a/apps/sales/src/app/modules/remission/components/remission-start-dialog/remission-start-dialog.component.spec.ts b/apps/sales/src/app/modules/remission/components/remission-start-dialog/remission-start-dialog.component.spec.ts index c0c02875b..9af2a3469 100644 --- a/apps/sales/src/app/modules/remission/components/remission-start-dialog/remission-start-dialog.component.spec.ts +++ b/apps/sales/src/app/modules/remission/components/remission-start-dialog/remission-start-dialog.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { RemissionStartDialogComponent } from './remission-start-dialog.component'; @@ -6,11 +6,13 @@ describe('RemissionStartDialogComponent', () => { let component: RemissionStartDialogComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [RemissionStartDialogComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [RemissionStartDialogComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(RemissionStartDialogComponent); diff --git a/apps/sales/src/app/modules/remission/components/remission-to-top-to-bottom-actions/remission-to-top-to-bottom-actions.component.spec.ts b/apps/sales/src/app/modules/remission/components/remission-to-top-to-bottom-actions/remission-to-top-to-bottom-actions.component.spec.ts index 149cc4762..0d8ffbf0f 100644 --- a/apps/sales/src/app/modules/remission/components/remission-to-top-to-bottom-actions/remission-to-top-to-bottom-actions.component.spec.ts +++ b/apps/sales/src/app/modules/remission/components/remission-to-top-to-bottom-actions/remission-to-top-to-bottom-actions.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { RemissionToTopToBottomActionsComponent } from './remission-to-top-to-bottom-actions.component'; @@ -6,11 +6,13 @@ describe('RemissionToTopToBottomActionsComponent', () => { let component: RemissionToTopToBottomActionsComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [RemissionToTopToBottomActionsComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [RemissionToTopToBottomActionsComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(RemissionToTopToBottomActionsComponent); diff --git a/apps/sales/src/app/modules/remission/overlays/remission-list-filter/remission-list-filter.component.ts b/apps/sales/src/app/modules/remission/overlays/remission-list-filter/remission-list-filter.component.ts index 36fad8227..71a58f3b4 100644 --- a/apps/sales/src/app/modules/remission/overlays/remission-list-filter/remission-list-filter.component.ts +++ b/apps/sales/src/app/modules/remission/overlays/remission-list-filter/remission-list-filter.component.ts @@ -8,8 +8,8 @@ import { RemissionFilterService } from '../../services/remission-filter.service' import { Filter, SelectFilter, SelectFilterOption } from '../../../filter'; import { RemissionResourceType } from '../../models/remission-resource-type.model'; import { withLatestFrom, filter, switchMap, map, throttleTime, startWith } from 'rxjs/operators'; -import { isNullOrUndefined } from 'util'; import { slideIn } from 'apps/sales/src/app/core/overlay'; +import { isNullOrUndefined } from '@utils/common'; @Component({ selector: 'app-remission-list-filter', diff --git a/apps/sales/src/app/modules/remission/pages/remission-add-product-to-remission-list/remission-add-product-to-remission-list.component.spec.ts b/apps/sales/src/app/modules/remission/pages/remission-add-product-to-remission-list/remission-add-product-to-remission-list.component.spec.ts index b6fe4ab8b..a1f6a665d 100644 --- a/apps/sales/src/app/modules/remission/pages/remission-add-product-to-remission-list/remission-add-product-to-remission-list.component.spec.ts +++ b/apps/sales/src/app/modules/remission/pages/remission-add-product-to-remission-list/remission-add-product-to-remission-list.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { RemissionAddProductToRemissionListComponent } from './remission-add-product-to-remission-list.component'; @@ -6,11 +6,13 @@ describe('RemissionAddProductToRemissionListComponent', () => { let component: RemissionAddProductToRemissionListComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [RemissionAddProductToRemissionListComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [RemissionAddProductToRemissionListComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(RemissionAddProductToRemissionListComponent); diff --git a/apps/sales/src/app/modules/remission/pages/remission-add-product-to-remission-list/remission-add-product-to-remission-list.component.ts b/apps/sales/src/app/modules/remission/pages/remission-add-product-to-remission-list/remission-add-product-to-remission-list.component.ts index 77a7c7d04..8f1a38b65 100644 --- a/apps/sales/src/app/modules/remission/pages/remission-add-product-to-remission-list/remission-add-product-to-remission-list.component.ts +++ b/apps/sales/src/app/modules/remission/pages/remission-add-product-to-remission-list/remission-add-product-to-remission-list.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit, ViewChild, ElementRef, AfterViewInit, ViewEncapsulation } from '@angular/core'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@utils/common'; import { Store } from '@ngxs/store'; import { RemissionService, RemissionProduct } from '@isa/remission'; import { take } from 'rxjs/operators'; diff --git a/apps/sales/src/app/modules/remission/pages/remission-details/remission-details.component.ts b/apps/sales/src/app/modules/remission/pages/remission-details/remission-details.component.ts index ceeb5330d..eb04a6fab 100644 --- a/apps/sales/src/app/modules/remission/pages/remission-details/remission-details.component.ts +++ b/apps/sales/src/app/modules/remission/pages/remission-details/remission-details.component.ts @@ -4,7 +4,6 @@ import { Observable, Subject, BehaviorSubject, of, combineLatest } from 'rxjs'; import { map, flatMap, takeUntil, filter, tap, catchError, take, withLatestFrom, switchMap } from 'rxjs/operators'; import { Store } from '@ngxs/store'; import { RemissionSelectors } from 'apps/sales/src/app/core/store/selectors/remission.selectors'; -import { isNullOrUndefined } from 'util'; import { SetAllExistingRemissions, SetRemissionProcess, @@ -21,6 +20,7 @@ import { Breadcrumb } from 'apps/sales/src/app/core/models/breadcrumb.model'; import { AppService } from '@sales/core-services'; import { RemissionContainerScannerScanditComponent } from 'shared/public_api'; import { RemissionProcess, RemissionService, RemissionSupplier, ShippingDocument } from '@isa/remission'; +import { isNullOrUndefined } from '@utils/common'; @Component({ selector: 'app-remission-details', diff --git a/apps/sales/src/app/modules/remission/pages/remission-finish/remission-finish.component.spec.ts b/apps/sales/src/app/modules/remission/pages/remission-finish/remission-finish.component.spec.ts index 46780720c..c9d11d62e 100644 --- a/apps/sales/src/app/modules/remission/pages/remission-finish/remission-finish.component.spec.ts +++ b/apps/sales/src/app/modules/remission/pages/remission-finish/remission-finish.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { RemissionFinishComponent } from './remission-finish.component'; import { RemissionClientModule } from '../../remission-client.module'; @@ -19,31 +19,33 @@ describe('RemissionFinishComponent', () => { const confirmationModal = jasmine.createSpyObj('RemissionScanConfirmationDialogComponent', ['openDialog']); const invalidBarcodeModal = jasmine.createSpyObj('RemissionScanProductInvalidBarcodeComponent', { ['openDialog']: of({}) }); - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [ - RemissionClientModule, - HttpClientTestingModule, - NgxsModule.forRoot(), - RouterModule.forRoot([]), - OAuthModule.forRoot(), - SsoModule.forRoot(), - NgIdleModule.forRoot(), - ], - providers: [ - RemissionService, - { - provide: AppConfiguration, - useValue: {} as AppConfiguration, - }, - ], - }).compileComponents(); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [ + RemissionClientModule, + HttpClientTestingModule, + NgxsModule.forRoot(), + RouterModule.forRoot([], { relativeLinkResolution: 'legacy' }), + OAuthModule.forRoot(), + SsoModule.forRoot(), + NgIdleModule.forRoot(), + ], + providers: [ + RemissionService, + { + provide: AppConfiguration, + useValue: {} as AppConfiguration, + }, + ], + }).compileComponents(); - fixture = TestBed.createComponent(RemissionFinishComponent); + fixture = TestBed.createComponent(RemissionFinishComponent); - component = fixture.componentInstance; - element = fixture.nativeElement; - })); + component = fixture.componentInstance; + element = fixture.nativeElement; + }) + ); it('should open the confirmation dialog after scanning the barcode', () => { spyOn(component, 'handleScanResult').and.callThrough(); diff --git a/apps/sales/src/app/modules/remission/pages/remission-finish/remission-finish.component.ts b/apps/sales/src/app/modules/remission/pages/remission-finish/remission-finish.component.ts index 7c2d2ff0b..72c8e99bb 100644 --- a/apps/sales/src/app/modules/remission/pages/remission-finish/remission-finish.component.ts +++ b/apps/sales/src/app/modules/remission/pages/remission-finish/remission-finish.component.ts @@ -4,7 +4,7 @@ import { Store, Select } from '@ngxs/store'; import { RemissionSelectors } from 'apps/sales/src/app/core/store/selectors/remission.selectors'; import { RemissionSupplier, RemissionProcess, RemissionService, ShippingDocument, Printer } from '@isa/remission'; import { filter, takeUntil, take, switchMap, map, tap, catchError } from 'rxjs/operators'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@utils/common'; import { Subject, Observable, merge, of, NEVER } from 'rxjs'; import { SUPPLIER_PLACEHOLDER } from '../../constants/remission.constants'; import { AddBreadcrumb, ResetBreadcrumbsTo } from 'apps/sales/src/app/core/store/actions/breadcrumb.actions'; diff --git a/apps/sales/src/app/modules/remission/pages/remission-list-create/product-list/product-list.component.ts b/apps/sales/src/app/modules/remission/pages/remission-list-create/product-list/product-list.component.ts index 842521342..6d8d77e31 100644 --- a/apps/sales/src/app/modules/remission/pages/remission-list-create/product-list/product-list.component.ts +++ b/apps/sales/src/app/modules/remission/pages/remission-list-create/product-list/product-list.component.ts @@ -18,7 +18,7 @@ import { RemissionSelectors } from 'apps/sales/src/app/core/store/selectors/remi import { map, switchMap, filter, catchError, takeUntil, take, distinctUntilChanged, debounceTime, tap, skip } from 'rxjs/operators'; import { RemissionListComponent } from '../../../components/remission-list'; import { RemissionResourceType } from '../../../models/remission-resource-type.model'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@utils/common'; import { RemissionProcessStatuses } from 'apps/sales/src/app/core/models/remission-process-statuses.model'; import { RemissionToTopToBottomActionsComponent } from '../../../components/remission-to-top-to-bottom-actions'; import { UpdateShippingDocuent, SetRemissionActiveView } from 'apps/sales/src/app/core/store/actions/remission.actions'; diff --git a/apps/sales/src/app/modules/remission/pages/remission-list-create/remission-list-create.component.ts b/apps/sales/src/app/modules/remission/pages/remission-list-create/remission-list-create.component.ts index 89e4248c9..831034461 100644 --- a/apps/sales/src/app/modules/remission/pages/remission-list-create/remission-list-create.component.ts +++ b/apps/sales/src/app/modules/remission/pages/remission-list-create/remission-list-create.component.ts @@ -7,7 +7,6 @@ import { Select, Store } from '@ngxs/store'; import { RemissionSelectors } from 'apps/sales/src/app/core/store/selectors/remission.selectors'; import { RemissionProcessStatuses } from 'apps/sales/src/app/core/models/remission-process-statuses.model'; import { RESOURCE_TYPE_SWITCH } from '../../constants/remission.constants'; -import { isNullOrUndefined } from 'util'; import { SetRemissionProcess, SetRemissionCreated, @@ -44,6 +43,7 @@ import { RemissionSupplier, } from '@isa/remission'; import { SupplierDTO } from '@swagger/remi'; +import { isNullOrUndefined } from '@utils/common'; @Component({ selector: 'app-remission-list-create', diff --git a/apps/sales/src/app/modules/remission/pages/remission-list-create/ueberlauf-capacities/ueberlauf-capacities.component.ts b/apps/sales/src/app/modules/remission/pages/remission-list-create/ueberlauf-capacities/ueberlauf-capacities.component.ts index 3eae77a37..c9a7df1f5 100644 --- a/apps/sales/src/app/modules/remission/pages/remission-list-create/ueberlauf-capacities/ueberlauf-capacities.component.ts +++ b/apps/sales/src/app/modules/remission/pages/remission-list-create/ueberlauf-capacities/ueberlauf-capacities.component.ts @@ -4,7 +4,7 @@ import { Observable } from 'rxjs'; import { RemissionSelectors } from 'apps/sales/src/app/core/store/selectors/remission.selectors'; import { CapacityType } from '@isa/remission'; import { map } from 'rxjs/operators'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@utils/common'; @Component({ selector: 'app-remission-uberlauf-capacities', diff --git a/apps/sales/src/app/modules/remission/pages/remission-list-started/remission-list-started.component.spec.ts b/apps/sales/src/app/modules/remission/pages/remission-list-started/remission-list-started.component.spec.ts index 7beb73cfb..3fae59191 100644 --- a/apps/sales/src/app/modules/remission/pages/remission-list-started/remission-list-started.component.spec.ts +++ b/apps/sales/src/app/modules/remission/pages/remission-list-started/remission-list-started.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { RemissionListStartedComponent } from './remission-list-started.component'; @@ -6,11 +6,13 @@ describe('RemissionListStartedComponent', () => { let component: RemissionListStartedComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [RemissionListStartedComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [RemissionListStartedComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(RemissionListStartedComponent); diff --git a/apps/sales/src/app/modules/remission/pages/remission-list-started/remission-list-started.component.ts b/apps/sales/src/app/modules/remission/pages/remission-list-started/remission-list-started.component.ts index 18421589e..720dff15b 100644 --- a/apps/sales/src/app/modules/remission/pages/remission-list-started/remission-list-started.component.ts +++ b/apps/sales/src/app/modules/remission/pages/remission-list-started/remission-list-started.component.ts @@ -20,7 +20,7 @@ import { Select, Store } from '@ngxs/store'; import { RemissionSelectors } from 'apps/sales/src/app/core/store/selectors/remission.selectors'; import { RemissionProcessStatuses } from 'apps/sales/src/app/core/models/remission-process-statuses.model'; import { RESOURCE_TYPE_SPECIFIC_MODEL_OPTIONS, RESOURCE_TYPE_SWITCH } from '../../constants/remission.constants'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@utils/common'; import { SetRemissionProcess, SetRemissionCreated, diff --git a/apps/sales/src/app/modules/remission/pages/remission-list-started/shipping-document-container/shipping-document-container.component.ts b/apps/sales/src/app/modules/remission/pages/remission-list-started/shipping-document-container/shipping-document-container.component.ts index 978ce34a0..af71a51c4 100644 --- a/apps/sales/src/app/modules/remission/pages/remission-list-started/shipping-document-container/shipping-document-container.component.ts +++ b/apps/sales/src/app/modules/remission/pages/remission-list-started/shipping-document-container/shipping-document-container.component.ts @@ -1,14 +1,13 @@ import { Component, ChangeDetectionStrategy, AfterViewInit, ElementRef, ViewChild, Inject, Renderer2 } from '@angular/core'; import { Select, Store } from '@ngxs/store'; import { RemissionSelectors } from 'apps/sales/src/app/core/store/selectors/remission.selectors'; -import { Observable, Subject } from 'rxjs'; +import { Observable } from 'rxjs'; import { ShippingDocument, RemissionProcess } from '@isa/remission'; import { RemissionTargetType } from '../../../models/remission-resource-type.model'; import { filter, map, take } from 'rxjs/operators'; -import { isNullOrUndefined } from 'util'; import { UpdateShippingDocuent } from 'apps/sales/src/app/core/store/actions/remission.actions'; -import { RemissionActiveView } from '../../../models'; import { DOCUMENT } from '@angular/common'; +import { isNullOrUndefined } from '@utils/common'; @Component({ selector: 'app-remission-shipping-document-container', diff --git a/apps/sales/src/app/modules/remission/pages/remission-list-started/shipping-document-creation/shipping-document-creation.component.ts b/apps/sales/src/app/modules/remission/pages/remission-list-started/shipping-document-creation/shipping-document-creation.component.ts index 6e54334c1..a5750f0a5 100644 --- a/apps/sales/src/app/modules/remission/pages/remission-list-started/shipping-document-creation/shipping-document-creation.component.ts +++ b/apps/sales/src/app/modules/remission/pages/remission-list-started/shipping-document-creation/shipping-document-creation.component.ts @@ -4,7 +4,7 @@ import { RemissionSelectors } from 'apps/sales/src/app/core/store/selectors/remi import { combineLatest, Observable, of } from 'rxjs'; import { filter, catchError, map, switchMap, take, first } from 'rxjs/operators'; import { RemissionScanProductInvalidBarcodeComponent } from '../../../components/remission-scan-product-invalid-barcode'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@utils/common'; import { AppService } from '@sales/core-services'; import { RemissionService, ShippingDocument, RemissionProcess } from '@isa/remission'; import { SetRemissionShippingDocument } from 'apps/sales/src/app/core/store/actions/remission.actions'; diff --git a/apps/sales/src/app/modules/remission/pages/remissions-overview/remissions-overview.component.ts b/apps/sales/src/app/modules/remission/pages/remissions-overview/remissions-overview.component.ts index d8898b48f..9958569bb 100644 --- a/apps/sales/src/app/modules/remission/pages/remissions-overview/remissions-overview.component.ts +++ b/apps/sales/src/app/modules/remission/pages/remissions-overview/remissions-overview.component.ts @@ -1,13 +1,8 @@ -import { - Component, - OnInit, - ChangeDetectionStrategy, - OnDestroy, -} from '@angular/core'; +import { Component, OnInit, ChangeDetectionStrategy, OnDestroy } from '@angular/core'; import { Observable, Subject, BehaviorSubject } from 'rxjs'; import { SetAllExistingRemissions } from 'apps/sales/src/app/core/store/actions/remission.actions'; import { Store, Select } from '@ngxs/store'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@utils/common'; import { filter, takeUntil, map, take } from 'rxjs/operators'; import { RemissionSelectors } from 'apps/sales/src/app/core/store/selectors/remission.selectors'; import { Router } from '@angular/router'; @@ -51,36 +46,22 @@ export class RemissionsOverviewComponent implements OnInit, OnDestroy { this.allRemissions$ .pipe( takeUntil(this.destroy$), - map((allRemissions) => - allRemissions.find( - (remission) => remission.externalId === externalRemissionId - ) - ), + map((allRemissions) => allRemissions.find((remission) => remission.externalId === externalRemissionId)), take(1) ) .subscribe(this.openDetailsPage); } private getShippingDocument(remission: RemissionProcess) { - return ( - (remission.shippingDocuments && - remission.shippingDocuments.find((sd) => !sd.isCompleted)) || - remission.shippingDocuments[0] - ); + return (remission.shippingDocuments && remission.shippingDocuments.find((sd) => !sd.isCompleted)) || remission.shippingDocuments[0]; } private openDetailsPage = (remissionProcess: RemissionProcess) => { const shippingDocumentId = this.getShippingDocumentId(remissionProcess); - const shippingDocumentNumber = this.getShippingDocumentNumber( - remissionProcess - ); + const shippingDocumentNumber = this.getShippingDocumentNumber(remissionProcess); if (!shippingDocumentId) { - this.errorService.addErrors( - 400, - 'Für den Vorgang existiert keine Warenbegleischein ID', - 'Warenbegleitschein ID unbekannt' - ); + this.errorService.addErrors(400, 'Für den Vorgang existiert keine Warenbegleischein ID', 'Warenbegleitschein ID unbekannt'); } const path = `remission/overview/${shippingDocumentId}`; @@ -88,9 +69,7 @@ export class RemissionsOverviewComponent implements OnInit, OnDestroy { new AddBreadcrumb( { path: path, - name: `Warenbegleitschein ${this.getFormattedShippingDocumentNumber( - shippingDocumentNumber - )}`, + name: `Warenbegleitschein ${this.getFormattedShippingDocumentNumber(shippingDocumentNumber)}`, }, 'remission' ) @@ -111,19 +90,14 @@ export class RemissionsOverviewComponent implements OnInit, OnDestroy { takeUntil(this.destroy$), filter((remissionProcesses) => !isNullOrUndefined(remissionProcesses)) ) - .subscribe( - this.allRemissionsSubscriptionHandler, - this.handleRemissionLoadingError - ); + .subscribe(this.allRemissionsSubscriptionHandler, this.handleRemissionLoadingError); } private handleRemissionLoadingError = (err: any) => { this.isLoading.next(false); }; - private allRemissionsSubscriptionHandler = ( - remissionProcesses: RemissionProcess[] - ) => { + private allRemissionsSubscriptionHandler = (remissionProcesses: RemissionProcess[]) => { this.store.dispatch(new SetAllExistingRemissions(remissionProcesses)); this.isLoading.next(false); }; @@ -144,14 +118,10 @@ export class RemissionsOverviewComponent implements OnInit, OnDestroy { return this.getShippingDocument(remissionProcess).shippingDocumentNumber; }; - private getFormattedShippingDocumentNumber( - shippingDocumentNumber: string - ): string { + private getFormattedShippingDocumentNumber(shippingDocumentNumber: string): string { const numberFormatter = new ShippingDocumentNumberFormatterPipe(); const numberParser = new PackageNumberParserPipe(); - return numberParser.transform( - numberFormatter.transform(shippingDocumentNumber) - ); + return numberParser.transform(numberFormatter.transform(shippingDocumentNumber)); } } diff --git a/apps/sales/src/app/modules/remission/services/remission-filter.service.ts b/apps/sales/src/app/modules/remission/services/remission-filter.service.ts index 80da5fa10..d06f9e4f5 100644 --- a/apps/sales/src/app/modules/remission/services/remission-filter.service.ts +++ b/apps/sales/src/app/modules/remission/services/remission-filter.service.ts @@ -5,7 +5,7 @@ import { RemissionSelectors } from '../../../core/store/selectors/remission.sele import { RemissionSelectedFilters, RemissionProcess, RemissionService, RemissionFilter } from '@isa/remission'; import { Select, Store } from '@ngxs/store'; import { filter, take, switchMap, withLatestFrom, map, tap, startWith, debounceTime } from 'rxjs/operators'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@utils/common'; import { RequestUpdateRemissionFilter } from '../../../core/store/actions/remission.actions'; import { flatten } from '../../../shared/utils'; import { RemissionResourceType } from '../models'; diff --git a/apps/sales/src/app/modules/shelf/components/article-details/article-details.component.ts b/apps/sales/src/app/modules/shelf/components/article-details/article-details.component.ts index d87d2a4ee..e0b12c2b9 100644 --- a/apps/sales/src/app/modules/shelf/components/article-details/article-details.component.ts +++ b/apps/sales/src/app/modules/shelf/components/article-details/article-details.component.ts @@ -4,7 +4,7 @@ import { Subject } from 'rxjs'; import { OrderItemDTO, EnvironmentChannel, LogisticianDTO, ReceiptListItemDTO } from '@swagger/oms'; import { orderChannelMapper, receiptType, OrderStatus } from 'apps/sales/src/app/core/mappings/shelf.mapping'; import { BranchSelectors } from 'apps/sales/src/app/core/store/selectors/branch.selector'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@utils/common'; import { CollectingShelfService } from 'apps/sales/src/app/core/services/collecting-shelf.service'; import { DatePipe } from '@angular/common'; import { SupplierState } from 'apps/sales/src/app/core/store/state/supplier.state'; diff --git a/apps/sales/src/app/modules/shelf/components/order-item-edit/order-item-edit.component.spec.ts b/apps/sales/src/app/modules/shelf/components/order-item-edit/order-item-edit.component.spec.ts index fb0bbd58e..b0bd6df75 100644 --- a/apps/sales/src/app/modules/shelf/components/order-item-edit/order-item-edit.component.spec.ts +++ b/apps/sales/src/app/modules/shelf/components/order-item-edit/order-item-edit.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { OrderItemEditComponent } from './order-item-edit.component'; @@ -6,11 +6,13 @@ describe('OrderItemEditComponent', () => { let component: OrderItemEditComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [OrderItemEditComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [OrderItemEditComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(OrderItemEditComponent); diff --git a/apps/sales/src/app/modules/shelf/components/order-loading/order-loading.component.spec.ts b/apps/sales/src/app/modules/shelf/components/order-loading/order-loading.component.spec.ts index 0d41606da..7321d9cf6 100644 --- a/apps/sales/src/app/modules/shelf/components/order-loading/order-loading.component.spec.ts +++ b/apps/sales/src/app/modules/shelf/components/order-loading/order-loading.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { OrderLoadingComponent } from './order-loading.component'; @@ -6,11 +6,13 @@ describe('OrderLoadingComponent', () => { let component: OrderLoadingComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [OrderLoadingComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [OrderLoadingComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(OrderLoadingComponent); diff --git a/apps/sales/src/app/modules/shelf/components/order-overview-edit/order-overview-edit.component.spec.ts b/apps/sales/src/app/modules/shelf/components/order-overview-edit/order-overview-edit.component.spec.ts index 89b430498..8abbdbad8 100644 --- a/apps/sales/src/app/modules/shelf/components/order-overview-edit/order-overview-edit.component.spec.ts +++ b/apps/sales/src/app/modules/shelf/components/order-overview-edit/order-overview-edit.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { OrderOverviewEditComponent } from './order-overview-edit.component'; @@ -6,11 +6,13 @@ describe('OrderOverviewEditComponent', () => { let component: OrderOverviewEditComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [OrderOverviewEditComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [OrderOverviewEditComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(OrderOverviewEditComponent); diff --git a/apps/sales/src/app/modules/shelf/components/searchbar/searchbar.component.ts b/apps/sales/src/app/modules/shelf/components/searchbar/searchbar.component.ts index 82a252b02..e8fce00ba 100644 --- a/apps/sales/src/app/modules/shelf/components/searchbar/searchbar.component.ts +++ b/apps/sales/src/app/modules/shelf/components/searchbar/searchbar.component.ts @@ -1,6 +1,6 @@ import { Component, Input, OnInit, OnDestroy, Output, EventEmitter, ChangeDetectionStrategy, ViewChild, ElementRef } from '@angular/core'; import { Subject, Observable } from 'rxjs'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@utils/common'; import { FormControl } from '@angular/forms'; @Component({ diff --git a/apps/sales/src/app/modules/shelf/overlays/shelf-filter/shelf-filter.component.ts b/apps/sales/src/app/modules/shelf/overlays/shelf-filter/shelf-filter.component.ts index 646093b50..d6eb2a454 100644 --- a/apps/sales/src/app/modules/shelf/overlays/shelf-filter/shelf-filter.component.ts +++ b/apps/sales/src/app/modules/shelf/overlays/shelf-filter/shelf-filter.component.ts @@ -5,7 +5,7 @@ import { Observable, interval, Subject } from 'rxjs'; import { Filter, SelectFilter } from '../../../filter'; import { ShelfFilterService } from '../../services/shelf-filter.service'; import { map, take, takeUntil } from 'rxjs/operators'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@utils/common'; import { ShelfSearchInputComponent } from '../../pages/shelf-search/search'; import { cloneFilter } from '../../../filter/utils'; import { FilterResetStrategy } from '../../../filter/selected-filter-options/components/defs'; diff --git a/apps/sales/src/app/modules/shelf/pages/shelf-edit-compartment/shelf-edit-compartment.component.ts b/apps/sales/src/app/modules/shelf/pages/shelf-edit-compartment/shelf-edit-compartment.component.ts index 7e6a51aab..83b9604a5 100644 --- a/apps/sales/src/app/modules/shelf/pages/shelf-edit-compartment/shelf-edit-compartment.component.ts +++ b/apps/sales/src/app/modules/shelf/pages/shelf-edit-compartment/shelf-edit-compartment.component.ts @@ -2,7 +2,7 @@ import { Component, OnInit, ChangeDetectionStrategy, ChangeDetectorRef } from '@ import { ActivatedRoute } from '@angular/router'; import { Observable } from 'rxjs'; import { filter, map, distinctUntilChanged, shareReplay, take, withLatestFrom, first } from 'rxjs/operators'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@utils/common'; import { ShelfEditFormService } from '../../services'; import { FormGroup, FormArray } from '@angular/forms'; import { ProcessingStatusPipe, PickUpDateOptionsToDisplayValuesPipe, ProcessingStatusOptionsPipe } from '../../pipes'; diff --git a/apps/sales/src/app/modules/shelf/pages/shelf-edit-order-from-customer/shelf-edit-order-from-customer.component.ts b/apps/sales/src/app/modules/shelf/pages/shelf-edit-order-from-customer/shelf-edit-order-from-customer.component.ts index e3b14b7fe..6b4d4b38f 100644 --- a/apps/sales/src/app/modules/shelf/pages/shelf-edit-order-from-customer/shelf-edit-order-from-customer.component.ts +++ b/apps/sales/src/app/modules/shelf/pages/shelf-edit-order-from-customer/shelf-edit-order-from-customer.component.ts @@ -4,7 +4,7 @@ import { CollectingShelfSelectors } from 'apps/sales/src/app/core/store/selector import { Observable, Subject } from 'rxjs'; import { OrderDTO } from '@swagger/oms'; import { takeUntil, filter, take, map } from 'rxjs/operators'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@utils/common'; import { Router, ActivatedRoute, Params } from '@angular/router'; import { ChangeCurrentRoute } from 'apps/sales/src/app/core/store/actions/process.actions'; import { PopBreadcrumbsAfterCurrent } from 'apps/sales/src/app/core/store/actions/breadcrumb.actions'; diff --git a/apps/sales/src/app/modules/shelf/pages/shelf-edit-order/shelf-edit-order.component.spec.ts b/apps/sales/src/app/modules/shelf/pages/shelf-edit-order/shelf-edit-order.component.spec.ts index 1ccf1eed3..5f9c1b331 100644 --- a/apps/sales/src/app/modules/shelf/pages/shelf-edit-order/shelf-edit-order.component.spec.ts +++ b/apps/sales/src/app/modules/shelf/pages/shelf-edit-order/shelf-edit-order.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { ShelfEditOrderComponent } from './shelf-edit-order.component'; @@ -6,11 +6,13 @@ describe('ShelfEditOrderComponent', () => { let component: ShelfEditOrderComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ShelfEditOrderComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [ShelfEditOrderComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(ShelfEditOrderComponent); diff --git a/apps/sales/src/app/modules/shelf/pages/shelf-edit-order/shelf-edit-order.component.ts b/apps/sales/src/app/modules/shelf/pages/shelf-edit-order/shelf-edit-order.component.ts index e01da22b8..293460ca2 100644 --- a/apps/sales/src/app/modules/shelf/pages/shelf-edit-order/shelf-edit-order.component.ts +++ b/apps/sales/src/app/modules/shelf/pages/shelf-edit-order/shelf-edit-order.component.ts @@ -3,7 +3,7 @@ import { ActivatedRoute } from '@angular/router'; import { FormGroup, FormArray } from '@angular/forms'; import { ShelfNavigationService } from '../../shared/services'; import { filter, map, distinctUntilChanged, shareReplay, take, withLatestFrom, first } from 'rxjs/operators'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@utils/common'; import { DatePipe } from '@angular/common'; import { OrderItemProcessingStatusValue } from '@swagger/oms'; import { ShelfEditFormService } from '../../services/shelf-edit-form.service'; diff --git a/apps/sales/src/app/modules/shelf/pages/shelf-history/shelf-history.component.ts b/apps/sales/src/app/modules/shelf/pages/shelf-history/shelf-history.component.ts index 5010cc304..0e5f5563e 100644 --- a/apps/sales/src/app/modules/shelf/pages/shelf-history/shelf-history.component.ts +++ b/apps/sales/src/app/modules/shelf/pages/shelf-history/shelf-history.component.ts @@ -2,7 +2,7 @@ import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Observable } from 'rxjs'; import { filter, map, switchMap, distinctUntilChanged, tap, shareReplay } from 'rxjs/operators'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@utils/common'; import { HistoryStateFacade } from '@shelf-store/history'; import { OrderHistoryStatus } from '@shelf-store/defs'; import { ShelfNavigationService } from '../../shared/services'; diff --git a/apps/sales/src/app/modules/shelf/pages/shelf-order-details/customer-features/customer-features.component.ts b/apps/sales/src/app/modules/shelf/pages/shelf-order-details/customer-features/customer-features.component.ts index 463d990e0..9e9322bc2 100644 --- a/apps/sales/src/app/modules/shelf/pages/shelf-order-details/customer-features/customer-features.component.ts +++ b/apps/sales/src/app/modules/shelf/pages/shelf-order-details/customer-features/customer-features.component.ts @@ -2,7 +2,7 @@ import { Component, ChangeDetectionStrategy, Input } from '@angular/core'; import { CustomerService } from '@swagger/crm'; import { ReplaySubject, NEVER, BehaviorSubject } from 'rxjs'; import { switchMap, timeout, catchError, map, filter, throttleTime, distinctUntilChanged } from 'rxjs/operators'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@utils/common'; @Component({ selector: 'app-shelf-details-customer-features', diff --git a/apps/sales/src/app/modules/shelf/pages/shelf-order-details/shelf-order-details.component.ts b/apps/sales/src/app/modules/shelf/pages/shelf-order-details/shelf-order-details.component.ts index 49a91f730..87769692f 100644 --- a/apps/sales/src/app/modules/shelf/pages/shelf-order-details/shelf-order-details.component.ts +++ b/apps/sales/src/app/modules/shelf/pages/shelf-order-details/shelf-order-details.component.ts @@ -31,7 +31,7 @@ import { ShelfShippingNoteService, } from '../../services'; import { ShelfOrderDetailsShelfTagsComponent } from './shelf-tags/shelftags.component'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@utils/common'; import { Store } from '@ngxs/store'; import { ResetBreadcrumbsTo } from '../../../../core/store/actions/breadcrumb.actions'; import { Breadcrumb } from '../../../../core/models/breadcrumb.model'; diff --git a/apps/sales/src/app/modules/shelf/pages/shelf-search/search/search-input.component.ts b/apps/sales/src/app/modules/shelf/pages/shelf-search/search/search-input.component.ts index d340ce879..ff49c8f66 100644 --- a/apps/sales/src/app/modules/shelf/pages/shelf-search/search/search-input.component.ts +++ b/apps/sales/src/app/modules/shelf/pages/shelf-search/search/search-input.component.ts @@ -19,12 +19,12 @@ import { ShelfSearchbarComponent } from '../../../components'; import { ShelfFilterService } from '../../../services/shelf-filter.service'; import { SearchStateFacade } from '@shelf-store'; import { AutocompleteOptions } from '../../../defs'; -import { isNullOrUndefined, isString } from 'util'; import { ProcessSelectors } from 'apps/sales/src/app/core/store/selectors/process.selectors'; import { Select } from '@ngxs/store'; import { ScrollPositionService } from 'apps/sales/src/app/core/services/scroll-position.service'; import { ORDER_DETAILS_PREFIX } from '../../shelf-search-results/order-details-prefix'; import { NativeContainerService } from 'native-container'; +import { isNullOrUndefined, isString } from '@utils/common'; @Component({ selector: 'app-shelf-search-input', diff --git a/apps/sales/src/app/modules/shelf/pipes/show-compartment-code.pipe.ts b/apps/sales/src/app/modules/shelf/pipes/show-compartment-code.pipe.ts index ddd8f4913..7a0f1e720 100644 --- a/apps/sales/src/app/modules/shelf/pipes/show-compartment-code.pipe.ts +++ b/apps/sales/src/app/modules/shelf/pipes/show-compartment-code.pipe.ts @@ -1,6 +1,6 @@ import { Pipe, PipeTransform } from '@angular/core'; import { OrderItemListItemDTO } from '@swagger/oms'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@utils/common'; @Pipe({ name: 'showCompartmentCode', diff --git a/apps/sales/src/app/modules/shelf/services/shelf-edit-form.service.ts b/apps/sales/src/app/modules/shelf/services/shelf-edit-form.service.ts index d418bd9eb..d0501d24d 100644 --- a/apps/sales/src/app/modules/shelf/services/shelf-edit-form.service.ts +++ b/apps/sales/src/app/modules/shelf/services/shelf-edit-form.service.ts @@ -15,7 +15,7 @@ import { } from '@swagger/oms'; import { DetailsFacade } from '@shelf-store/details'; import { take, filter, map, shareReplay } from 'rxjs/operators'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@utils/common'; import { EnvironmentChannelPipe } from '../pipes/environment-channel.pipe'; import { DatePipe } from '@angular/common'; import { ProcessingStatusNameMap } from '../constants'; diff --git a/apps/sales/src/app/modules/shelf/services/shelf-filter.service.ts b/apps/sales/src/app/modules/shelf/services/shelf-filter.service.ts index ed8505c31..295dd26c7 100644 --- a/apps/sales/src/app/modules/shelf/services/shelf-filter.service.ts +++ b/apps/sales/src/app/modules/shelf/services/shelf-filter.service.ts @@ -4,7 +4,7 @@ import { SelectFilter, Filter, SelectFilterOption } from '../../filter'; import { startWith, map, filter, takeUntil, take, distinctUntilChanged } from 'rxjs/operators'; import { SearchStateFacade } from '../../../store/customer'; import { flatten } from '../../../shared/utils'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@utils/common'; import { cloneFilter } from '../../filter/utils'; @Injectable({ providedIn: 'root' }) diff --git a/apps/sales/src/app/pages/content/content.component.spec.ts b/apps/sales/src/app/pages/content/content.component.spec.ts index 74b7a29c5..d7a01a176 100644 --- a/apps/sales/src/app/pages/content/content.component.spec.ts +++ b/apps/sales/src/app/pages/content/content.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { ContentPageComponent } from './content.component'; @@ -6,11 +6,13 @@ describe('ContentComponent', () => { let component: ContentPageComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ContentPageComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [ContentPageComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(ContentPageComponent); diff --git a/apps/sales/src/app/shared/components/back-arrow/back-arrow.component.spec.ts b/apps/sales/src/app/shared/components/back-arrow/back-arrow.component.spec.ts index 57d70ebf2..16d0a1166 100644 --- a/apps/sales/src/app/shared/components/back-arrow/back-arrow.component.spec.ts +++ b/apps/sales/src/app/shared/components/back-arrow/back-arrow.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { BackArrowComponent } from './back-arrow.component'; @@ -6,11 +6,13 @@ describe('BackArrowComponent', () => { let component: BackArrowComponent; let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [BackArrowComponent], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [BackArrowComponent], + }).compileComponents(); + }) + ); beforeEach(() => { fixture = TestBed.createComponent(BackArrowComponent); diff --git a/apps/sales/src/app/store/customer/shelf/search/search.effects.ts b/apps/sales/src/app/store/customer/shelf/search/search.effects.ts index a64e1db7b..f172188c7 100644 --- a/apps/sales/src/app/store/customer/shelf/search/search.effects.ts +++ b/apps/sales/src/app/store/customer/shelf/search/search.effects.ts @@ -16,8 +16,8 @@ import { of, NEVER } from 'rxjs'; import { inputDtoToSelectedFilters, inputDtoToPrimaryFilterOption } from './mappers'; import { SelectFilter } from 'apps/sales/src/app/modules/filter'; import { PrimaryFilterOption } from 'apps/sales/src/app/modules/shelf/defs'; -import { isNullOrUndefined, isNumber } from 'util'; import { SearchProcess } from './defs'; +import { isNullOrUndefined, isNumber } from '@utils/common'; @Injectable() export class SearchEffects implements OnInitEffects { diff --git a/apps/sales/src/app/store/customer/shelf/search/search.facade.ts b/apps/sales/src/app/store/customer/shelf/search/search.facade.ts index c6e9ae009..788aa3d09 100644 --- a/apps/sales/src/app/store/customer/shelf/search/search.facade.ts +++ b/apps/sales/src/app/store/customer/shelf/search/search.facade.ts @@ -8,10 +8,10 @@ import { SharedSelectors } from 'apps/sales/src/app/core/store/selectors/shared. import { combineLatest, Subject } from 'rxjs'; import { selectFiltersToFiltersDictionary, primaryFiltersToFiltersDictionary } from './mappers'; import { Observable } from 'rxjs'; -import { isNullOrUndefined } from 'util'; import { SelectFilter } from 'apps/sales/src/app/modules/filter'; import { PrimaryFilterOption } from 'apps/sales/src/app/modules/shelf/defs'; import { SearchProcessState } from './defs'; +import { isNullOrUndefined } from '@utils/common'; @Injectable({ providedIn: 'root' }) export class SearchStateFacade { diff --git a/apps/sales/src/main.ts b/apps/sales/src/main.ts index 0c93e5139..c44e498d4 100644 --- a/apps/sales/src/main.ts +++ b/apps/sales/src/main.ts @@ -1,4 +1,4 @@ -import { version } from 'package'; +import packageInfo from 'package'; import { enableProdMode } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; @@ -19,7 +19,7 @@ if ('serviceWorker' in navigator && environment.production) { navigator.serviceWorker.register('./ngsw-worker.js'); } -console.log(`%cISA Version: ${version}`, 'font-weight: bold; font-size: 18px'); +console.log(`%cISA Version: ${packageInfo.version}`, 'font-weight: bold; font-size: 18px'); (async () => { try { diff --git a/apps/sales/src/polyfills.ts b/apps/sales/src/polyfills.ts index 8e42e8e9e..8afba5daf 100644 --- a/apps/sales/src/polyfills.ts +++ b/apps/sales/src/polyfills.ts @@ -84,7 +84,7 @@ import 'web-animations-js'; // Run `npm install --save web-animations-js`. /*************************************************************************************************** * Zone JS is required by default for Angular itself. */ -import 'zone.js/dist/zone'; // Included with Angular CLI. +import 'zone.js'; // Included with Angular CLI. /*************************************************************************************************** * APPLICATION IMPORTS diff --git a/apps/sales/src/test.ts b/apps/sales/src/test.ts index da9049dc6..0c1cedcf5 100644 --- a/apps/sales/src/test.ts +++ b/apps/sales/src/test.ts @@ -1,6 +1,6 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone-testing'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/shared/goods-in-out/src/lib/goods-in-out-order-details/goods-in-out-order-details-covers/goods-in-out-order-details-covers.component.scss b/apps/shared/goods-in-out/src/lib/goods-in-out-order-details/goods-in-out-order-details-covers/goods-in-out-order-details-covers.component.scss index a88a2085a..60b9a7bb9 100644 --- a/apps/shared/goods-in-out/src/lib/goods-in-out-order-details/goods-in-out-order-details-covers/goods-in-out-order-details-covers.component.scss +++ b/apps/shared/goods-in-out/src/lib/goods-in-out-order-details/goods-in-out-order-details-covers/goods-in-out-order-details-covers.component.scss @@ -3,14 +3,17 @@ } ui-slider { + @apply px-4; max-width: 845px; } .cover { - @apply mr-3; + @apply mr-5; .image { - @apply border-none outline-none bg-transparent; + @apply relative border-none outline-none bg-transparent; + width: 53px; + height: 85px; } .quantity { @@ -18,14 +21,12 @@ ui-slider { } .compartment-code { - @apply relative inline-block bg-black text-white opacity-80 rounded-md break-all text-center text-x-small; + @apply absolute bottom-0 left-0 inline-block bg-black text-white opacity-80 rounded-md break-all text-center text-x-small; max-width: 53px; - margin-left: -53px; } .thumbnail { @apply rounded-md shadow-lg; - width: 53px; height: 85px; } diff --git a/apps/shared/goods-in-out/src/lib/goods-in-out-order-details/goods-in-out-order-details-header/goods-in-out-order-details-header.component.scss b/apps/shared/goods-in-out/src/lib/goods-in-out-order-details/goods-in-out-order-details-header/goods-in-out-order-details-header.component.scss index 26c7bc442..48311a137 100644 --- a/apps/shared/goods-in-out/src/lib/goods-in-out-order-details/goods-in-out-order-details-header/goods-in-out-order-details-header.component.scss +++ b/apps/shared/goods-in-out/src/lib/goods-in-out-order-details/goods-in-out-order-details-header/goods-in-out-order-details-header.component.scss @@ -39,7 +39,7 @@ } .goods-in-out-header-details-header { - @apply flex flex-row justify-between m-0; + @apply flex flex-row justify-between m-0 font-bold; } .goods-in-out-header-details-wrapper { diff --git a/apps/shared/goods-in-out/src/lib/goods-in-out-order-details/goods-in-out-order-details.component.scss b/apps/shared/goods-in-out/src/lib/goods-in-out-order-details/goods-in-out-order-details.component.scss index bafe92b7e..0226948da 100644 --- a/apps/shared/goods-in-out/src/lib/goods-in-out-order-details/goods-in-out-order-details.component.scss +++ b/apps/shared/goods-in-out/src/lib/goods-in-out-order-details/goods-in-out-order-details.component.scss @@ -40,6 +40,6 @@ ::ng-deep .branch shared-goods-in-out-order-details .goods-in-out-order-details-action-wrapper button { &:disabled { - @apply bg-inactive-branch border-inactive-branch text-white !important; + @apply bg-inactive-branch border-inactive-branch text-white; } } diff --git a/apps/shared/goods-in-out/src/lib/goods-in-out-order-edit/goods-in-out-order-edit.component.scss b/apps/shared/goods-in-out/src/lib/goods-in-out-order-edit/goods-in-out-order-edit.component.scss index 8de11aa66..fec4823ae 100644 --- a/apps/shared/goods-in-out/src/lib/goods-in-out-order-edit/goods-in-out-order-edit.component.scss +++ b/apps/shared/goods-in-out/src/lib/goods-in-out-order-edit/goods-in-out-order-edit.component.scss @@ -129,6 +129,14 @@ input { flex-grow: 0 !important; } +::ng-deep shared-goods-in-out-order-edit ui-select .ui-select-toggle { + margin-left: 1rem !important; +} + ::ng-deep shared-goods-in-out-order-edit ui-select .ui-select-options { width: max-content !important; } + +::ng-deep shared-goods-in-out-order-edit shared-notification-channel-control label { + font-weight: normal !important; +} diff --git a/apps/shared/goods-in-out/src/lib/goods-in-out-order-edit/goods-in-out-order-edit.component.ts b/apps/shared/goods-in-out/src/lib/goods-in-out-order-edit/goods-in-out-order-edit.component.ts index b3a4724f6..cf4c0fa67 100644 --- a/apps/shared/goods-in-out/src/lib/goods-in-out-order-edit/goods-in-out-order-edit.component.ts +++ b/apps/shared/goods-in-out/src/lib/goods-in-out-order-edit/goods-in-out-order-edit.component.ts @@ -29,7 +29,7 @@ import { validateSsc } from '../validators/ssc.validator'; templateUrl: 'goods-in-out-order-edit.component.html', styleUrls: ['goods-in-out-order-edit.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, - providers: [EnvironmentChannelPipe, ProcessingStatusPipe], + providers: [EnvironmentChannelPipe, ProcessingStatusPipe, DatePipe], }) export class SharedGoodsInOutOrderEditComponent implements OnChanges, OnDestroy { @Output() diff --git a/apps/shared/goods-in-out/src/lib/goods-in-out-order-group/goods-in-out-order-group.component.scss b/apps/shared/goods-in-out/src/lib/goods-in-out-order-group/goods-in-out-order-group.component.scss index 8763ea08c..3a6fb1a04 100644 --- a/apps/shared/goods-in-out/src/lib/goods-in-out-order-group/goods-in-out-order-group.component.scss +++ b/apps/shared/goods-in-out/src/lib/goods-in-out-order-group/goods-in-out-order-group.component.scss @@ -3,7 +3,7 @@ } .goods-in-out-order-group-header { - @apply bg-white rounded-t-card p-4 pb-0; + @apply bg-white rounded-t-card p-4 pb-0 font-bold; h3 { @apply mt-0 mb-4; diff --git a/apps/shared/goods-in-out/src/test.ts b/apps/shared/goods-in-out/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/shared/goods-in-out/src/test.ts +++ b/apps/shared/goods-in-out/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/shared/notification-channel-control/src/lib/notification-channel-control.component.html b/apps/shared/notification-channel-control/src/lib/notification-channel-control.component.html index 9140c4356..9e47e23f4 100644 --- a/apps/shared/notification-channel-control/src/lib/notification-channel-control.component.html +++ b/apps/shared/notification-channel-control/src/lib/notification-channel-control.component.html @@ -10,11 +10,6 @@ E-Mail SMS -
- -
+
+ +
@@ -41,5 +41,10 @@ Keine gültige Mobilnummer
+
+ +
diff --git a/apps/shared/notification-channel-control/src/lib/notification-channel-control.component.scss b/apps/shared/notification-channel-control/src/lib/notification-channel-control.component.scss index bd4d92760..e3560870c 100644 --- a/apps/shared/notification-channel-control/src/lib/notification-channel-control.component.scss +++ b/apps/shared/notification-channel-control/src/lib/notification-channel-control.component.scss @@ -3,10 +3,11 @@ } .nc-heading { - @apply flex flex-row items-center px-5 py-6 bg-white; + @apply flex flex-row items-center px-4 py-6 bg-white; label { - width: 154px; + @apply font-bold; + width: 200px; } .expand { @@ -41,8 +42,8 @@ @apply flex flex-row items-start pb-6; label { - @apply mt-1; - width: 154px; + @apply mt-1 font-bold; + width: 200px; } .input-wrapper { @@ -67,10 +68,8 @@ } .nc-generate { - @apply absolute right-16; - button { - @apply text-base font-bold text-brand outline-none border-none bg-transparent; + @apply text-base font-bold text-brand outline-none border-none bg-transparent mr-4 absolute right-0; } } @@ -98,7 +97,7 @@ } ui-icon { - @apply text-white !important; + @apply text-white; } } } diff --git a/apps/shared/notification-channel-control/src/lib/notification-channel-control.component.ts b/apps/shared/notification-channel-control/src/lib/notification-channel-control.component.ts index 11098b54c..fb3749adb 100644 --- a/apps/shared/notification-channel-control/src/lib/notification-channel-control.component.ts +++ b/apps/shared/notification-channel-control/src/lib/notification-channel-control.component.ts @@ -1,9 +1,8 @@ import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { ControlContainer, FormGroup } from '@angular/forms'; -import { DomainOmsService } from '@domain/oms'; import { ComponentStore } from '@ngrx/component-store'; +import { CommunicationDetailsDTO } from '@swagger/checkout'; import { NotificationChannel } from '@swagger/oms'; -import { UiModalService } from '@ui/modal'; import { NEVER, Observable } from 'rxjs'; import { map, shareReplay, startWith, tap } from 'rxjs/operators'; @@ -20,6 +19,9 @@ export class SharedNotificationChannelControlComponent extends ComponentStore; - constructor( - private _notificationsGroup: ControlContainer, - private _cdr: ChangeDetectorRef, - private _omsService: DomainOmsService, - private _modal: UiModalService - ) { + constructor(private _notificationsGroup: ControlContainer, private _cdr: ChangeDetectorRef) { super({ open: false, }); @@ -126,6 +123,9 @@ export class SharedNotificationChannelControlComponent extends ComponentStore val | current, 0) as NotificationChannel; this.notificationChannelControl.setValue(notificationChannel); this.notificationChannelControl.markAsDirty(); + if (this.communicationDetails) { + this.channelActionEvent.emit(notificationChannels); + } } toggle(value?: boolean) { diff --git a/apps/shared/notification-channel-control/src/test.ts b/apps/shared/notification-channel-control/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/shared/notification-channel-control/src/test.ts +++ b/apps/shared/notification-channel-control/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/shell/breadcrumb/karma.conf.js b/apps/shell/breadcrumb/karma.conf.js index 1f5bd735e..815282d28 100644 --- a/apps/shell/breadcrumb/karma.conf.js +++ b/apps/shell/breadcrumb/karma.conf.js @@ -1,5 +1,8 @@ // Karma configuration file, see link for more information // https://karma-runner.github.io/1.0/config/configuration-file.html +const customLaunchers = require('../../../karma/custom-launchers'); +const junitReporter = require('../../../karma/junit-reporter')('isa-app'); +const coverageReporter = require('../../../karma/coverage-reporter')('isa-app'); module.exports = function (config) { config.set({ @@ -9,23 +12,25 @@ module.exports = function (config) { require('karma-jasmine'), require('karma-chrome-launcher'), require('karma-jasmine-html-reporter'), - require('karma-coverage-istanbul-reporter'), + require('karma-coverage'), + require('karma-junit-reporter'), require('@angular-devkit/build-angular/plugins/karma'), ], client: { clearContext: false, // leave Jasmine Spec Runner output visible in browser }, - coverageIstanbulReporter: { - dir: require('path').join(__dirname, '../../../coverage/shell/breadcrumb'), - reports: ['html', 'lcovonly', 'text-summary'], - fixWebpackSourcePaths: true, + jasmineHtmlReporter: { + suppressAll: true, // removes the duplicated traces }, + coverageReporter, + junitReporter, reporters: ['progress', 'kjhtml'], port: 9876, colors: true, logLevel: config.LOG_INFO, autoWatch: true, browsers: ['Chrome'], + customLaunchers, singleRun: false, restartOnFileChange: true, }); diff --git a/apps/shell/breadcrumb/src/lib/shell-breadcrumb.component.scss b/apps/shell/breadcrumb/src/lib/shell-breadcrumb.component.scss index 0c1c0263b..3a20b85b2 100644 --- a/apps/shell/breadcrumb/src/lib/shell-breadcrumb.component.scss +++ b/apps/shell/breadcrumb/src/lib/shell-breadcrumb.component.scss @@ -1,5 +1,6 @@ :host { - @apply flex gap-3 flex-row box-content relative; + @apply flex gap-3 py-4 flex-row box-content relative; + background-color: var(--shell-breadcrumb-background); } .link-back { diff --git a/apps/shell/breadcrumb/src/lib/shell-breadcrumb.component.ts b/apps/shell/breadcrumb/src/lib/shell-breadcrumb.component.ts index 6c48a32f1..cb4fd6b51 100644 --- a/apps/shell/breadcrumb/src/lib/shell-breadcrumb.component.ts +++ b/apps/shell/breadcrumb/src/lib/shell-breadcrumb.component.ts @@ -1,7 +1,6 @@ import { Component, ChangeDetectionStrategy, Input, OnInit } from '@angular/core'; -import { Location } from '@angular/common'; import { Breadcrumb, BreadcrumbService } from '@core/breadcrumb'; -import { BehaviorSubject, combineLatest, merge, Observable, of, ReplaySubject } from 'rxjs'; +import { BehaviorSubject, combineLatest, merge, Observable, of } from 'rxjs'; import { filter, map, switchMap } from 'rxjs/operators'; import { NavigationEnd, Router } from '@angular/router'; @@ -34,7 +33,7 @@ export class ShellBreadcrumbComponent implements OnInit { previousBreadcrumb$: Observable; - constructor(public breadcrumbService: BreadcrumbService, private location: Location, private router: Router) {} + constructor(public breadcrumbService: BreadcrumbService, private router: Router) {} ngOnInit(): void { const navigated$ = merge(of(undefined), this.router.events.pipe(filter((event) => event instanceof NavigationEnd))); diff --git a/apps/shell/breadcrumb/src/test.ts b/apps/shell/breadcrumb/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/shell/breadcrumb/src/test.ts +++ b/apps/shell/breadcrumb/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/shell/filter-overlay/README.md b/apps/shell/filter-overlay/README.md new file mode 100644 index 000000000..06e96e874 --- /dev/null +++ b/apps/shell/filter-overlay/README.md @@ -0,0 +1,25 @@ +# FilterOverlay + +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 filter-overlay` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project filter-overlay`. + +> Note: Don't forget to add `--project filter-overlay` or else it will be added to the default project in your `angular.json` file. + +## Build + +Run `ng build filter-overlay` to build the project. The build artifacts will be stored in the `dist/` directory. + +## Publishing + +After building your library with `ng build filter-overlay`, go to the dist folder `cd dist/filter-overlay` and run `npm publish`. + +## Running unit tests + +Run `ng test filter-overlay` 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. diff --git a/apps/shell/filter-overlay/karma.conf.js b/apps/shell/filter-overlay/karma.conf.js new file mode 100644 index 000000000..cdf51e8f6 --- /dev/null +++ b/apps/shell/filter-overlay/karma.conf.js @@ -0,0 +1,41 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage'), + require('@angular-devkit/build-angular/plugins/karma'), + ], + client: { + jasmine: { + // you can add configuration options for Jasmine here + // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html + // for example, you can disable the random execution with `random: false` + // or set a specific seed with `seed: 4321` + }, + clearContext: false, // leave Jasmine Spec Runner output visible in browser + }, + jasmineHtmlReporter: { + suppressAll: true, // removes the duplicated traces + }, + coverageReporter: { + dir: require('path').join(__dirname, '../../../coverage/shell/filter-overlay'), + subdir: '.', + reporters: [{ type: 'html' }, { type: 'text-summary' }], + }, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + singleRun: false, + restartOnFileChange: true, + }); +}; diff --git a/apps/shell/filter-overlay/ng-package.json b/apps/shell/filter-overlay/ng-package.json new file mode 100644 index 000000000..b21787a08 --- /dev/null +++ b/apps/shell/filter-overlay/ng-package.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../../dist/shell/filter-overlay", + "lib": { + "entryFile": "src/public-api.ts" + } +} \ No newline at end of file diff --git a/apps/shell/filter-overlay/package.json b/apps/shell/filter-overlay/package.json new file mode 100644 index 000000000..5c40d1134 --- /dev/null +++ b/apps/shell/filter-overlay/package.json @@ -0,0 +1,11 @@ +{ + "name": "@shell/filter-overlay", + "version": "0.0.1", + "peerDependencies": { + "@angular/common": "^12.2.0", + "@angular/core": "^12.2.0" + }, + "dependencies": { + "tslib": "^2.3.0" + } +} \ No newline at end of file diff --git a/apps/shell/filter-overlay/src/lib/filter-overlay.component.html b/apps/shell/filter-overlay/src/lib/filter-overlay.component.html new file mode 100644 index 000000000..27bb94967 --- /dev/null +++ b/apps/shell/filter-overlay/src/lib/filter-overlay.component.html @@ -0,0 +1,5 @@ + +
+ +
+
diff --git a/apps/shell/filter-overlay/src/lib/filter-overlay.component.scss b/apps/shell/filter-overlay/src/lib/filter-overlay.component.scss new file mode 100644 index 000000000..6ff28dc61 --- /dev/null +++ b/apps/shell/filter-overlay/src/lib/filter-overlay.component.scss @@ -0,0 +1,6 @@ +div { + @apply z-fixed mx-auto; + width: 100vw; + height: calc(100vh - 8.375rem); + background-color: var(--bg-color); +} diff --git a/apps/shell/filter-overlay/src/lib/filter-overlay.component.spec.ts b/apps/shell/filter-overlay/src/lib/filter-overlay.component.spec.ts new file mode 100644 index 000000000..c04403b2c --- /dev/null +++ b/apps/shell/filter-overlay/src/lib/filter-overlay.component.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { FilterOverlayComponent } from './filter-overlay.component'; + +describe('FilterOverlayComponent', () => { + let component: FilterOverlayComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [FilterOverlayComponent], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(FilterOverlayComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/apps/shell/filter-overlay/src/lib/filter-overlay.component.ts b/apps/shell/filter-overlay/src/lib/filter-overlay.component.ts new file mode 100644 index 000000000..0437f28f3 --- /dev/null +++ b/apps/shell/filter-overlay/src/lib/filter-overlay.component.ts @@ -0,0 +1,84 @@ +import { animate, style, transition, trigger } from '@angular/animations'; +import { Overlay, OverlayPositionBuilder, OverlayRef } from '@angular/cdk/overlay'; +import { TemplatePortal } from '@angular/cdk/portal'; +import { + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + EmbeddedViewRef, + OnDestroy, + OnInit, + TemplateRef, + ViewChild, + ViewContainerRef, +} from '@angular/core'; +import { Subject } from 'rxjs'; + +@Component({ + selector: 'shell-filter-overlay', + templateUrl: 'filter-overlay.component.html', + styleUrls: ['filter-overlay.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, + exportAs: 'shellFilterOverlay', + animations: [ + trigger('slideInOut', [ + transition(':enter', [style({ transform: 'translateX(100%)' }), animate('250ms', style({ transform: 'translateX(0%)' }))]), + transition(':leave', [style({ transform: '*' }), animate('250ms', style({ transform: 'translateX(100%)' }))]), + ]), + ], +}) +export class ShellFilterOverlayComponent implements OnInit, OnDestroy { + @ViewChild('filterOverlay') + filterOverlay: TemplateRef; + + private viewRef: EmbeddedViewRef; + private overlayRef: OverlayRef; + private _onDestroy$ = new Subject(); + + get isOpen() { + return !!this.viewRef; + } + + constructor( + private _viewContainerRef: ViewContainerRef, + private _overlay: Overlay, + private _cdr: ChangeDetectorRef, + private _overlayPositionBuilder: OverlayPositionBuilder + ) {} + + ngOnInit(): void { + this.createOverlay(); + } + + ngOnDestroy(): void { + this.overlayRef?.dispose(); + this._onDestroy$.next(); + this._onDestroy$.complete(); + } + + createOverlay() { + this.overlayRef = this._overlay.create({ + positionStrategy: this._overlayPositionBuilder.global().bottom('0px').right('0px').left('0px').top('8.375rem'), + hasBackdrop: true, + backdropClass: 'cdk-overlay-transparent-backdrop', + }); + } + + open() { + const dropdownPortal = new TemplatePortal(this.filterOverlay, this._viewContainerRef); + + if (!!this.viewRef) { + this.close(); + } + + this.viewRef = this.overlayRef.attach(dropdownPortal); + this._cdr.markForCheck(); + } + + close() { + this.viewRef?.destroy(); + this.overlayRef.detach(); + delete this.viewRef; + this._cdr.markForCheck(); + } +} diff --git a/apps/shell/filter-overlay/src/lib/filter-overlay.module.ts b/apps/shell/filter-overlay/src/lib/filter-overlay.module.ts new file mode 100644 index 000000000..68419aea3 --- /dev/null +++ b/apps/shell/filter-overlay/src/lib/filter-overlay.module.ts @@ -0,0 +1,9 @@ +import { NgModule } from '@angular/core'; +import { ShellFilterOverlayComponent } from './filter-overlay.component'; + +@NgModule({ + declarations: [ShellFilterOverlayComponent], + imports: [], + exports: [ShellFilterOverlayComponent], +}) +export class ShellFilterOverlayModule {} diff --git a/apps/shell/filter-overlay/src/public-api.ts b/apps/shell/filter-overlay/src/public-api.ts new file mode 100644 index 000000000..31494ec41 --- /dev/null +++ b/apps/shell/filter-overlay/src/public-api.ts @@ -0,0 +1,6 @@ +/* + * Public API Surface of filter-overlay + */ + +export * from './lib/filter-overlay.component'; +export * from './lib/filter-overlay.module'; diff --git a/apps/shell/filter-overlay/src/test.ts b/apps/shell/filter-overlay/src/test.ts new file mode 100644 index 000000000..b22c5d4af --- /dev/null +++ b/apps/shell/filter-overlay/src/test.ts @@ -0,0 +1,25 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import 'zone.js'; +import 'zone.js/testing'; +import { getTestBed } from '@angular/core/testing'; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; + +declare const require: { + context( + path: string, + deep?: boolean, + filter?: RegExp + ): { + keys(): string[]; + (id: string): T; + }; +}; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), { teardown: { destroyAfterEach: true } }); + +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().map(context); diff --git a/apps/shell/filter-overlay/tsconfig.lib.json b/apps/shell/filter-overlay/tsconfig.lib.json new file mode 100644 index 000000000..a7141c543 --- /dev/null +++ b/apps/shell/filter-overlay/tsconfig.lib.json @@ -0,0 +1,20 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "outDir": "../../../out-tsc/lib", + "target": "es2015", + "declaration": true, + "declarationMap": true, + "inlineSources": true, + "types": [], + "lib": [ + "dom", + "es2018" + ] + }, + "exclude": [ + "src/test.ts", + "**/*.spec.ts" + ] +} diff --git a/apps/shell/filter-overlay/tsconfig.lib.prod.json b/apps/shell/filter-overlay/tsconfig.lib.prod.json new file mode 100644 index 000000000..06de549e1 --- /dev/null +++ b/apps/shell/filter-overlay/tsconfig.lib.prod.json @@ -0,0 +1,10 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.lib.json", + "compilerOptions": { + "declarationMap": false + }, + "angularCompilerOptions": { + "compilationMode": "partial" + } +} diff --git a/apps/shell/filter-overlay/tsconfig.spec.json b/apps/shell/filter-overlay/tsconfig.spec.json new file mode 100644 index 000000000..85392ee8f --- /dev/null +++ b/apps/shell/filter-overlay/tsconfig.spec.json @@ -0,0 +1,17 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "outDir": "../../../out-tsc/spec", + "types": [ + "jasmine" + ] + }, + "files": [ + "src/test.ts" + ], + "include": [ + "**/*.spec.ts", + "**/*.d.ts" + ] +} diff --git a/apps/shell/footer/README.md b/apps/shell/footer/README.md new file mode 100644 index 000000000..4a9c46d9b --- /dev/null +++ b/apps/shell/footer/README.md @@ -0,0 +1,25 @@ +# Footer + +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 footer` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project footer`. + +> Note: Don't forget to add `--project footer` or else it will be added to the default project in your `angular.json` file. + +## Build + +Run `ng build footer` to build the project. The build artifacts will be stored in the `dist/` directory. + +## Publishing + +After building your library with `ng build footer`, go to the dist folder `cd dist/footer` and run `npm publish`. + +## Running unit tests + +Run `ng test footer` 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. diff --git a/apps/shell/footer/karma.conf.js b/apps/shell/footer/karma.conf.js new file mode 100644 index 000000000..c921aa41a --- /dev/null +++ b/apps/shell/footer/karma.conf.js @@ -0,0 +1,43 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html +const customLaunchers = require('../../../karma/custom-launchers'); +const junitReporter = require('../../../karma/junit-reporter')('shell-footer'); +const coverageReporter = require('../../../karma/coverage-reporter')('shell-footer'); + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage'), + require('karma-junit-reporter'), + require('@angular-devkit/build-angular/plugins/karma'), + ], + client: { + jasmine: { + // you can add configuration options for Jasmine here + // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html + // for example, you can disable the random execution with `random: false` + // or set a specific seed with `seed: 4321` + }, + clearContext: false, // leave Jasmine Spec Runner output visible in browser + }, + jasmineHtmlReporter: { + suppressAll: true, // removes the duplicated traces + }, + coverageReporter, + junitReporter, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + customLaunchers, + singleRun: false, + restartOnFileChange: true, + }); +}; diff --git a/apps/shell/footer/ng-package.json b/apps/shell/footer/ng-package.json new file mode 100644 index 000000000..fc5122987 --- /dev/null +++ b/apps/shell/footer/ng-package.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../../dist/shell/footer", + "lib": { + "entryFile": "src/public-api.ts" + } +} \ No newline at end of file diff --git a/apps/shell/footer/package.json b/apps/shell/footer/package.json new file mode 100644 index 000000000..85ef9f65d --- /dev/null +++ b/apps/shell/footer/package.json @@ -0,0 +1,11 @@ +{ + "name": "@shell/footer", + "version": "0.0.1", + "peerDependencies": { + "@angular/common": "^12.2.0", + "@angular/core": "^12.2.0" + }, + "dependencies": { + "tslib": "^2.3.0" + } +} \ No newline at end of file diff --git a/apps/shell/footer/src/lib/footer.component.html b/apps/shell/footer/src/lib/footer.component.html new file mode 100644 index 000000000..2d85af586 --- /dev/null +++ b/apps/shell/footer/src/lib/footer.component.html @@ -0,0 +1,5 @@ +
+ +
diff --git a/apps/shell/footer/src/lib/footer.component.scss b/apps/shell/footer/src/lib/footer.component.scss new file mode 100644 index 000000000..9af26fee7 --- /dev/null +++ b/apps/shell/footer/src/lib/footer.component.scss @@ -0,0 +1,26 @@ +:host { + @apply block box-border; + background-color: var(--shell-footer-background); +} + +footer { + @apply h-20; +} + +nav { + @apply h-full flex flex-row items-center justify-around; +} + +::ng-deep shell-footer a { + @apply flex flex-col items-center justify-center; + color: var(--shell-footer-link-inactive); + flex-basis: 100%; + + &:active { + color: var(--shell-footer-link-active); + } + + &.disabled { + color: var(--shell-footer-link-disabled); + } +} diff --git a/apps/shell/footer/src/lib/footer.component.spec.ts b/apps/shell/footer/src/lib/footer.component.spec.ts new file mode 100644 index 000000000..8db43a7f7 --- /dev/null +++ b/apps/shell/footer/src/lib/footer.component.spec.ts @@ -0,0 +1,20 @@ +import { RouterTestingModule } from '@angular/router/testing'; +import { Spectator, createComponentFactory } from '@ngneat/spectator'; +import { UiIconModule } from '@ui/icon'; +import { ShellFooterComponent } from './footer.component'; + +describe('ShellFooterComponent', () => { + let spectator: Spectator; + const createComponent = createComponentFactory({ + component: ShellFooterComponent, + imports: [UiIconModule, RouterTestingModule], + }); + + beforeEach(() => { + spectator = createComponent(); + }); + + it('should create the app', () => { + expect(spectator.component).toBeTruthy(); + }); +}); diff --git a/apps/shell/footer/src/lib/footer.component.ts b/apps/shell/footer/src/lib/footer.component.ts new file mode 100644 index 000000000..61440f1c0 --- /dev/null +++ b/apps/shell/footer/src/lib/footer.component.ts @@ -0,0 +1,13 @@ +import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'shell-footer', + templateUrl: './footer.component.html', + styleUrls: ['./footer.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ShellFooterComponent implements OnInit { + constructor() {} + + ngOnInit(): void {} +} diff --git a/apps/shell/footer/src/lib/footer.module.ts b/apps/shell/footer/src/lib/footer.module.ts new file mode 100644 index 000000000..ce5981bc2 --- /dev/null +++ b/apps/shell/footer/src/lib/footer.module.ts @@ -0,0 +1,10 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { ShellFooterComponent } from './footer.component'; + +@NgModule({ + declarations: [ShellFooterComponent], + imports: [CommonModule], + exports: [ShellFooterComponent], +}) +export class ShellFooterModule {} diff --git a/apps/shell/footer/src/public-api.ts b/apps/shell/footer/src/public-api.ts new file mode 100644 index 000000000..91846e6b8 --- /dev/null +++ b/apps/shell/footer/src/public-api.ts @@ -0,0 +1,6 @@ +/* + * Public API Surface of footer + */ + +export * from './lib/footer.component'; +export * from './lib/footer.module'; diff --git a/apps/shell/footer/src/test.ts b/apps/shell/footer/src/test.ts new file mode 100644 index 000000000..b22c5d4af --- /dev/null +++ b/apps/shell/footer/src/test.ts @@ -0,0 +1,25 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import 'zone.js'; +import 'zone.js/testing'; +import { getTestBed } from '@angular/core/testing'; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; + +declare const require: { + context( + path: string, + deep?: boolean, + filter?: RegExp + ): { + keys(): string[]; + (id: string): T; + }; +}; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), { teardown: { destroyAfterEach: true } }); + +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().map(context); diff --git a/apps/shell/footer/tsconfig.lib.json b/apps/shell/footer/tsconfig.lib.json new file mode 100644 index 000000000..a7141c543 --- /dev/null +++ b/apps/shell/footer/tsconfig.lib.json @@ -0,0 +1,20 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "outDir": "../../../out-tsc/lib", + "target": "es2015", + "declaration": true, + "declarationMap": true, + "inlineSources": true, + "types": [], + "lib": [ + "dom", + "es2018" + ] + }, + "exclude": [ + "src/test.ts", + "**/*.spec.ts" + ] +} diff --git a/apps/shell/footer/tsconfig.lib.prod.json b/apps/shell/footer/tsconfig.lib.prod.json new file mode 100644 index 000000000..06de549e1 --- /dev/null +++ b/apps/shell/footer/tsconfig.lib.prod.json @@ -0,0 +1,10 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.lib.json", + "compilerOptions": { + "declarationMap": false + }, + "angularCompilerOptions": { + "compilationMode": "partial" + } +} diff --git a/apps/shell/footer/tsconfig.spec.json b/apps/shell/footer/tsconfig.spec.json new file mode 100644 index 000000000..85392ee8f --- /dev/null +++ b/apps/shell/footer/tsconfig.spec.json @@ -0,0 +1,17 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "outDir": "../../../out-tsc/spec", + "types": [ + "jasmine" + ] + }, + "files": [ + "src/test.ts" + ], + "include": [ + "**/*.spec.ts", + "**/*.d.ts" + ] +} diff --git a/apps/shell/header/karma.conf.js b/apps/shell/header/karma.conf.js index 4c3860669..cf12075f1 100644 --- a/apps/shell/header/karma.conf.js +++ b/apps/shell/header/karma.conf.js @@ -1,5 +1,8 @@ // Karma configuration file, see link for more information // https://karma-runner.github.io/1.0/config/configuration-file.html +const customLaunchers = require('../../../karma/custom-launchers'); +const junitReporter = require('../../../karma/junit-reporter')('shell-header'); +const coverageReporter = require('../../../karma/coverage-reporter')('shell-header'); module.exports = function (config) { config.set({ @@ -9,23 +12,22 @@ module.exports = function (config) { require('karma-jasmine'), require('karma-chrome-launcher'), require('karma-jasmine-html-reporter'), - require('karma-coverage-istanbul-reporter'), + require('karma-coverage'), + require('karma-junit-reporter'), require('@angular-devkit/build-angular/plugins/karma'), ], client: { clearContext: false, // leave Jasmine Spec Runner output visible in browser }, - coverageIstanbulReporter: { - dir: require('path').join(__dirname, '../../../coverage/shell/header'), - reports: ['html', 'lcovonly', 'text-summary'], - fixWebpackSourcePaths: true, - }, + coverageReporter, + junitReporter, reporters: ['progress', 'kjhtml'], port: 9876, colors: true, logLevel: config.LOG_INFO, autoWatch: true, browsers: ['Chrome'], + customLaunchers, singleRun: false, restartOnFileChange: true, }); diff --git a/apps/shell/header/src/lib/header.component.html b/apps/shell/header/src/lib/header.component.html index 7aac81448..68917f723 100644 --- a/apps/shell/header/src/lib/header.component.html +++ b/apps/shell/header/src/lib/header.component.html @@ -1,19 +1,24 @@ -
-
- Hugendubel +
+
+ Hugendubel
-
- - - + + -
- + +
+ +
-
+
diff --git a/apps/shell/header/src/lib/header.component.scss b/apps/shell/header/src/lib/header.component.scss index 249c71ad1..d41ab5620 100644 --- a/apps/shell/header/src/lib/header.component.scss +++ b/apps/shell/header/src/lib/header.component.scss @@ -1,3 +1,81 @@ :host { - @apply block; + @apply block box-border bg-white px-4 pt-4 pb-2; +} + +header { + @apply grid grid-flow-col items-center grid-cols-3 relative; +} + +.brand { + img { + @apply w-48; + } +} + +.navigation { + @apply grid grid-flow-row gap-y-2 items-center; + + .navigation-content { + @apply hidden absolute top-14 -right-2 grid-flow-row gap-y-4 items-center justify-center bg-white rounded shadow p-4 z-popover; + + &.open { + @apply grid; + } + } + + .external-content { + @apply grid grid-flow-col gap-x-4 items-center justify-center; + } + + @screen tablet { + .navigation-content { + @apply grid relative top-auto right-auto bg-transparent shadow-none rounded-none p-0; + } + } +} + +.section-toggle { + @apply hidden; + + @screen tablet { + @apply inline-flex; + } +} + +.more-toggle-btn { + @apply p-2 text-right; + color: var(--shell-header-button-color); + + &.active, + &:focus, + &:hover { + color: var(--shell-header-button-color-active); + } +} + +::ng-deep shell-header { + .navigation { + button, + a { + @apply grid grid-flow-col gap-x-2 justify-center items-center p-2 font-bold; + } + } + + .external-content { + button, + a { + @apply p-4 grid grid-flow-col items-center justify-center gap-2 font-bold; + color: var(--shell-header-button-color); + + &.active, + &:focus, + &:hover { + color: var(--shell-header-button-color-active); + } + + &[disabled] { + color: var(--shell-header-button-color-disabled); + } + } + } } diff --git a/apps/shell/header/src/lib/header.component.spec.ts b/apps/shell/header/src/lib/header.component.spec.ts new file mode 100644 index 000000000..3b1223b63 --- /dev/null +++ b/apps/shell/header/src/lib/header.component.spec.ts @@ -0,0 +1,36 @@ +import { fakeAsync } from '@angular/core/testing'; +import { Spectator, createComponentFactory } from '@ngneat/spectator'; +import { UiIconModule } from '@ui/icon'; +import { ShellHeaderComponent } from './header.component'; +import { ShellSectionToggleComponent } from './section-toggle/section-toggle.component'; + +describe('ShellHeaderComponent', () => { + let spectator: Spectator; + const createComponent = createComponentFactory({ + component: ShellHeaderComponent, + declarations: [ShellSectionToggleComponent], + imports: [UiIconModule], + }); + + beforeEach(() => (spectator = createComponent())); + + it('should create', () => { + expect(spectator.component).toBeTruthy(); + }); + + describe('toggleMoreMenu', () => { + it('should toggle more menu', () => { + spectator.component.toggleMoreMenu(); + expect(spectator.component.moreMenuOpen).toBeTruthy(); + spectator.component.toggleMoreMenu(); + expect(spectator.component.moreMenuOpen).toBeFalsy(); + }); + + it('should use the parameter value if provided', () => { + spectator.component.toggleMoreMenu(true); + expect(spectator.component.moreMenuOpen).toBeTruthy(); + spectator.component.toggleMoreMenu(false); + expect(spectator.component.moreMenuOpen).toBeFalsy(); + }); + }); +}); diff --git a/apps/shell/header/src/lib/header.component.ts b/apps/shell/header/src/lib/header.component.ts index 156f4c8cb..eaf064494 100644 --- a/apps/shell/header/src/lib/header.component.ts +++ b/apps/shell/header/src/lib/header.component.ts @@ -1,4 +1,5 @@ -import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnInit, Output, QueryList, ViewChildren } from '@angular/core'; +import { ShellSectionToggleComponent } from './section-toggle/section-toggle.component'; @Component({ selector: 'shell-header', @@ -7,7 +8,28 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class ShellHeaderComponent implements OnInit { + @Input() + section: string; + + @Output() + sectionChange = new EventEmitter(); + + @ViewChildren(ShellSectionToggleComponent) + sectionToggles: QueryList; + + moreMenuOpen = false; + constructor() {} ngOnInit(): void {} + + toggleMoreMenu(value?: boolean) { + this.moreMenuOpen = value ?? !this.moreMenuOpen; + + if (this.moreMenuOpen) { + setTimeout(() => { + this.sectionToggles?.forEach((cmp) => cmp.updateSwitchPositionAndSize()); + }, 0); + } + } } diff --git a/apps/shell/header/src/lib/header.module.ts b/apps/shell/header/src/lib/header.module.ts index e15028568..63128a649 100644 --- a/apps/shell/header/src/lib/header.module.ts +++ b/apps/shell/header/src/lib/header.module.ts @@ -2,9 +2,10 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { UiIconModule } from '@ui/icon'; import { ShellHeaderComponent } from './header.component'; +import { ShellSectionToggleComponent } from './section-toggle/section-toggle.component'; @NgModule({ - declarations: [ShellHeaderComponent], + declarations: [ShellHeaderComponent, ShellSectionToggleComponent], imports: [CommonModule, UiIconModule], exports: [ShellHeaderComponent], }) diff --git a/apps/shell/header/src/lib/section-toggle/section-toggle.component.html b/apps/shell/header/src/lib/section-toggle/section-toggle.component.html index e69de29bb..6a3b62360 100644 --- a/apps/shell/header/src/lib/section-toggle/section-toggle.component.html +++ b/apps/shell/header/src/lib/section-toggle/section-toggle.component.html @@ -0,0 +1,9 @@ +
+ diff --git a/apps/shell/header/src/lib/section-toggle/section-toggle.component.scss b/apps/shell/header/src/lib/section-toggle/section-toggle.component.scss index e69de29bb..9e1e63c88 100644 --- a/apps/shell/header/src/lib/section-toggle/section-toggle.component.scss +++ b/apps/shell/header/src/lib/section-toggle/section-toggle.component.scss @@ -0,0 +1,18 @@ +:host { + @apply inline-flex flex-row box-border rounded-full relative; + background-color: var(--shell-header-switch-background); +} + +button { + @apply px-4 py-2 font-bold text-black rounded-full z-dropdown transition-all duration-200 ease-in-out; + color: var(--shell-header-label-color); + + &.active { + color: var(--shell-header-label-color-active); + } +} + +.switch { + @apply absolute top-0 bottom-0 rounded-full transition-all duration-200 ease-in-out; + background-color: var(--shell-header-switch-color); +} diff --git a/apps/shell/header/src/lib/section-toggle/section-toggle.component.spec.ts b/apps/shell/header/src/lib/section-toggle/section-toggle.component.spec.ts new file mode 100644 index 000000000..c1f31d947 --- /dev/null +++ b/apps/shell/header/src/lib/section-toggle/section-toggle.component.spec.ts @@ -0,0 +1,116 @@ +import { Spectator, createComponentFactory } from '@ngneat/spectator'; +import { ShellSectionToggleComponent } from './section-toggle.component'; + +describe('ShellSectionToggleComponent', () => { + let spectator: Spectator; + const createComponent = createComponentFactory(ShellSectionToggleComponent); + + beforeEach(() => { + spectator = createComponent(); + }); + + it('should create', () => { + expect(spectator.component).toBeTruthy(); + }); + + describe('setSection()', () => { + it('should set the section', () => { + spectator.component.setSection('test'); + expect(spectator.component.section).toEqual('test'); + }); + + it('should emit sectionChange', () => { + const sectionChange = spectator.component.sectionChange; + spyOn(sectionChange, 'emit'); + spectator.component.setSection('test'); + expect(sectionChange.emit).toHaveBeenCalledWith('test'); + }); + + it('should not call emit if section is the same', () => { + const sectionChange = spectator.component.sectionChange; + spyOn(sectionChange, 'emit'); + spectator.component.setSection('test'); + spectator.component.setSection('test'); + expect(sectionChange.emit).toHaveBeenCalledTimes(1); + }); + + it('should call updateSwitchPositionAndSize()', () => { + spyOn(spectator.component, 'updateSwitchPositionAndSize'); + spectator.component.setSection('test'); + expect(spectator.component.updateSwitchPositionAndSize).toHaveBeenCalled(); + }); + }); + + describe('updateSwitchPositionAndSize()', () => { + it('should call setStyle 2 times', () => { + spyOn(spectator.component['_renderer'], 'setStyle'); + spectator.component.updateSwitchPositionAndSize(); + expect(spectator.component['_renderer'].setStyle).toHaveBeenCalledTimes(2); + }); + }); + + describe('button element', () => { + it('should have an attribute section', () => { + spectator.setInput('sections', [ + { + label: 'test 1', + key: 'test1', + }, + { + label: 'test 2', + key: 'test2', + }, + ]); + + expect(spectator.query('button[section="test1"]')).toExist(); + expect(spectator.query('button[section="test2"]')).toExist(); + }); + + it('should have the class active when section is selected', () => { + spectator.setInput({ + sections: [ + { + label: 'test 1', + key: 'test1', + }, + { + label: 'test 2', + key: 'test2', + }, + ], + section: 'test1', + }); + + expect(spectator.query('button[section="test1"]')).toHaveClass('active'); + expect(spectator.query('button[section="test2"]')).not.toHaveClass('active'); + }); + + it('should have the label as content', () => { + spectator.setInput({ + sections: [ + { + label: 'test 1', + key: 'test1', + }, + { + label: 'test 2', + key: 'test2', + }, + ], + section: 'test1', + }); + + expect(spectator.query('button[section="test1"]')).toHaveText('test 1'); + expect(spectator.query('button[section="test2"]')).toHaveText('test 2'); + }); + }); + + describe('sectionChange', () => { + it('should emit sectionChange', () => { + spectator.output('sectionChange').subscribe((section) => { + expect(section).toEqual('test'); + }); + spectator.component.setSection('test'); + }); + }); +}); diff --git a/apps/shell/header/src/lib/section-toggle/section-toggle.component.ts b/apps/shell/header/src/lib/section-toggle/section-toggle.component.ts index 0dfc8f82c..05991ee53 100644 --- a/apps/shell/header/src/lib/section-toggle/section-toggle.component.ts +++ b/apps/shell/header/src/lib/section-toggle/section-toggle.component.ts @@ -1,4 +1,15 @@ -import { Component, ChangeDetectionStrategy } from '@angular/core'; +import { + Component, + ChangeDetectionStrategy, + Input, + Output, + EventEmitter, + ElementRef, + Renderer2, + AfterViewInit, + OnChanges, + SimpleChanges, +} from '@angular/core'; @Component({ selector: 'shell-section-toggle', @@ -6,6 +17,54 @@ import { Component, ChangeDetectionStrategy } from '@angular/core'; styleUrls: ['section-toggle.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class ShellSectionToggleComponent { - constructor() {} +export class ShellSectionToggleComponent implements AfterViewInit, OnChanges { + @Input() + section: string = 'customer'; + + @Input() + sections: Array<{ label: string; key: string }> = [ + { label: 'Kunden', key: 'customer' }, + { label: 'Filiale', key: 'branch' }, + ]; + + @Output() + sectionChange = new EventEmitter(); + + constructor(private readonly _elementRef: ElementRef, private readonly _renderer: Renderer2) {} + + ngOnChanges(changes: SimpleChanges): void { + if (changes.section) { + this.updateSwitchPositionAndSize(); + } + } + + ngAfterViewInit(): void { + this.updateSwitchPositionAndSize(); + } + + setSection(section: string) { + if (this.section !== section) { + this.section = section; + this.sectionChange.emit(section); + } + this.updateSwitchPositionAndSize(); + } + + updateSwitchPositionAndSize() { + const hostElement = this._elementRef?.nativeElement; + const targetElement = hostElement?.querySelector(`button[section="${this.section}"]`); + const switchElement = hostElement?.querySelector('.switch'); + + if (!hostElement || !targetElement || !switchElement) { + return; + } + + const targetElementRect = targetElement.getBoundingClientRect(); + const targetElementLeft = targetElementRect.left; + const targetElementWidth = targetElementRect.width; + const hostElementLeft = hostElement.getBoundingClientRect().left; + const left = targetElementLeft - hostElementLeft; + this._renderer.setStyle(switchElement, 'left', `${left}px`); + this._renderer.setStyle(switchElement, 'width', `${targetElementWidth}px`); + } } diff --git a/apps/shell/header/src/public-api.ts b/apps/shell/header/src/public-api.ts index ca2264c55..2ff82dc0e 100644 --- a/apps/shell/header/src/public-api.ts +++ b/apps/shell/header/src/public-api.ts @@ -2,6 +2,5 @@ * Public API Surface of header */ -export * from './lib/header.service'; export * from './lib/header.component'; export * from './lib/header.module'; diff --git a/apps/shell/header/src/test.ts b/apps/shell/header/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/shell/header/src/test.ts +++ b/apps/shell/header/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/shell/process/README.md b/apps/shell/process/README.md new file mode 100644 index 000000000..7a1e0aef9 --- /dev/null +++ b/apps/shell/process/README.md @@ -0,0 +1,25 @@ +# Process + +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 process` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project process`. + +> Note: Don't forget to add `--project process` or else it will be added to the default project in your `angular.json` file. + +## Build + +Run `ng build process` to build the project. The build artifacts will be stored in the `dist/` directory. + +## Publishing + +After building your library with `ng build process`, go to the dist folder `cd dist/process` and run `npm publish`. + +## Running unit tests + +Run `ng test process` 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. diff --git a/apps/shell/process/karma.conf.js b/apps/shell/process/karma.conf.js new file mode 100644 index 000000000..4e3446b79 --- /dev/null +++ b/apps/shell/process/karma.conf.js @@ -0,0 +1,43 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html +const customLaunchers = require('../../../karma/custom-launchers'); +const junitReporter = require('../../../karma/junit-reporter')('shell-process'); +const coverageReporter = require('../../../karma/coverage-reporter')('shell-process'); + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage'), + require('karma-junit-reporter'), + require('@angular-devkit/build-angular/plugins/karma'), + ], + client: { + jasmine: { + // you can add configuration options for Jasmine here + // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html + // for example, you can disable the random execution with `random: false` + // or set a specific seed with `seed: 4321` + }, + clearContext: false, // leave Jasmine Spec Runner output visible in browser + }, + jasmineHtmlReporter: { + suppressAll: true, // removes the duplicated traces + }, + coverageReporter, + junitReporter, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + customLaunchers, + singleRun: false, + restartOnFileChange: true, + }); +}; diff --git a/apps/shell/process/ng-package.json b/apps/shell/process/ng-package.json new file mode 100644 index 000000000..66fa87532 --- /dev/null +++ b/apps/shell/process/ng-package.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../../dist/shell/process", + "lib": { + "entryFile": "src/public-api.ts" + } +} \ No newline at end of file diff --git a/apps/shell/process/package.json b/apps/shell/process/package.json new file mode 100644 index 000000000..ba84ccb41 --- /dev/null +++ b/apps/shell/process/package.json @@ -0,0 +1,11 @@ +{ + "name": "@shell/process", + "version": "0.0.1", + "peerDependencies": { + "@angular/common": "^12.2.0", + "@angular/core": "^12.2.0" + }, + "dependencies": { + "tslib": "^2.3.0" + } +} \ No newline at end of file diff --git a/apps/shell/process/src/lib/process-tab/process-tab.component.html b/apps/shell/process/src/lib/process-tab/process-tab.component.html new file mode 100644 index 000000000..31aa1df07 --- /dev/null +++ b/apps/shell/process/src/lib/process-tab/process-tab.component.html @@ -0,0 +1,19 @@ + + + + diff --git a/apps/shell/process/src/lib/process-tab/process-tab.component.scss b/apps/shell/process/src/lib/process-tab/process-tab.component.scss new file mode 100644 index 000000000..d0abd12bf --- /dev/null +++ b/apps/shell/process/src/lib/process-tab/process-tab.component.scss @@ -0,0 +1,39 @@ +:host { + @apply box-border; +} + +.tab { + @apply flex flex-row items-center justify-center mr-4 pb-1; + color: var(--shell-process-text-inactive); + border-bottom: 3px solid transparent; + + h2 { + @apply text-lg font-bold whitespace-nowrap py-1; + } + + .close { + @apply ml-2 mt-px-2; + } +} + +.badge { + @apply flex flex-row items-center justify-center py-1 px-4 ml-2 rounded-full; + background-color: var(--shell-process-badge-background); + + &.badge-active { + @apply text-white; + background-color: var(--shell-process-badge-active); + } + + ui-icon { + @apply mr-2; + } + + span { + @apply text-lg font-bold; + } +} + +.active { + border-bottom: 3px solid var(--shell-process-border-active); +} diff --git a/apps/shell/process/src/lib/process-tab/process-tab.component.spec.ts b/apps/shell/process/src/lib/process-tab/process-tab.component.spec.ts new file mode 100644 index 000000000..a941e88b8 --- /dev/null +++ b/apps/shell/process/src/lib/process-tab/process-tab.component.spec.ts @@ -0,0 +1,160 @@ +import { ApplicationProcess } from '@core/application'; +import { Spectator, createComponentFactory } from '@ngneat/spectator'; +import { UiIconModule } from '@ui/icon'; +import { UiModalService } from '@ui/modal'; +import { ShellProcessTabComponent } from './process-tab.component'; + +describe('ShellProcessTabComponent', () => { + const process: ApplicationProcess = { + id: 1, + name: 'Vorgang', + type: 'cart', + section: 'customer', + }; + + let spectator: Spectator; + + const createComponent = createComponentFactory({ + component: ShellProcessTabComponent, + declarations: [], + imports: [UiIconModule], + mocks: [UiModalService], + }); + + beforeEach(() => (spectator = createComponent({ props: { process } }))); + + it('should create', () => { + expect(spectator.component).toBeTruthy(); + }); + + describe('input process', () => { + it('should render h2 with process name', () => { + expect(spectator.query('h2')).toHaveText(`${process.name}`); + }); + + describe('process.type is cart', () => { + it('should render button.badge', () => { + expect(spectator.query('button.badge')).toBeVisible(); + }); + + it('should render ui-icon with icon cart', () => { + expect(spectator.query('button.badge ui-icon')).toHaveAttribute('icon', 'cart'); + }); + + it('should render process.data.count as text', () => { + spectator.setInput('process', { ...process, data: { count: 5 } }); + expect(spectator.query('button.badge span')).toHaveText('5'); + }); + + it('should render 0 if process.data.count is not set', () => { + expect(spectator.query('button.badge span')).toHaveText('0'); + }); + }); + }); + + describe('input isActive', () => { + it('should render the close button if isActive is true for the current active process and the process is closable', () => { + spectator.setInput('isActive', true); + spectator.setInput('process', { id: 1, name: 'Vorgang', closeable: true, section: 'customer' }); + expect(spectator.query('button.close')).toBeVisible(); + }); + + it('should add class .active for process button if isActive is true for the current active process and the process is closable', () => { + spectator.setInput('isActive', true); + spectator.setInput('process', { id: 1, name: 'Vorgang', closeable: true, section: 'customer' }); + expect(spectator.query('button.tab')).toHaveClass('active'); + }); + + it('should not render the close button if isActive is false for all inactive processes', () => { + spectator.setInput('isActive', false); + expect(spectator.query('button.close')).not.toBeVisible(); + }); + + it('should not add class .active for process button if isActive is false for all inactive tabs', () => { + spectator.setInput('isActive', false); + expect(spectator.query('button.tab')).not.toHaveClass('active'); + }); + }); + + describe('output activateProcess', () => { + it('should emit after clicking on process tab', () => { + spyOn(spectator.component.activateProcess, 'emit'); + spectator.click('button.tab'); + expect(spectator.component.activateProcess.emit).toHaveBeenCalled(); + }); + }); + + describe('output processAction', () => { + it('should emit after clicking on button.badge', () => { + spyOn(spectator.component.processAction, 'emit'); + spectator.click('button.badge'); + expect(spectator.component.processAction.emit).toHaveBeenCalled(); + }); + }); + + describe('output closeProcess', () => { + it('should emit with process.id after clicking on button.close on active processes', () => { + spectator.setInput('isActive', true); + spectator.setInput('process', { id: 1, name: 'Vorgang', closeable: true, section: 'customer' }); + spyOn(spectator.component.closeProcess, 'emit'); + spectator.click('button.close'); + expect(spectator.component.closeProcess.emit).toHaveBeenCalledWith(process.id); + }); + }); + + describe('onTabClick()', () => { + it('should be called on button.tab click', () => { + spyOn(spectator.component, 'onTabClick'); + spectator.click('button.tab'); + expect(spectator.component.onTabClick).toHaveBeenCalled(); + }); + + it('should emit activateProcess with the process.id', () => { + spyOn(spectator.component.activateProcess, 'emit'); + spectator.component.onTabClick(); + expect(spectator.component.activateProcess.emit).toHaveBeenCalled(); + }); + + it('should call scrollIntoView on the tab ViewChild', () => { + spyOn(spectator.component.tab.nativeElement, 'scrollIntoView'); + spectator.component.onTabClick(); + expect(spectator.component.tab.nativeElement.scrollIntoView).toHaveBeenCalledWith({ behavior: 'smooth' }); + }); + }); + + describe('triggerAnimation()', () => { + it('should set animate to true', () => { + spectator.component.animate = false; + spectator.component.triggerAnimation(); + expect(spectator.component.animate).toBeTrue(); + }); + }); + + describe('slideAnimationDone()', () => { + it('should be called when slideIne.done event is triggered', () => { + spyOn(spectator.component, 'slideAnimationDone'); + spectator.triggerEventHandler('button.tab', '@slideIn.done', undefined); + expect(spectator.component.slideAnimationDone).toHaveBeenCalled(); + }); + + it('should call scrollIntoView when the isActive is true', () => { + spectator.setInput('isActive', true); + spyOn(spectator.component.tab.nativeElement, 'scrollIntoView'); + spectator.component.slideAnimationDone(undefined); + expect(spectator.component.tab.nativeElement.scrollIntoView).toHaveBeenCalledWith({ behavior: 'smooth' }); + }); + + it('should not call scrollIntoView when the isActive is false', () => { + spectator.setInput('isActive', false); + spyOn(spectator.component.tab.nativeElement, 'scrollIntoView'); + spectator.component.slideAnimationDone(undefined); + expect(spectator.component.tab.nativeElement.scrollIntoView).not.toHaveBeenCalled(); + }); + + it('should set animate to false', () => { + spectator.component.animate = true; + spectator.component.slideAnimationDone(undefined); + expect(spectator.component.animate).toBeFalse(); + }); + }); +}); diff --git a/apps/shell/process/src/lib/process-tab/process-tab.component.ts b/apps/shell/process/src/lib/process-tab/process-tab.component.ts new file mode 100644 index 000000000..97d5a6a87 --- /dev/null +++ b/apps/shell/process/src/lib/process-tab/process-tab.component.ts @@ -0,0 +1,96 @@ +import { animate, state, style, transition, trigger } from '@angular/animations'; +import { ChangeDetectionStrategy, Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; +import { ApplicationProcess } from '@core/application'; +import { ConfirmModalData, UiConfirmModalComponent, UiModalService } from '@ui/modal'; + +@Component({ + selector: 'shell-process-tab', + templateUrl: './process-tab.component.html', + styleUrls: ['./process-tab.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, + animations: [ + trigger('slideIn', [ + state( + 'start', + style({ + marginRight: '5rem', + }) + ), + state( + 'end', + style({ + marginRight: '1rem', + }) + ), + transition('start => end', [animate('200ms ease-in')]), + ]), + ], +}) +export class ShellProcessTabComponent implements OnInit { + @ViewChild('tab', { static: true }) tab: ElementRef; + + @Input() + process: ApplicationProcess; + + @Input() + isActive: boolean; + + @Output() + activateProcess = new EventEmitter(); + + @Output() + processAction = new EventEmitter(); + + @Output() + closeProcess = new EventEmitter(); + + animate = false; + + constructor(private readonly _modal: UiModalService) {} + + ngOnInit() {} + + triggerAnimation() { + this.animate = true; + } + + slideAnimationDone(animationEnd: AnimationEvent) { + if (this.isActive) { + this.tab.nativeElement.scrollIntoView({ behavior: 'smooth' }); + } + this.animate = false; + } + + onTabClick() { + this.activateProcess.emit(this.process.id); + this.tab.nativeElement.scrollIntoView({ behavior: 'smooth' }); + } + + onProcessAction(e: Event) { + e.preventDefault(); + e.stopPropagation(); + this.processAction.emit(this.process); + } + + checkCloseProcess() { + if (this.process.confirmClosing) { + this._modal + .open({ + content: UiConfirmModalComponent, + title: 'Möchtest Sie den Vorgang wirklich schließen?', + data: { + message: this.process.name, + confirmLabel: 'Schließen', + rejectLabel: 'Abbrechen', + } as ConfirmModalData, + }) + .afterClosed$.subscribe((result) => { + if (result.data) { + this.closeProcess.emit(this.process.id); + } + }); + } else { + this.closeProcess.emit(this.process.id); + } + } +} diff --git a/apps/shell/process/src/lib/process.component.html b/apps/shell/process/src/lib/process.component.html new file mode 100644 index 000000000..6702c8f4f --- /dev/null +++ b/apps/shell/process/src/lib/process.component.html @@ -0,0 +1,12 @@ + + + + + diff --git a/apps/shell/process/src/lib/process.component.scss b/apps/shell/process/src/lib/process.component.scss new file mode 100644 index 000000000..a0a0fc70b --- /dev/null +++ b/apps/shell/process/src/lib/process.component.scss @@ -0,0 +1,35 @@ +:host { + @apply box-border grid items-center px-4 pt-0 overflow-hidden max-w-full; + grid-auto-flow: column; + grid-template-columns: 1fr auto; + background-color: var(--shell-process-background); +} + +ui-slider { + @apply pt-2; +} + +button.add { + @apply flex justify-center items-center ml-4; + + .label { + @apply font-bold text-base mr-2; + color: var(--shell-process-add-label); + } + + .symbol { + @apply flex justify-center items-center rounded-full w-9 h-9 text-3xl; + color: var(--shell-process-add-icon-text); + background-color: var(--shell-process-add-icon-background); + } +} + +::ng-deep shell-process ui-slider { + .next-wrapper { + @apply pl-40; + } + + .prev-wrapper { + @apply pr-40; + } +} diff --git a/apps/shell/process/src/lib/process.component.spec.ts b/apps/shell/process/src/lib/process.component.spec.ts new file mode 100644 index 000000000..9541320af --- /dev/null +++ b/apps/shell/process/src/lib/process.component.spec.ts @@ -0,0 +1,46 @@ +import { Spectator, createComponentFactory } from '@ngneat/spectator'; +import { UiSliderModule } from '@ui/slider'; +import { ShellProcessComponent } from './process.component'; + +describe('ShellProcessComponent', () => { + let spectator: Spectator; + const createComponent = createComponentFactory({ + component: ShellProcessComponent, + declarations: [], + imports: [UiSliderModule], + }); + + beforeEach(() => (spectator = createComponent())); + + it('should create', () => { + expect(spectator.component).toBeTruthy(); + }); + + it('should emit after add process button click', () => { + spectator.setInput('canAddProcess', true); + spyOn(spectator.component.addProcess, 'emit'); + spectator.click('button.add'); + expect(spectator.component.addProcess.emit).toHaveBeenCalled(); + }); + + it('should render button.add if canAddProcess is true', () => { + spectator.setInput('canAddProcess', true); + expect(spectator.query('button.add')).toExist(); + }); + + it('should not render button.add if canAddProcess is false', () => { + spectator.setInput('canAddProcess', false); + expect(spectator.query('button.add')).not.toExist(); + }); + + it('should render the lable in button.add if label is set', () => { + spectator.setInput('canAddProcess', true); + spectator.setInput('label', 'Add Process'); + expect(spectator.query('button.add span.label')).toHaveText('Add Process'); + }); + + it('should not render the lable in button.add if label is not set', () => { + spectator.setInput('canAddProcess', true); + expect(spectator.query('button.add span.label')).not.toExist(); + }); +}); diff --git a/apps/shell/process/src/lib/process.component.ts b/apps/shell/process/src/lib/process.component.ts new file mode 100644 index 000000000..94cc8935b --- /dev/null +++ b/apps/shell/process/src/lib/process.component.ts @@ -0,0 +1,22 @@ +import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; + +@Component({ + selector: 'shell-process', + templateUrl: './process.component.html', + styleUrls: ['./process.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ShellProcessComponent implements OnInit { + @Output() + addProcess = new EventEmitter(); + + @Input() + label: string; + + @Input() + canAddProcess: boolean; + + constructor() {} + + ngOnInit(): void {} +} diff --git a/apps/shell/process/src/lib/process.module.ts b/apps/shell/process/src/lib/process.module.ts new file mode 100644 index 000000000..d4ed96e6c --- /dev/null +++ b/apps/shell/process/src/lib/process.module.ts @@ -0,0 +1,13 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { UiIconModule } from '@ui/icon'; +import { UiSliderModule } from '@ui/slider'; +import { ShellProcessTabComponent } from './process-tab/process-tab.component'; +import { ShellProcessComponent } from './process.component'; + +@NgModule({ + imports: [CommonModule, UiIconModule, UiSliderModule], + declarations: [ShellProcessComponent, ShellProcessTabComponent], + exports: [ShellProcessComponent, ShellProcessTabComponent], +}) +export class ShellProcessModule {} diff --git a/apps/shell/process/src/public-api.ts b/apps/shell/process/src/public-api.ts new file mode 100644 index 000000000..bd34e0f0c --- /dev/null +++ b/apps/shell/process/src/public-api.ts @@ -0,0 +1,7 @@ +/* + * Public API Surface of process + */ + +export * from './lib/process.component'; +export * from './lib/process-tab/process-tab.component'; +export * from './lib/process.module'; diff --git a/apps/shell/process/src/test.ts b/apps/shell/process/src/test.ts new file mode 100644 index 000000000..b22c5d4af --- /dev/null +++ b/apps/shell/process/src/test.ts @@ -0,0 +1,25 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import 'zone.js'; +import 'zone.js/testing'; +import { getTestBed } from '@angular/core/testing'; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; + +declare const require: { + context( + path: string, + deep?: boolean, + filter?: RegExp + ): { + keys(): string[]; + (id: string): T; + }; +}; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), { teardown: { destroyAfterEach: true } }); + +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().map(context); diff --git a/apps/shell/process/tsconfig.lib.json b/apps/shell/process/tsconfig.lib.json new file mode 100644 index 000000000..a7141c543 --- /dev/null +++ b/apps/shell/process/tsconfig.lib.json @@ -0,0 +1,20 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "outDir": "../../../out-tsc/lib", + "target": "es2015", + "declaration": true, + "declarationMap": true, + "inlineSources": true, + "types": [], + "lib": [ + "dom", + "es2018" + ] + }, + "exclude": [ + "src/test.ts", + "**/*.spec.ts" + ] +} diff --git a/apps/shell/process/tsconfig.lib.prod.json b/apps/shell/process/tsconfig.lib.prod.json new file mode 100644 index 000000000..06de549e1 --- /dev/null +++ b/apps/shell/process/tsconfig.lib.prod.json @@ -0,0 +1,10 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.lib.json", + "compilerOptions": { + "declarationMap": false + }, + "angularCompilerOptions": { + "compilationMode": "partial" + } +} diff --git a/apps/shell/process/tsconfig.spec.json b/apps/shell/process/tsconfig.spec.json new file mode 100644 index 000000000..85392ee8f --- /dev/null +++ b/apps/shell/process/tsconfig.spec.json @@ -0,0 +1,17 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "outDir": "../../../out-tsc/spec", + "types": [ + "jasmine" + ] + }, + "files": [ + "src/test.ts" + ], + "include": [ + "**/*.spec.ts", + "**/*.d.ts" + ] +} diff --git a/apps/store/search-component-store/README.md b/apps/store/search-component-store/README.md new file mode 100644 index 000000000..c42adae4f --- /dev/null +++ b/apps/store/search-component-store/README.md @@ -0,0 +1,25 @@ +# SearchComponentStore + +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 search-component-store` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project search-component-store`. + +> Note: Don't forget to add `--project search-component-store` or else it will be added to the default project in your `angular.json` file. + +## Build + +Run `ng build search-component-store` to build the project. The build artifacts will be stored in the `dist/` directory. + +## Publishing + +After building your library with `ng build search-component-store`, go to the dist folder `cd dist/search-component-store` and run `npm publish`. + +## Running unit tests + +Run `ng test search-component-store` 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. diff --git a/apps/store/search-component-store/karma.conf.js b/apps/store/search-component-store/karma.conf.js new file mode 100644 index 000000000..b6cdaf539 --- /dev/null +++ b/apps/store/search-component-store/karma.conf.js @@ -0,0 +1,43 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html +const customLaunchers = require('../../../karma/custom-launchers'); +const junitReporter = require('../../../karma/junit-reporter')('store-search-component-store'); +const coverageReporter = require('../../../karma/coverage-reporter')('store-search-component-store'); + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage'), + require('karma-junit-reporter'), + require('@angular-devkit/build-angular/plugins/karma'), + ], + client: { + jasmine: { + // you can add configuration options for Jasmine here + // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html + // for example, you can disable the random execution with `random: false` + // or set a specific seed with `seed: 4321` + }, + clearContext: false, // leave Jasmine Spec Runner output visible in browser + }, + jasmineHtmlReporter: { + suppressAll: true, // removes the duplicated traces + }, + coverageReporter, + junitReporter, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + customLaunchers, + singleRun: false, + restartOnFileChange: true, + }); +}; diff --git a/apps/store/search-component-store/ng-package.json b/apps/store/search-component-store/ng-package.json new file mode 100644 index 000000000..b923bbc91 --- /dev/null +++ b/apps/store/search-component-store/ng-package.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../../dist/store/search-component-store", + "lib": { + "entryFile": "src/public-api.ts" + } +} \ No newline at end of file diff --git a/apps/store/search-component-store/package.json b/apps/store/search-component-store/package.json new file mode 100644 index 000000000..7c91683ba --- /dev/null +++ b/apps/store/search-component-store/package.json @@ -0,0 +1,11 @@ +{ + "name": "@store/search-component-store", + "version": "0.0.1", + "peerDependencies": { + "@angular/common": "^12.2.0", + "@angular/core": "^12.2.0" + }, + "dependencies": { + "tslib": "^2.3.0" + } +} \ No newline at end of file diff --git a/apps/store/search-component-store/src/lib/defs/index.ts b/apps/store/search-component-store/src/lib/defs/index.ts new file mode 100644 index 000000000..c584370e0 --- /dev/null +++ b/apps/store/search-component-store/src/lib/defs/index.ts @@ -0,0 +1,6 @@ +// start:ng42.barrel +export * from './search-settings-loader'; +export * from './search-state-search-result'; +export * from './search-state-search-service'; +export * from './search-state'; +// end:ng42.barrel diff --git a/apps/store/search-component-store/src/lib/defs/search-settings-loader.ts b/apps/store/search-component-store/src/lib/defs/search-settings-loader.ts new file mode 100644 index 000000000..f68dbcb5e --- /dev/null +++ b/apps/store/search-component-store/src/lib/defs/search-settings-loader.ts @@ -0,0 +1,6 @@ +import { UISettingsDTO } from '@swagger/cat'; +import { Observable } from 'rxjs'; + +export interface SearchStateSettingsLoader { + load(): Promise | Observable | UISettingsDTO; +} diff --git a/apps/store/search-component-store/src/lib/defs/search-state-search-result.ts b/apps/store/search-component-store/src/lib/defs/search-state-search-result.ts new file mode 100644 index 000000000..6145afb30 --- /dev/null +++ b/apps/store/search-component-store/src/lib/defs/search-state-search-result.ts @@ -0,0 +1,6 @@ +export interface SearchStateSearchResult { + items?: T[]; + message?: string; + hits?: number; + error?: boolean; +} diff --git a/apps/store/search-component-store/src/lib/defs/search-state-search-service.ts b/apps/store/search-component-store/src/lib/defs/search-state-search-service.ts new file mode 100644 index 000000000..10618427b --- /dev/null +++ b/apps/store/search-component-store/src/lib/defs/search-state-search-service.ts @@ -0,0 +1,10 @@ +import { UiFilter } from '@ui/filter'; +import { Observable } from 'rxjs'; +import { SearchStateSearchResult } from './search-state-search-result'; + +export interface SearchStateSearchService { + search( + filter: UiFilter, + options: { skip: number } + ): Promise> | Observable> | SearchStateSearchResult; +} diff --git a/apps/store/search-component-store/src/lib/defs/search-state.ts b/apps/store/search-component-store/src/lib/defs/search-state.ts new file mode 100644 index 000000000..4589a90a6 --- /dev/null +++ b/apps/store/search-component-store/src/lib/defs/search-state.ts @@ -0,0 +1,9 @@ +import { UiFilter } from '@ui/filter'; + +export interface SearchState { + filter?: UiFilter; + status?: 'fetching' | 'error'; + message?: string; + items: T[]; + hits: number; +} diff --git a/apps/store/search-component-store/src/lib/index.ts b/apps/store/search-component-store/src/lib/index.ts new file mode 100644 index 000000000..ef764d879 --- /dev/null +++ b/apps/store/search-component-store/src/lib/index.ts @@ -0,0 +1,6 @@ +// start:ng42.barrel +export * from './search-component-store.module'; +export * from './search-component-store.service'; +export * from './tokens'; +export * from './defs'; +// end:ng42.barrel diff --git a/apps/store/search-component-store/src/lib/search-component-store.module.ts b/apps/store/search-component-store/src/lib/search-component-store.module.ts new file mode 100644 index 000000000..350a45574 --- /dev/null +++ b/apps/store/search-component-store/src/lib/search-component-store.module.ts @@ -0,0 +1,8 @@ +import { NgModule } from '@angular/core'; + +@NgModule({ + declarations: [], + imports: [], + exports: [], +}) +export class SearchComponentStoreModule {} diff --git a/apps/store/search-component-store/src/lib/search-component-store.service.spec.ts b/apps/store/search-component-store/src/lib/search-component-store.service.spec.ts new file mode 100644 index 000000000..d60149f86 --- /dev/null +++ b/apps/store/search-component-store/src/lib/search-component-store.service.spec.ts @@ -0,0 +1,560 @@ +import { SpectatorService, createServiceFactory, SpyObject } from '@ngneat/spectator'; +import { UISettingsDTO } from '@swagger/cat'; +import { UiFilter } from '@ui/filter'; +import { SearchComponentStoreService } from './search-component-store.service'; +import { SEARCH_STATE_SEARCH_SERVICE, SEARCH_STATE_SETTINGS_LOADER } from './tokens'; +import { SearchState, SearchStateSearchResult, SearchStateSearchService, SearchStateSettingsLoader } from './defs'; +import { BehaviorSubject, Observable, of, throwError } from 'rxjs'; +import { delay } from 'rxjs/operators'; +import { UiErrorModalComponent, UiModalService } from '@ui/modal'; +import { CacheService } from '@core/cache'; + +describe('SearchComponentStoreService', () => { + let spectator: SpectatorService; + let settingsLoader: SearchStateSettingsLoader; + let searchService: SearchStateSearchService; + let uiModalService: SpyObject; + let cacheService: SpyObject; + const createService = createServiceFactory({ + service: SearchComponentStoreService, + providers: [ + { provide: SEARCH_STATE_SETTINGS_LOADER, useValue: { load: () => null } }, + { provide: SEARCH_STATE_SEARCH_SERVICE, useValue: { search: () => null } }, + ], + mocks: [UiModalService, CacheService], + }); + + beforeEach(() => { + spectator = createService(); + settingsLoader = spectator.inject(SEARCH_STATE_SETTINGS_LOADER); + searchService = spectator.inject(SEARCH_STATE_SEARCH_SERVICE); + uiModalService = spectator.inject(UiModalService); + cacheService = spectator.inject(CacheService); + spyOn(spectator.service, 'patchState').and.callThrough(); + }); + + it('should create', () => { + expect(spectator.service).toBeTruthy(); + }); + + describe('get state', () => { + it('should return the state', () => { + const state = { items: [], hits: 0 }; + spectator.service.setState(state); + expect(spectator.service.state).toBe(state); + }); + }); + + describe('get state$', () => { + it('should return the state$', () => { + const state = { items: [], hits: 0 }; + spectator.service.setState(state); + + spectator.service.state$ + .subscribe((s) => { + expect(s).toBe(state); + }) + .unsubscribe(); + }); + }); + + describe('get filter', () => { + it('should return the filter', () => { + const filter = new UiFilter(); + spectator.service.setFilter(filter); + expect(spectator.service.filter).toBe(filter); + }); + }); + + describe('get filter$', () => { + it('should return the filter$', () => { + const filter = new UiFilter(); + spectator.service.setFilter(filter); + + spectator.service.filter$ + .subscribe((f) => { + expect(f).toBe(filter); + }) + .unsubscribe(); + }); + }); + + describe('get status', () => { + it('should return the status', () => { + spectator.service.setStatus('fetching'); + expect(spectator.service.status).toBe('fetching'); + }); + }); + + describe('get status$', () => { + it('should return the status$', () => { + spectator.service.setStatus('fetching'); + + spectator.service.status$ + .subscribe((s) => { + expect(s).toBe('fetching'); + }) + .unsubscribe(); + }); + }); + + describe('get fetching', () => { + it('should return the fetching', () => { + spectator.service.setStatus('fetching'); + expect(spectator.service.fetching).toBe(true); + }); + }); + + describe('get fetching$', () => { + it('should return the fetching$', () => { + spectator.service.setStatus('fetching'); + + spectator.service.fetching$ + .subscribe((s) => { + expect(s).toBe(true); + }) + .unsubscribe(); + }); + }); + + describe('get error', () => { + it('should return the error', () => { + spectator.service.setStatus('error'); + expect(spectator.service.error).toBe(true); + }); + }); + + describe('get error$', () => { + it('should return the error$', () => { + spectator.service.setStatus('error'); + + spectator.service.error$ + .subscribe((s) => { + expect(s).toBe(true); + }) + .unsubscribe(); + }); + }); + + describe('get message', () => { + it('should return the message', () => { + spectator.service.setMessage('message'); + expect(spectator.service.message).toBe('message'); + }); + }); + + describe('get message$', () => { + it('should return the message$', () => { + spectator.service.setMessage('message'); + + spectator.service.message$ + .subscribe((s) => { + expect(s).toBe('message'); + }) + .unsubscribe(); + }); + }); + + describe('get items', () => { + it('should return the items', () => { + spectator.service.setItems([{}, {}]); + expect(spectator.service.items).toEqual([{}, {}]); + }); + }); + + describe('get items$', () => { + it('should return the items$', () => { + spectator.service.setItems([{}, {}]); + + spectator.service.items$ + .subscribe((s) => { + expect(s).toEqual([{}, {}]); + }) + .unsubscribe(); + }); + }); + + describe('get hits', () => { + it('should return the hits', () => { + spectator.service.setHits(5); + expect(spectator.service.hits).toEqual(5); + }); + }); + + describe('get hits$', () => { + it('should return the hits$', () => { + spectator.service.setHits(5); + + spectator.service.hits$ + .subscribe((s) => { + expect(s).toEqual(5); + }) + .unsubscribe(); + }); + }); + + describe('setItems()', () => { + it('should set the items', () => { + spectator.service.setItems([{}, {}]); + expect(spectator.service.items).toEqual([{}, {}]); + expect(spectator.service.patchState).toHaveBeenCalledWith({ items: [{}, {}] }); + }); + }); + + describe('appendItems()', () => { + it('should append the items', () => { + spyOnProperty(spectator.service, 'items', 'get').and.returnValue([{ name: 'item1' }, { name: 'item2' }]); + spectator.service.appendItems([{ name: 'item3' }, { name: 'item4' }]); + expect(spectator.service.patchState).toHaveBeenCalledWith({ + items: [{ name: 'item1' }, { name: 'item2' }, { name: 'item3' }, { name: 'item4' }], + }); + }); + }); + + describe('clearItems()', () => { + it('should clear the items', () => { + spectator.service.setItems([{}, {}]); + spectator.service.clearItems(); + expect(spectator.service.items).toEqual([]); + expect(spectator.service.patchState).toHaveBeenCalledWith({ items: [] }); + }); + }); + + describe('setStatus()', () => { + it('should set the status fetching', () => { + spectator.service.setStatus('fetching'); + expect(spectator.service.status).toBe('fetching'); + expect(spectator.service.patchState).toHaveBeenCalledWith({ status: 'fetching' }); + }); + it('should set the status error', () => { + spectator.service.setStatus('error'); + expect(spectator.service.status).toBe('error'); + expect(spectator.service.patchState).toHaveBeenCalledWith({ status: 'error' }); + }); + }); + + describe('setHits()', () => { + it('should set the hits', () => { + spectator.service.setHits(2); + expect(spectator.service.patchState).toHaveBeenCalledWith({ hits: 2 }); + }); + }); + + describe('setMessage()', () => { + it('should set the message', () => { + spectator.service.setMessage('Test'); + expect(spectator.service.patchState).toHaveBeenCalledWith({ message: 'Test' }); + }); + }); + + describe('reset()', () => { + it('should reset the state without defaults', () => { + spyOn(spectator.service, 'setState'); + spectator.service.reset(); + expect(spectator.service.setState).toHaveBeenCalledWith({ items: [], hits: 0 }); + }); + + it('should reset the state with defaults', () => { + spyOn(spectator.service, 'setState'); + spectator.service.reset({ hits: 2, message: 'Test' }); + expect(spectator.service.setState).toHaveBeenCalledWith({ items: [], hits: 2, message: 'Test' }); + }); + }); + + describe('setDefaultFilter()', () => { + it('should set the filter with default settings', async () => { + spyOn(spectator.service, 'getDefaultSettings').and.returnValue(Promise.resolve({ imageUrl: 'testUrl' })); + spyOn(spectator.service, 'setFilter').and.callThrough(); + await spectator.service.setDefaultFilter(); + expect(spectator.service.getDefaultSettings).toHaveBeenCalled(); + expect(spectator.service.setFilter).toHaveBeenCalledWith({ imageUrl: 'testUrl' }); + }); + }); + + describe('setFilter()', () => { + it('should set the filter with UiFilter as argument', () => { + const filter = new UiFilter(); + spectator.service.setFilter(filter); + expect(spectator.service.patchState).toHaveBeenCalledWith({ filter }); + }); + + it('should set the filter with UISettingsDTO as argument', () => { + const settings: UISettingsDTO = { imageUrl: 'testUrl' }; + const filter = new UiFilter(); + spyOn(UiFilter, 'create').and.returnValue(filter); + spectator.service.setFilter(settings); + expect(spectator.service.patchState).toHaveBeenCalledWith({ filter }); + expect(UiFilter.create).toHaveBeenCalledWith(settings); + }); + }); + + describe('getDefaultSettings()', () => { + let settingsLoaderSpy: jasmine.Spy<() => Promise | Observable | UISettingsDTO>; + + beforeEach(() => { + settingsLoaderSpy = spyOn(settingsLoader, 'load'); + }); + + it('should call _settingsLoader.load()', () => { + spectator.service.getDefaultSettings(); + expect(settingsLoader.load).toHaveBeenCalled(); + }); + + it('should check if load() returns an Observable and return the result as a Promise', async () => { + settingsLoaderSpy.and.returnValue(of({ imageUrl: 'testUrl' })); + + const settingsPromise = spectator.service.getDefaultSettings(); + const settings = await settingsPromise; + + expect(settingsPromise).toBeInstanceOf(Promise); + expect(settings).toEqual({ imageUrl: 'testUrl' }); + }); + + it('should check if load() returns a Subject and return the result as a Promise', async () => { + const subject = new BehaviorSubject({ imageUrl: 'testUrl' }); + settingsLoaderSpy.and.returnValue(subject); + + const settingsPromise = spectator.service.getDefaultSettings(); + subject.complete(); + const settings = await settingsPromise; + + expect(settingsPromise).toBeInstanceOf(Promise); + expect(settings).toEqual({ imageUrl: 'testUrl' }); + }); + + it('should check if load() returns a Promise and return the result as a Promise', async () => { + settingsLoaderSpy.and.returnValue(Promise.resolve({ imageUrl: 'testUrl' })); + + const settingsPromise = spectator.service.getDefaultSettings(); + const settings = await settingsPromise; + + expect(settingsPromise).toBeInstanceOf(Promise); + expect(settings).toEqual({ imageUrl: 'testUrl' }); + }); + + it('should check if load() returns a Object and return the result as a Promise', async () => { + settingsLoaderSpy.and.returnValue({ imageUrl: 'testUrl' }); + + const settingsPromise = spectator.service.getDefaultSettings(); + const settings = await settingsPromise; + + expect(settingsPromise).toBeInstanceOf(Promise); + expect(settings).toEqual({ imageUrl: 'testUrl' }); + }); + }); + + describe('searchRequest()', () => { + let searchServiceSpy: jasmine.Spy<() => + | Promise + | Observable + | SearchStateSearchResult>; + + beforeEach(() => { + searchServiceSpy = spyOn(searchService, 'search'); + }); + + it('should call _searchService.search with filter and skip of items.length', () => { + const filter = new UiFilter(); + spectator.service.searchRequest(filter, [{ name: 'Test' }]); + expect(searchService.search).toHaveBeenCalledWith(filter, { skip: 1 }); + }); + + it('should check if search() returns an Observable and return the result as an Observable', (done) => { + const filter = new UiFilter(); + searchServiceSpy.and.returnValue(of({ message: 'Testmessage' }).pipe(delay(1))); + + const searchRequest = spectator.service.searchRequest(filter, []); + + expect(searchRequest).toBeInstanceOf(Observable); + + searchRequest.subscribe((result) => { + expect(result).toEqual({ message: 'Testmessage' }); + done(); + }); + }); + + it('should check if search() returns an Promise and return the result as an Observable', (done) => { + const filter = new UiFilter(); + searchServiceSpy.and.returnValue(Promise.resolve({ message: 'Testmessage' })); + + const searchRequest = spectator.service.searchRequest(filter, []); + + expect(searchRequest).toBeInstanceOf(Observable); + + searchRequest.subscribe((result) => { + expect(result).toEqual({ message: 'Testmessage' }); + done(); + }); + }); + + it('should check if search() returns an Object and return the result as an Observable', (done) => { + const filter = new UiFilter(); + searchServiceSpy.and.returnValue({ message: 'Testmessage' }); + + const searchRequest = spectator.service.searchRequest(filter, []); + + expect(searchRequest).toBeInstanceOf(Observable); + + searchRequest.subscribe((result) => { + expect(result).toEqual({ message: 'Testmessage' }); + done(); + }); + }); + }); + + describe('handleSearchResponse()', () => { + it('should set the status "error" and message "Keine Suchergebnisse" if res.hits is 0', () => { + const res: SearchStateSearchResult = { + hits: 0, + }; + spyOn(spectator.service, 'setStatus'); + spyOn(spectator.service, 'setMessage'); + spectator.service.handleSearchResponse(res); + expect(spectator.service.setStatus).toHaveBeenCalledWith('error'); + expect(spectator.service.setMessage).toHaveBeenCalledWith('Keine Suchergebnisse'); + }); + + it('should reset status and message and append items and set hits and if res.hits is not 0', () => { + const res: SearchStateSearchResult = { + hits: 3, + items: [{}], + }; + spyOn(spectator.service, 'setStatus'); + spyOn(spectator.service, 'setMessage'); + spyOn(spectator.service, 'appendItems'); + spyOn(spectator.service, 'setHits'); + spectator.service.handleSearchResponse(res); + expect(spectator.service.setStatus).toHaveBeenCalledWith(undefined); + expect(spectator.service.setMessage).toHaveBeenCalledWith(undefined); + expect(spectator.service.appendItems).toHaveBeenCalledWith([{}]); + expect(spectator.service.setHits).toHaveBeenCalledWith(3); + }); + }); + + describe('handleSearchError()', () => { + it('should call setStatus and open error dialog', () => { + const err = { + message: 'Test', + }; + spyOn(spectator.service, 'setStatus'); + spectator.service.handleSearchError(err); + expect(spectator.service.setStatus).toHaveBeenCalledWith('error'); + expect(uiModalService.open).toHaveBeenCalledWith({ + content: UiErrorModalComponent, + title: 'Suche Fehlgeschlagen', + data: err, + }); + }); + }); + + describe('search effect', () => { + let setStatusSpy: jasmine.Spy<(status: undefined | 'fetching' | 'error') => void>; + let searchRequestSpy: jasmine.Spy<(filter: UiFilter, items: any[]) => Observable>>; + let handleSearchResponseSpy: jasmine.Spy<(res: SearchStateSearchResult) => void>; + let handleSearchErrorSpy: jasmine.Spy<(err: any) => void>; + let searchCompletedNextSpy: jasmine.Spy<(value: SearchState) => void>; + let getStateSpy: jasmine.Spy<() => SearchState>; + let clearItemsSpy: jasmine.Spy<() => void>; + + beforeEach(() => { + setStatusSpy = spyOn(spectator.service, 'setStatus'); + searchRequestSpy = spyOn(spectator.service, 'searchRequest'); + handleSearchResponseSpy = spyOn(spectator.service, 'handleSearchResponse'); + handleSearchErrorSpy = spyOn(spectator.service, 'handleSearchError'); + searchCompletedNextSpy = spyOn(spectator.service.searchCompleted, 'next'); + getStateSpy = spyOnProperty(spectator.service, 'state', 'get'); + clearItemsSpy = spyOn(spectator.service, 'clearItems'); + }); + + it('should call setStatus, searchRequest, handleSearchResponse and searchCompleted.next after a successful search', () => { + const filter = new UiFilter(); + const items = [{}]; + searchRequestSpy.and.returnValue(of({ items: [{ name: 'Testobject' }], hits: 1 })); + getStateSpy.and.returnValue({ items: [{ name: 'Testobject' }], hits: 1 }); + spectator.service.setFilter(filter); + spectator.service.setItems(items); + spectator.service.search({}); + expect(spectator.service.clearItems).not.toHaveBeenCalled(); + expect(spectator.service.handleSearchError).not.toHaveBeenCalled(); + expect(spectator.service.setStatus).toHaveBeenCalledWith('fetching'); + expect(spectator.service.searchRequest).toHaveBeenCalledWith(filter, items); + expect(spectator.service.handleSearchResponse).toHaveBeenCalledWith({ items: [{ name: 'Testobject' }], hits: 1 }); + expect(spectator.service.searchCompleted.next).toHaveBeenCalledWith({ items: [{ name: 'Testobject' }], hits: 1 }); + }); + + it('should call clearItems and setStatus, searchRequest, handleSearchResponse and searchCompleted.next after a successful search with options.clear true', () => { + const filter = new UiFilter(); + const items = [{}]; + searchRequestSpy.and.returnValue(of({ items: [{ name: 'Testobject' }], hits: 1 })); + getStateSpy.and.returnValue({ items: [{ name: 'Testobject' }], hits: 1 }); + spectator.service.setFilter(filter); + spectator.service.setItems(items); + spectator.service.search({ clear: true }); + expect(spectator.service.clearItems).toHaveBeenCalled(); + expect(spectator.service.handleSearchError).not.toHaveBeenCalled(); + expect(spectator.service.setStatus).toHaveBeenCalledWith('fetching'); + expect(spectator.service.searchRequest).toHaveBeenCalledWith(filter, items); + expect(spectator.service.handleSearchResponse).toHaveBeenCalledWith({ items: [{ name: 'Testobject' }], hits: 1 }); + expect(spectator.service.searchCompleted.next).toHaveBeenCalledWith({ items: [{ name: 'Testobject' }], hits: 1 }); + }); + + it('should call setStatus, searchRequest, handleSearchError and searchCompleted.next after a failed search', () => { + const filter = new UiFilter(); + const items = [{}]; + searchRequestSpy.and.returnValue(throwError({ message: 'error' })); + getStateSpy.and.returnValue({ items: [], hits: 0, message: 'error' }); + spectator.service.setFilter(filter); + spectator.service.setItems(items); + spectator.service.search({}); + expect(spectator.service.clearItems).not.toHaveBeenCalled(); + expect(spectator.service.handleSearchError).toHaveBeenCalledWith({ message: 'error' }); + expect(spectator.service.setStatus).toHaveBeenCalledWith('fetching'); + expect(spectator.service.searchRequest).toHaveBeenCalledWith(filter, items); + expect(spectator.service.handleSearchResponse).not.toHaveBeenCalled(); + expect(spectator.service.searchCompleted.next).toHaveBeenCalledWith({ items: [], hits: 0, message: 'error' }); + }); + }); + + describe('resetFilter()', () => { + it('should call getDefaultSettings()', () => { + spyOn(spectator.service, 'getDefaultSettings').and.returnValue(Promise.resolve({ imageUrl: 'testUrl' })); + spectator.service.resetFilter(); + expect(spectator.service.getDefaultSettings).toHaveBeenCalled(); + }); + + it('should create the UiFilter and set it from queryParams', async () => { + const queryParams = { skip: '5' }; + const filter = new UiFilter(); + spyOn(UiFilter, 'create').and.returnValue(filter); + spyOn(filter, 'fromQueryParams'); + await spectator.service.resetFilter(queryParams); + + spectator.service.filter$ + .subscribe((f) => { + expect(f).toBeDefined(); + expect(f.fromQueryParams).toHaveBeenCalledWith(queryParams); + }) + .unsubscribe(); + }); + }); + + describe('restoreResults()', () => { + it('should call _cache.get() with the filter queryParams', () => { + const filter = new UiFilter(); + spectator.service.setFilter(filter); + spectator.service.restoreResults(); + expect(cacheService.get).toHaveBeenCalledWith(filter.getQueryParams()); + }); + + it('should set hits and items from the cachedResult', () => { + const hits = 5; + const items = [{ name: 'item1' }, { name: 'item2' }]; + cacheService.get.and.returnValue({ hits, items }); + const filter = new UiFilter(); + spectator.service.setFilter(filter); + spectator.service.restoreResults(); + expect(spectator.service.items).toEqual(items); + expect(spectator.service.hits).toEqual(hits); + }); + }); +}); diff --git a/apps/store/search-component-store/src/lib/search-component-store.service.ts b/apps/store/search-component-store/src/lib/search-component-store.service.ts new file mode 100644 index 000000000..415aa94f2 --- /dev/null +++ b/apps/store/search-component-store/src/lib/search-component-store.service.ts @@ -0,0 +1,252 @@ +import { Inject, Injectable, OnDestroy } from '@angular/core'; +import { CacheService } from '@core/cache'; +import { ComponentStore, tapResponse } from '@ngrx/component-store'; +import { UISettingsDTO } from '@swagger/cat'; +import { UiFilter } from '@ui/filter'; +import { UiErrorModalComponent, UiModalService } from '@ui/modal'; +import { from, Observable, of, Subject } from 'rxjs'; +import { switchMap, withLatestFrom, finalize, tap, filter, takeUntil } from 'rxjs/operators'; +import { SearchState, SearchStateSearchResult, SearchStateSearchService, SearchStateSettingsLoader } from './defs'; +import { SEARCH_STATE_SEARCH_SERVICE, SEARCH_STATE_SETTINGS_LOADER } from './tokens'; + +@Injectable() +export class SearchComponentStoreService extends ComponentStore> implements OnDestroy { + private _onDestroy$ = new Subject(); + + get state() { + return this.get(); + } + + state$ = this.select((state) => state); + + get filter() { + return this.get((state) => state.filter); + } + + filter$ = this.select((state) => state.filter); + + get status() { + return this.get((state) => state.status); + } + + status$ = this.select((state) => state.status); + + get fetching() { + return this.status === 'fetching'; + } + + fetching$ = this.select((state) => state.status === 'fetching'); + + get error() { + return this.status === 'error'; + } + + error$ = this.select((state) => state.status === 'error'); + + get message() { + return this.get((state) => state.message); + } + + message$ = this.select((state) => state.message); + + get items() { + return this.get((state) => state.items); + } + + items$ = this.select((state) => state.items); + + get hits() { + return this.get((state) => state.hits); + } + + hits$ = this.select((state) => state.hits); + + searchCompleted = new Subject>(); + + constructor( + @Inject(SEARCH_STATE_SETTINGS_LOADER) private readonly _settingsLoader: SearchStateSettingsLoader, + @Inject(SEARCH_STATE_SEARCH_SERVICE) private readonly _searchService: SearchStateSearchService, + private readonly _cache: CacheService, + private readonly _uiModal: UiModalService + ) { + super({ + items: [], + hits: 0, + }); + + this.filter$ + .pipe( + takeUntil(this._onDestroy$), + filter((filter) => !!filter) + ) + .subscribe((filter) => { + const cachedResult = this._cache.get(filter?.getQueryParams()); + if (cachedResult) { + this.setHits(cachedResult.hits); + this.setItems(cachedResult.items); + } + }); + } + + ngOnDestroy(): void { + this._onDestroy$.next(); + this._onDestroy$.complete(); + } + + setItems(items: T[]) { + this.patchState({ items }); + } + + appendItems(items: T[]) { + this.patchState({ + items: [...this.items, ...items], + }); + } + + clearItems() { + this.patchState({ items: [] }); + } + + setStatus(status: undefined | 'fetching' | 'error') { + this.patchState({ status }); + } + + setHits(hits: number) { + this.patchState({ hits }); + } + + setMessage(message: string) { + this.patchState({ message }); + } + + reset(defaults: Partial> = {}) { + this.setState({ + items: [], + hits: 0, + ...defaults, + }); + } + + async setDefaultFilter() { + const filter = await this.getDefaultSettings(); + this.setFilter(filter); + } + + async resetFilter(queryParams?: Record) { + const settings = await this.getDefaultSettings(); + const filter = UiFilter.create(settings); + + if (queryParams) { + filter.fromQueryParams(queryParams); + } + this.reset({ filter }); + } + + setFilter(filter: UiFilter | UISettingsDTO) { + let _filter: UiFilter; + + if (filter instanceof UiFilter) { + _filter = filter; + } else { + _filter = UiFilter.create(filter); + } + + this.patchState({ filter: _filter }); + } + + getDefaultSettings(): Promise { + const settings = this._settingsLoader.load(); + + // check if settings is an Observable + if (settings instanceof Observable) { + return settings.toPromise(); + } + + // check if setting is a Promise + if (settings instanceof Promise) { + return settings; + } + + return Promise.resolve(settings); + } + + search = this.effect((options$: Observable<{ clear?: boolean }>) => + options$.pipe( + tap((options) => { + this.setStatus('fetching'); + if (options.clear) { + this.clearItems(); + } + }), + withLatestFrom(this.filter$, this.items$), + switchMap(([_, filter, items]) => { + return this.searchRequest(filter, items).pipe( + tapResponse( + (res) => { + this.handleSearchResponse(res); + this.cacheResults(filter, { items: [...items, ...res.items], hits: res.hits }); + }, + (err) => this.handleSearchError(err) + ), + finalize(() => { + this.searchCompleted.next(this.state); + }) + ); + }) + ) + ); + + searchRequest(filter: UiFilter, items: T[]): Observable> { + const result = this._searchService.search(filter, { skip: items.length }); + + // check if result is an Observable + if (result instanceof Observable) { + return result; + } + + // check if result is a Promise + if (result instanceof Promise) { + return from(result); + } + + return of(result); + } + + handleSearchResponse(res: SearchStateSearchResult) { + if (res.hits === 0) { + this.setStatus('error'); + this.setMessage('Keine Suchergebnisse'); + this.setHits(0); + this.clearItems(); + } else { + this.setStatus(undefined); + this.appendItems(res.items); + this.setHits(res.hits); + this.setMessage(undefined); + } + } + + handleSearchError(err: any) { + this.setStatus('error'); + this._uiModal.open({ + content: UiErrorModalComponent, + title: 'Suche Fehlgeschlagen', + data: err, + }); + } + + cacheResults(filter: UiFilter, data: { items: T[]; hits: number }) { + this._cache.set(filter.getQueryParams(), data); + } + + restoreResults() { + const cachedResult = this._cache.get(this.filter.getQueryParams()); + + if (cachedResult) { + this.setHits(cachedResult.hits); + this.setItems(cachedResult.items); + } + + return cachedResult; + } +} diff --git a/apps/store/search-component-store/src/lib/tokens.ts b/apps/store/search-component-store/src/lib/tokens.ts new file mode 100644 index 000000000..1dff306ca --- /dev/null +++ b/apps/store/search-component-store/src/lib/tokens.ts @@ -0,0 +1,7 @@ +import { InjectionToken } from '@angular/core'; +import { SearchStateSettingsLoader } from './defs/search-settings-loader'; +import { SearchStateSearchService } from './defs/search-state-search-service'; + +export const SEARCH_STATE_SETTINGS_LOADER = new InjectionToken('SEARCH_STATE_SETTINGS_LOADER'); + +export const SEARCH_STATE_SEARCH_SERVICE = new InjectionToken('SEARCH_STATE_SEARCH_SERVICE'); diff --git a/apps/store/search-component-store/src/public-api.ts b/apps/store/search-component-store/src/public-api.ts new file mode 100644 index 000000000..22761edeb --- /dev/null +++ b/apps/store/search-component-store/src/public-api.ts @@ -0,0 +1,5 @@ +/* + * Public API Surface of search-component-store + */ + +export * from './lib'; diff --git a/apps/store/search-component-store/src/test.ts b/apps/store/search-component-store/src/test.ts new file mode 100644 index 000000000..b22c5d4af --- /dev/null +++ b/apps/store/search-component-store/src/test.ts @@ -0,0 +1,25 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import 'zone.js'; +import 'zone.js/testing'; +import { getTestBed } from '@angular/core/testing'; +import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; + +declare const require: { + context( + path: string, + deep?: boolean, + filter?: RegExp + ): { + keys(): string[]; + (id: string): T; + }; +}; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), { teardown: { destroyAfterEach: true } }); + +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().map(context); diff --git a/apps/store/search-component-store/tsconfig.lib.json b/apps/store/search-component-store/tsconfig.lib.json new file mode 100644 index 000000000..a7141c543 --- /dev/null +++ b/apps/store/search-component-store/tsconfig.lib.json @@ -0,0 +1,20 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "outDir": "../../../out-tsc/lib", + "target": "es2015", + "declaration": true, + "declarationMap": true, + "inlineSources": true, + "types": [], + "lib": [ + "dom", + "es2018" + ] + }, + "exclude": [ + "src/test.ts", + "**/*.spec.ts" + ] +} diff --git a/apps/store/search-component-store/tsconfig.lib.prod.json b/apps/store/search-component-store/tsconfig.lib.prod.json new file mode 100644 index 000000000..06de549e1 --- /dev/null +++ b/apps/store/search-component-store/tsconfig.lib.prod.json @@ -0,0 +1,10 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.lib.json", + "compilerOptions": { + "declarationMap": false + }, + "angularCompilerOptions": { + "compilationMode": "partial" + } +} diff --git a/apps/store/search-component-store/tsconfig.spec.json b/apps/store/search-component-store/tsconfig.spec.json new file mode 100644 index 000000000..85392ee8f --- /dev/null +++ b/apps/store/search-component-store/tsconfig.spec.json @@ -0,0 +1,17 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "outDir": "../../../out-tsc/spec", + "types": [ + "jasmine" + ] + }, + "files": [ + "src/test.ts" + ], + "include": [ + "**/*.spec.ts", + "**/*.d.ts" + ] +} diff --git a/apps/swagger/availability/src/test.ts b/apps/swagger/availability/src/test.ts index e11ff1c97..b1cc186b6 100644 --- a/apps/swagger/availability/src/test.ts +++ b/apps/swagger/availability/src/test.ts @@ -1,8 +1,8 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files import 'core-js/es7/reflect'; -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, diff --git a/apps/swagger/availability/tsconfig.lib.json b/apps/swagger/availability/tsconfig.lib.json index 26afc021c..396ad05d1 100644 --- a/apps/swagger/availability/tsconfig.lib.json +++ b/apps/swagger/availability/tsconfig.lib.json @@ -2,6 +2,7 @@ "extends": "../../../tsconfig.json", "compilerOptions": { "outDir": "../../../out-tsc/lib", + "declarationMap": true, "target": "es2015", "module": "es2015", "moduleResolution": "node", diff --git a/apps/swagger/availability/tsconfig.lib.prod.json b/apps/swagger/availability/tsconfig.lib.prod.json index b1d501abb..2617a83e3 100644 --- a/apps/swagger/availability/tsconfig.lib.prod.json +++ b/apps/swagger/availability/tsconfig.lib.prod.json @@ -1,5 +1,8 @@ { "extends": "./tsconfig.lib.json", + "compilerOptions": { + "declarationMap": false + }, "angularCompilerOptions": { "enableIvy": false } diff --git a/apps/swagger/cat/src/test.ts b/apps/swagger/cat/src/test.ts index e11ff1c97..b1cc186b6 100644 --- a/apps/swagger/cat/src/test.ts +++ b/apps/swagger/cat/src/test.ts @@ -1,8 +1,8 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files import 'core-js/es7/reflect'; -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, diff --git a/apps/swagger/cat/tsconfig.lib.json b/apps/swagger/cat/tsconfig.lib.json index 26afc021c..396ad05d1 100644 --- a/apps/swagger/cat/tsconfig.lib.json +++ b/apps/swagger/cat/tsconfig.lib.json @@ -2,6 +2,7 @@ "extends": "../../../tsconfig.json", "compilerOptions": { "outDir": "../../../out-tsc/lib", + "declarationMap": true, "target": "es2015", "module": "es2015", "moduleResolution": "node", diff --git a/apps/swagger/cat/tsconfig.lib.prod.json b/apps/swagger/cat/tsconfig.lib.prod.json index b1d501abb..2617a83e3 100644 --- a/apps/swagger/cat/tsconfig.lib.prod.json +++ b/apps/swagger/cat/tsconfig.lib.prod.json @@ -1,5 +1,8 @@ { "extends": "./tsconfig.lib.json", + "compilerOptions": { + "declarationMap": false + }, "angularCompilerOptions": { "enableIvy": false } diff --git a/apps/swagger/checkout/src/test.ts b/apps/swagger/checkout/src/test.ts index e11ff1c97..b1cc186b6 100644 --- a/apps/swagger/checkout/src/test.ts +++ b/apps/swagger/checkout/src/test.ts @@ -1,8 +1,8 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files import 'core-js/es7/reflect'; -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, diff --git a/apps/swagger/checkout/tsconfig.lib.json b/apps/swagger/checkout/tsconfig.lib.json index 26afc021c..396ad05d1 100644 --- a/apps/swagger/checkout/tsconfig.lib.json +++ b/apps/swagger/checkout/tsconfig.lib.json @@ -2,6 +2,7 @@ "extends": "../../../tsconfig.json", "compilerOptions": { "outDir": "../../../out-tsc/lib", + "declarationMap": true, "target": "es2015", "module": "es2015", "moduleResolution": "node", diff --git a/apps/swagger/checkout/tsconfig.lib.prod.json b/apps/swagger/checkout/tsconfig.lib.prod.json index b1d501abb..2617a83e3 100644 --- a/apps/swagger/checkout/tsconfig.lib.prod.json +++ b/apps/swagger/checkout/tsconfig.lib.prod.json @@ -1,5 +1,8 @@ { "extends": "./tsconfig.lib.json", + "compilerOptions": { + "declarationMap": false + }, "angularCompilerOptions": { "enableIvy": false } diff --git a/apps/swagger/crm/src/lib/models.ts b/apps/swagger/crm/src/lib/models.ts index 11396bb20..c56d42f24 100644 --- a/apps/swagger/crm/src/lib/models.ts +++ b/apps/swagger/crm/src/lib/models.ts @@ -11,6 +11,10 @@ export { KeyValueDTOOfStringAndString } from './models/key-value-dtoof-string-an export { IPublicUserInfo } from './models/ipublic-user-info'; export { ProblemDetails } from './models/problem-details'; export { ResponseArgsOfIEnumerableOfInputDTO } from './models/response-args-of-ienumerable-of-input-dto'; +export { ResponseArgsOfQuerySettingsDTO } from './models/response-args-of-query-settings-dto'; +export { QuerySettingsDTO } from './models/query-settings-dto'; +export { InputGroupDTO } from './models/input-group-dto'; +export { OrderByDTO } from './models/order-by-dto'; export { ListResponseArgsOfCustomerInfoDTO } from './models/list-response-args-of-customer-info-dto'; export { ResponseArgsOfIEnumerableOfCustomerInfoDTO } from './models/response-args-of-ienumerable-of-customer-info-dto'; export { CustomerInfoDTO } from './models/customer-info-dto'; @@ -73,7 +77,6 @@ export { EntityDTOOfCustomerInfoDTOAndICustomer } from './models/entity-dtoof-cu export { ReadOnlyEntityDTOOfCustomerInfoDTOAndICustomer } from './models/read-only-entity-dtoof-customer-info-dtoand-icustomer'; export { QueryTokenDTO } from './models/query-token-dto'; export { QueryTokenDTO2 } from './models/query-token-dto2'; -export { OrderByDTO } from './models/order-by-dto'; export { ResponseArgsOfCustomerDTO } from './models/response-args-of-customer-dto'; export { ResponseArgsOfShippingAddressDTO } from './models/response-args-of-shipping-address-dto'; export { ResponseArgsOfAssignedPayerDTO } from './models/response-args-of-assigned-payer-dto'; diff --git a/apps/swagger/crm/src/lib/models/input-group-dto.ts b/apps/swagger/crm/src/lib/models/input-group-dto.ts new file mode 100644 index 000000000..558455e56 --- /dev/null +++ b/apps/swagger/crm/src/lib/models/input-group-dto.ts @@ -0,0 +1,8 @@ +/* tslint:disable */ +import { InputDTO } from './input-dto'; +export interface InputGroupDTO { + description?: string; + group?: string; + input?: Array; + label?: string; +} diff --git a/apps/swagger/crm/src/lib/models/problem-details.ts b/apps/swagger/crm/src/lib/models/problem-details.ts index 370f918ed..3145234d6 100644 --- a/apps/swagger/crm/src/lib/models/problem-details.ts +++ b/apps/swagger/crm/src/lib/models/problem-details.ts @@ -1,7 +1,7 @@ /* tslint:disable */ export interface ProblemDetails { detail?: string; - extensions?: {[key: string]: any}; + extensions: {[key: string]: any}; instance?: string; status?: number; title?: string; diff --git a/apps/swagger/crm/src/lib/models/query-settings-dto.ts b/apps/swagger/crm/src/lib/models/query-settings-dto.ts new file mode 100644 index 000000000..6d58c4cc6 --- /dev/null +++ b/apps/swagger/crm/src/lib/models/query-settings-dto.ts @@ -0,0 +1,8 @@ +/* tslint:disable */ +import { InputGroupDTO } from './input-group-dto'; +import { OrderByDTO } from './order-by-dto'; +export interface QuerySettingsDTO { + filter?: Array; + input?: Array; + orderBy?: Array; +} diff --git a/apps/swagger/crm/src/lib/models/response-args-of-query-settings-dto.ts b/apps/swagger/crm/src/lib/models/response-args-of-query-settings-dto.ts new file mode 100644 index 000000000..1cf6c1a3b --- /dev/null +++ b/apps/swagger/crm/src/lib/models/response-args-of-query-settings-dto.ts @@ -0,0 +1,6 @@ +/* tslint:disable */ +import { ResponseArgs } from './response-args'; +import { QuerySettingsDTO } from './query-settings-dto'; +export interface ResponseArgsOfQuerySettingsDTO extends ResponseArgs{ + result?: QuerySettingsDTO; +} diff --git a/apps/swagger/crm/src/lib/services/customer.service.ts b/apps/swagger/crm/src/lib/services/customer.service.ts index 6b8005035..13c5602a2 100644 --- a/apps/swagger/crm/src/lib/services/customer.service.ts +++ b/apps/swagger/crm/src/lib/services/customer.service.ts @@ -9,6 +9,7 @@ import { map as __map, filter as __filter } from 'rxjs/operators'; import { ResponseArgsOfInputDTO } from '../models/response-args-of-input-dto'; import { ResponseArgsOfIEnumerableOfInputDTO } from '../models/response-args-of-ienumerable-of-input-dto'; +import { ResponseArgsOfQuerySettingsDTO } from '../models/response-args-of-query-settings-dto'; import { ListResponseArgsOfCustomerInfoDTO } from '../models/list-response-args-of-customer-info-dto'; import { QueryTokenDTO } from '../models/query-token-dto'; import { ResponseArgsOfCustomerDTO } from '../models/response-args-of-customer-dto'; @@ -34,6 +35,7 @@ import { ResponseArgsOfHistoryDTO } from '../models/response-args-of-history-dto class CustomerService extends __BaseService { static readonly CustomerCanExtendCustomerPath = '/customer/{customerId}/canextend'; static readonly CustomerQueryCustomerFilterPath = '/customer/s/filter'; + static readonly CustomerCustomerQuerySettingsPath = '/customer/s/settings'; static readonly CustomerListCustomersPath = '/customer/s'; static readonly CustomerGetCustomerPath = '/customer/{customerId}'; static readonly CustomerUpdateCustomerPath = '/customer/{customerId}'; @@ -83,7 +85,7 @@ class CustomerService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'GET', - this.rootUrl + `/customer/${encodeURIComponent(params.customerId)}/canextend`, + this.rootUrl + `/customer/${encodeURIComponent(String(params.customerId))}/canextend`, __body, { headers: __headers, @@ -145,6 +147,39 @@ class CustomerService extends __BaseService { ); } + /** + * Wareneingang-Filter + */ + CustomerCustomerQuerySettingsResponse(): __Observable<__StrictHttpResponse> { + let __params = this.newParams(); + let __headers = new HttpHeaders(); + let __body: any = null; + let req = new HttpRequest( + 'GET', + this.rootUrl + `/customer/s/settings`, + __body, + { + headers: __headers, + params: __params, + responseType: 'json' + }); + + return this.http.request(req).pipe( + __filter(_r => _r instanceof HttpResponse), + __map((_r) => { + return _r as __StrictHttpResponse; + }) + ); + } + /** + * Wareneingang-Filter + */ + CustomerCustomerQuerySettings(): __Observable { + return this.CustomerCustomerQuerySettingsResponse().pipe( + __map(_r => _r.body as ResponseArgsOfQuerySettingsDTO) + ); + } + /** * Kundensuche * @param queryToken Suchkriterien @@ -197,7 +232,7 @@ class CustomerService extends __BaseService { if (params.eagerLoading != null) __params = __params.set('eagerLoading', params.eagerLoading.toString()); let req = new HttpRequest( 'GET', - this.rootUrl + `/customer/${encodeURIComponent(params.customerId)}`, + this.rootUrl + `/customer/${encodeURIComponent(String(params.customerId))}`, __body, { headers: __headers, @@ -242,7 +277,7 @@ class CustomerService extends __BaseService { __body = params.customer; let req = new HttpRequest( 'PUT', - this.rootUrl + `/customer/${encodeURIComponent(params.customerId)}`, + this.rootUrl + `/customer/${encodeURIComponent(String(params.customerId))}`, __body, { headers: __headers, @@ -290,7 +325,7 @@ class CustomerService extends __BaseService { if (params.eagerLoading != null) __params = __params.set('eagerLoading', params.eagerLoading.toString()); let req = new HttpRequest( 'PATCH', - this.rootUrl + `/customer/${encodeURIComponent(params.customerId)}`, + this.rootUrl + `/customer/${encodeURIComponent(String(params.customerId))}`, __body, { headers: __headers, @@ -339,7 +374,7 @@ class CustomerService extends __BaseService { if (params.deletionComment != null) __params = __params.set('deletionComment', params.deletionComment.toString()); let req = new HttpRequest( 'DELETE', - this.rootUrl + `/customer/${encodeURIComponent(params.customerId)}`, + this.rootUrl + `/customer/${encodeURIComponent(String(params.customerId))}`, __body, { headers: __headers, @@ -422,7 +457,7 @@ class CustomerService extends __BaseService { let req = new HttpRequest( 'POST', - this.rootUrl + `/customer/${encodeURIComponent(params.customerId)}/shippingaddress`, + this.rootUrl + `/customer/${encodeURIComponent(String(params.customerId))}/shippingaddress`, __body, { headers: __headers, @@ -469,7 +504,7 @@ class CustomerService extends __BaseService { if (params.skip != null) __params = __params.set('skip', params.skip.toString()); let req = new HttpRequest( 'GET', - this.rootUrl + `/customer/${encodeURIComponent(params.customerId)}/shippingaddress`, + this.rootUrl + `/customer/${encodeURIComponent(String(params.customerId))}/shippingaddress`, __body, { headers: __headers, @@ -557,7 +592,7 @@ class CustomerService extends __BaseService { if (params.eagerLoading != null) __params = __params.set('eagerLoading', params.eagerLoading.toString()); let req = new HttpRequest( 'POST', - this.rootUrl + `/customer/${encodeURIComponent(params.customerId)}/payer`, + this.rootUrl + `/customer/${encodeURIComponent(String(params.customerId))}/payer`, __body, { headers: __headers, @@ -606,7 +641,7 @@ class CustomerService extends __BaseService { if (params.deactivationComment != null) __params = __params.set('deactivationComment', params.deactivationComment.toString()); let req = new HttpRequest( 'PATCH', - this.rootUrl + `/customer/${encodeURIComponent(params.customerId)}/deactivate`, + this.rootUrl + `/customer/${encodeURIComponent(String(params.customerId))}/deactivate`, __body, { headers: __headers, @@ -682,7 +717,7 @@ class CustomerService extends __BaseService { let req = new HttpRequest( 'GET', - this.rootUrl + `/customer/${encodeURIComponent(customerId)}/bonuscard`, + this.rootUrl + `/customer/${encodeURIComponent(String(customerId))}/bonuscard`, __body, { headers: __headers, @@ -854,7 +889,7 @@ class CustomerService extends __BaseService { let req = new HttpRequest( 'PUT', - this.rootUrl + `/customer/${encodeURIComponent(params.customerId)}/shippingaddress/${encodeURIComponent(params.shippingAddressId)}`, + this.rootUrl + `/customer/${encodeURIComponent(String(params.customerId))}/shippingaddress/${encodeURIComponent(String(params.shippingAddressId))}`, __body, { headers: __headers, @@ -904,7 +939,7 @@ class CustomerService extends __BaseService { if (params.deletionComment != null) __params = __params.set('deletionComment', params.deletionComment.toString()); let req = new HttpRequest( 'DELETE', - this.rootUrl + `/customer/${encodeURIComponent(params.customerId)}/shippingaddress/${encodeURIComponent(params.shippingAddressId)}`, + this.rootUrl + `/customer/${encodeURIComponent(String(params.customerId))}/shippingaddress/${encodeURIComponent(String(params.shippingAddressId))}`, __body, { headers: __headers, @@ -987,7 +1022,7 @@ class CustomerService extends __BaseService { if (params.skip != null) __params = __params.set('skip', params.skip.toString()); let req = new HttpRequest( 'GET', - this.rootUrl + `/customer/${encodeURIComponent(params.customerId)}/assignedpayers`, + this.rootUrl + `/customer/${encodeURIComponent(String(params.customerId))}/assignedpayers`, __body, { headers: __headers, @@ -1038,7 +1073,7 @@ class CustomerService extends __BaseService { if (params.eagerLoading != null) __params = __params.set('eagerLoading', params.eagerLoading.toString()); let req = new HttpRequest( 'PATCH', - this.rootUrl + `/customer/${encodeURIComponent(params.customerId)}/payer/${encodeURIComponent(params.payerId)}/modifydefaultflag`, + this.rootUrl + `/customer/${encodeURIComponent(String(params.customerId))}/payer/${encodeURIComponent(String(params.payerId))}/modifydefaultflag`, __body, { headers: __headers, @@ -1085,7 +1120,7 @@ class CustomerService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'GET', - this.rootUrl + `/customer/${encodeURIComponent(params.customerId)}/history`, + this.rootUrl + `/customer/${encodeURIComponent(String(params.customerId))}/history`, __body, { headers: __headers, @@ -1123,7 +1158,7 @@ class CustomerService extends __BaseService { let req = new HttpRequest( 'GET', - this.rootUrl + `/customer/shippingaddress/${encodeURIComponent(shippingaddressId)}`, + this.rootUrl + `/customer/shippingaddress/${encodeURIComponent(String(shippingaddressId))}`, __body, { headers: __headers, @@ -1165,7 +1200,7 @@ class CustomerService extends __BaseService { if (params.isDefault != null) __params = __params.set('isDefault', params.isDefault.toString()); let req = new HttpRequest( 'PATCH', - this.rootUrl + `/customer/${encodeURIComponent(params.customerId)}/shippingaddress/${encodeURIComponent(params.shippingAddressId)}/modifydefaultflag`, + this.rootUrl + `/customer/${encodeURIComponent(String(params.customerId))}/shippingaddress/${encodeURIComponent(String(params.shippingAddressId))}/modifydefaultflag`, __body, { headers: __headers, @@ -1213,7 +1248,7 @@ class CustomerService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'GET', - this.rootUrl + `/customer/${encodeURIComponent(params.customerId)}/shippingaddress/${encodeURIComponent(params.shippingAddressId)}/history`, + this.rootUrl + `/customer/${encodeURIComponent(String(params.customerId))}/shippingaddress/${encodeURIComponent(String(params.shippingAddressId))}/history`, __body, { headers: __headers, diff --git a/apps/swagger/crm/src/lib/services/opt-in.service.ts b/apps/swagger/crm/src/lib/services/opt-in.service.ts index dd1034403..b23de58c0 100644 --- a/apps/swagger/crm/src/lib/services/opt-in.service.ts +++ b/apps/swagger/crm/src/lib/services/opt-in.service.ts @@ -35,7 +35,7 @@ class OptInService extends __BaseService { let req = new HttpRequest( 'GET', - this.rootUrl + `/optin/${encodeURIComponent(optInId)}`, + this.rootUrl + `/optin/${encodeURIComponent(String(optInId))}`, __body, { headers: __headers, @@ -74,7 +74,7 @@ class OptInService extends __BaseService { __body = params.optIn; let req = new HttpRequest( 'PUT', - this.rootUrl + `/optin/${encodeURIComponent(params.optInId)}`, + this.rootUrl + `/optin/${encodeURIComponent(String(params.optInId))}`, __body, { headers: __headers, @@ -112,7 +112,7 @@ class OptInService extends __BaseService { let req = new HttpRequest( 'DELETE', - this.rootUrl + `/optin/${encodeURIComponent(optInId)}`, + this.rootUrl + `/optin/${encodeURIComponent(String(optInId))}`, __body, { headers: __headers, diff --git a/apps/swagger/crm/src/lib/services/payer.service.ts b/apps/swagger/crm/src/lib/services/payer.service.ts index d7076ba44..0b9aaea9b 100644 --- a/apps/swagger/crm/src/lib/services/payer.service.ts +++ b/apps/swagger/crm/src/lib/services/payer.service.ts @@ -81,7 +81,7 @@ class PayerService extends __BaseService { __body = params.payer; let req = new HttpRequest( 'PUT', - this.rootUrl + `/payer/${encodeURIComponent(params.payerId)}`, + this.rootUrl + `/payer/${encodeURIComponent(String(params.payerId))}`, __body, { headers: __headers, @@ -120,7 +120,7 @@ class PayerService extends __BaseService { let req = new HttpRequest( 'GET', - this.rootUrl + `/payer/${encodeURIComponent(payerId)}`, + this.rootUrl + `/payer/${encodeURIComponent(String(payerId))}`, __body, { headers: __headers, @@ -160,7 +160,7 @@ class PayerService extends __BaseService { if (params.deactivationComment != null) __params = __params.set('deactivationComment', params.deactivationComment.toString()); let req = new HttpRequest( 'PATCH', - this.rootUrl + `/payer/${encodeURIComponent(params.payerId)}/deactivate`, + this.rootUrl + `/payer/${encodeURIComponent(String(params.payerId))}/deactivate`, __body, { headers: __headers, @@ -207,7 +207,7 @@ class PayerService extends __BaseService { if (params.deletionComment != null) __params = __params.set('deletionComment', params.deletionComment.toString()); let req = new HttpRequest( 'DELETE', - this.rootUrl + `/payer/${encodeURIComponent(params.payerId)}/delete`, + this.rootUrl + `/payer/${encodeURIComponent(String(params.payerId))}/delete`, __body, { headers: __headers, @@ -253,7 +253,7 @@ class PayerService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'GET', - this.rootUrl + `/payer/${encodeURIComponent(params.payerId)}/history`, + this.rootUrl + `/payer/${encodeURIComponent(String(params.payerId))}/history`, __body, { headers: __headers, diff --git a/apps/swagger/crm/src/test.ts b/apps/swagger/crm/src/test.ts index e11ff1c97..b1cc186b6 100644 --- a/apps/swagger/crm/src/test.ts +++ b/apps/swagger/crm/src/test.ts @@ -1,8 +1,8 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files import 'core-js/es7/reflect'; -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, diff --git a/apps/swagger/crm/tsconfig.lib.json b/apps/swagger/crm/tsconfig.lib.json index 26afc021c..396ad05d1 100644 --- a/apps/swagger/crm/tsconfig.lib.json +++ b/apps/swagger/crm/tsconfig.lib.json @@ -2,6 +2,7 @@ "extends": "../../../tsconfig.json", "compilerOptions": { "outDir": "../../../out-tsc/lib", + "declarationMap": true, "target": "es2015", "module": "es2015", "moduleResolution": "node", diff --git a/apps/swagger/crm/tsconfig.lib.prod.json b/apps/swagger/crm/tsconfig.lib.prod.json index b1d501abb..2617a83e3 100644 --- a/apps/swagger/crm/tsconfig.lib.prod.json +++ b/apps/swagger/crm/tsconfig.lib.prod.json @@ -1,5 +1,8 @@ { "extends": "./tsconfig.lib.json", + "compilerOptions": { + "declarationMap": false + }, "angularCompilerOptions": { "enableIvy": false } diff --git a/apps/swagger/eis/src/lib/models.ts b/apps/swagger/eis/src/lib/models.ts index ad7218acf..de86fc2f9 100644 --- a/apps/swagger/eis/src/lib/models.ts +++ b/apps/swagger/eis/src/lib/models.ts @@ -4,6 +4,7 @@ export { EntityDTOContainerOfInfoDTO } from './models/entity-dtocontainer-of-inf export { EntityDTOReferenceContainer } from './models/entity-dtoreference-container'; export { ExternalReferenceDTO } from './models/external-reference-dto'; export { EntityStatus } from './models/entity-status'; +export { TouchedBase } from './models/touched-base'; export { EntityDTOContainerOfDisplayInfoDTO } from './models/entity-dtocontainer-of-display-info-dto'; export { DisplayInfoDTO } from './models/display-info-dto'; export { ProcessingStatus } from './models/processing-status'; @@ -49,8 +50,11 @@ export { InputDTO } from './models/input-dto'; export { InputType } from './models/input-type'; export { InputOptionsDTO } from './models/input-options-dto'; export { OptionDTO } from './models/option-dto'; -export { QueryTokenDTO } from './models/query-token-dto'; +export { ResponseArgsOfQuerySettingsDTO } from './models/response-args-of-query-settings-dto'; +export { QuerySettingsDTO } from './models/query-settings-dto'; +export { InputGroupDTO } from './models/input-group-dto'; export { OrderByDTO } from './models/order-by-dto'; +export { QueryTokenDTO } from './models/query-token-dto'; export { DashboardInfoRequest } from './models/dashboard-info-request'; export { ResponseArgsOfEntityDTOContainerOfBranchDTO } from './models/response-args-of-entity-dtocontainer-of-branch-dto'; export { ResponseArgsOfDisplayInfoDTO } from './models/response-args-of-display-info-dto'; diff --git a/apps/swagger/eis/src/lib/models/entity-dto.ts b/apps/swagger/eis/src/lib/models/entity-dto.ts index b5049f54e..8e00ab865 100644 --- a/apps/swagger/eis/src/lib/models/entity-dto.ts +++ b/apps/swagger/eis/src/lib/models/entity-dto.ts @@ -1,6 +1,7 @@ /* tslint:disable */ +import { TouchedBase } from './touched-base'; import { EntityStatus } from './entity-status'; -export interface EntityDTO { +export interface EntityDTO extends TouchedBase{ changed?: string; created?: string; id?: number; diff --git a/apps/swagger/eis/src/lib/models/entity-dtoreference-container.ts b/apps/swagger/eis/src/lib/models/entity-dtoreference-container.ts index 096b961c3..45253f44f 100644 --- a/apps/swagger/eis/src/lib/models/entity-dtoreference-container.ts +++ b/apps/swagger/eis/src/lib/models/entity-dtoreference-container.ts @@ -1,6 +1,7 @@ /* tslint:disable */ +import { TouchedBase } from './touched-base'; import { ExternalReferenceDTO } from './external-reference-dto'; -export interface EntityDTOReferenceContainer { +export interface EntityDTOReferenceContainer extends TouchedBase{ displayLabel?: string; enabled?: boolean; externalReference?: ExternalReferenceDTO; diff --git a/apps/swagger/eis/src/lib/models/external-reference-dto.ts b/apps/swagger/eis/src/lib/models/external-reference-dto.ts index 074d10f02..703821aed 100644 --- a/apps/swagger/eis/src/lib/models/external-reference-dto.ts +++ b/apps/swagger/eis/src/lib/models/external-reference-dto.ts @@ -1,6 +1,7 @@ /* tslint:disable */ +import { TouchedBase } from './touched-base'; import { EntityStatus } from './entity-status'; -export interface ExternalReferenceDTO { +export interface ExternalReferenceDTO extends TouchedBase{ externalChanged?: string; externalCreated?: string; externalNumber?: string; diff --git a/apps/swagger/eis/src/lib/models/input-dto.ts b/apps/swagger/eis/src/lib/models/input-dto.ts index 109c6030d..a44cca825 100644 --- a/apps/swagger/eis/src/lib/models/input-dto.ts +++ b/apps/swagger/eis/src/lib/models/input-dto.ts @@ -3,10 +3,14 @@ import { InputOptionsDTO } from './input-options-dto'; import { InputType } from './input-type'; export interface InputDTO { constraint?: string; + description?: string; key?: string; label?: string; + maxValue?: string; + minValue?: string; options?: InputOptionsDTO; placeholder?: string; + target?: string; type: InputType; value?: string; } diff --git a/apps/swagger/eis/src/lib/models/input-group-dto.ts b/apps/swagger/eis/src/lib/models/input-group-dto.ts new file mode 100644 index 000000000..558455e56 --- /dev/null +++ b/apps/swagger/eis/src/lib/models/input-group-dto.ts @@ -0,0 +1,8 @@ +/* tslint:disable */ +import { InputDTO } from './input-dto'; +export interface InputGroupDTO { + description?: string; + group?: string; + input?: Array; + label?: string; +} diff --git a/apps/swagger/eis/src/lib/models/input-type.ts b/apps/swagger/eis/src/lib/models/input-type.ts index 9479a8088..d9c742e77 100644 --- a/apps/swagger/eis/src/lib/models/input-type.ts +++ b/apps/swagger/eis/src/lib/models/input-type.ts @@ -1,2 +1,2 @@ /* tslint:disable */ -export type InputType = 0 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 96; \ No newline at end of file +export type InputType = 0 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128 | 256 | 512 | 1024 | 2048 | 3072 | 4096 | 8192 | 12288; \ No newline at end of file diff --git a/apps/swagger/eis/src/lib/models/option-dto.ts b/apps/swagger/eis/src/lib/models/option-dto.ts index 44a5b882c..4d13492b3 100644 --- a/apps/swagger/eis/src/lib/models/option-dto.ts +++ b/apps/swagger/eis/src/lib/models/option-dto.ts @@ -1,8 +1,12 @@ /* tslint:disable */ export interface OptionDTO { + description?: string; enabled?: boolean; key?: string; label?: string; + maxValue?: string; + minValue?: string; + placeholder?: string; selected?: boolean; value?: string; values?: Array; diff --git a/apps/swagger/eis/src/lib/models/problem-details.ts b/apps/swagger/eis/src/lib/models/problem-details.ts index 370f918ed..3145234d6 100644 --- a/apps/swagger/eis/src/lib/models/problem-details.ts +++ b/apps/swagger/eis/src/lib/models/problem-details.ts @@ -1,7 +1,7 @@ /* tslint:disable */ export interface ProblemDetails { detail?: string; - extensions?: {[key: string]: any}; + extensions: {[key: string]: any}; instance?: string; status?: number; title?: string; diff --git a/apps/swagger/eis/src/lib/models/query-settings-dto.ts b/apps/swagger/eis/src/lib/models/query-settings-dto.ts new file mode 100644 index 000000000..6d58c4cc6 --- /dev/null +++ b/apps/swagger/eis/src/lib/models/query-settings-dto.ts @@ -0,0 +1,8 @@ +/* tslint:disable */ +import { InputGroupDTO } from './input-group-dto'; +import { OrderByDTO } from './order-by-dto'; +export interface QuerySettingsDTO { + filter?: Array; + input?: Array; + orderBy?: Array; +} diff --git a/apps/swagger/eis/src/lib/models/response-args-of-query-settings-dto.ts b/apps/swagger/eis/src/lib/models/response-args-of-query-settings-dto.ts new file mode 100644 index 000000000..1cf6c1a3b --- /dev/null +++ b/apps/swagger/eis/src/lib/models/response-args-of-query-settings-dto.ts @@ -0,0 +1,6 @@ +/* tslint:disable */ +import { ResponseArgs } from './response-args'; +import { QuerySettingsDTO } from './query-settings-dto'; +export interface ResponseArgsOfQuerySettingsDTO extends ResponseArgs{ + result?: QuerySettingsDTO; +} diff --git a/apps/swagger/eis/src/lib/models/touched-base.ts b/apps/swagger/eis/src/lib/models/touched-base.ts new file mode 100644 index 000000000..1075419bc --- /dev/null +++ b/apps/swagger/eis/src/lib/models/touched-base.ts @@ -0,0 +1,3 @@ +/* tslint:disable */ +export interface TouchedBase { +} diff --git a/apps/swagger/eis/src/lib/services/eisbackend.service.ts b/apps/swagger/eis/src/lib/services/eisbackend.service.ts index 53042fc1b..8e4cac6d9 100644 --- a/apps/swagger/eis/src/lib/services/eisbackend.service.ts +++ b/apps/swagger/eis/src/lib/services/eisbackend.service.ts @@ -153,7 +153,7 @@ class EISBackendService extends __BaseService { let req = new HttpRequest( 'DELETE', - this.rootUrl + `/eis/delete/${encodeURIComponent(String(infoId))}`, + this.rootUrl + `/eis/delete/${encodeURIComponent(infoId)}`, __body, { headers: __headers, @@ -197,7 +197,7 @@ class EISBackendService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'PUT', - this.rootUrl + `/eis/updateapproval/${encodeURIComponent(String(params.infoId))}`, + this.rootUrl + `/eis/updateapproval/${encodeURIComponent(params.infoId)}`, __body, { headers: __headers, @@ -247,7 +247,7 @@ class EISBackendService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'PUT', - this.rootUrl + `/eis/updatereject/${encodeURIComponent(String(params.infoId))}`, + this.rootUrl + `/eis/updatereject/${encodeURIComponent(params.infoId)}`, __body, { headers: __headers, @@ -297,7 +297,7 @@ class EISBackendService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'PUT', - this.rootUrl + `/eis/info/${encodeURIComponent(String(params.infoId))}`, + this.rootUrl + `/eis/info/${encodeURIComponent(params.infoId)}`, __body, { headers: __headers, @@ -347,7 +347,7 @@ class EISBackendService extends __BaseService { if (params.eagerLoading != null) __params = __params.set('eagerLoading', params.eagerLoading.toString()); let req = new HttpRequest( 'GET', - this.rootUrl + `/eis/info/${encodeURIComponent(String(params.infoId))}`, + this.rootUrl + `/eis/info/${encodeURIComponent(params.infoId)}`, __body, { headers: __headers, @@ -397,7 +397,7 @@ class EISBackendService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'PATCH', - this.rootUrl + `/eis/info/approve/${encodeURIComponent(String(params.allowsave))}`, + this.rootUrl + `/eis/info/approve/${encodeURIComponent(params.allowsave)}`, __body, { headers: __headers, @@ -453,7 +453,7 @@ class EISBackendService extends __BaseService { if (params.eagerLoading != null) __params = __params.set('eagerLoading', params.eagerLoading.toString()); let req = new HttpRequest( 'GET', - this.rootUrl + `/eis/word/${encodeURIComponent(String(params.datum))}`, + this.rootUrl + `/eis/word/${encodeURIComponent(params.datum)}`, __body, { headers: __headers, diff --git a/apps/swagger/eis/src/lib/services/eispublic.service.ts b/apps/swagger/eis/src/lib/services/eispublic.service.ts index 90610ee26..c41d36732 100644 --- a/apps/swagger/eis/src/lib/services/eispublic.service.ts +++ b/apps/swagger/eis/src/lib/services/eispublic.service.ts @@ -10,6 +10,7 @@ import { map as __map, filter as __filter } from 'rxjs/operators'; import { ListResponseArgsOfDisplayInfoDTO } from '../models/list-response-args-of-display-info-dto'; import { DisplayInfoRequest } from '../models/display-info-request'; import { ResponseArgsOfIEnumerableOfInputDTO } from '../models/response-args-of-ienumerable-of-input-dto'; +import { ResponseArgsOfQuerySettingsDTO } from '../models/response-args-of-query-settings-dto'; import { QueryTokenDTO } from '../models/query-token-dto'; import { DashboardInfoRequest } from '../models/dashboard-info-request'; import { ResponseArgsOfEntityDTOContainerOfBranchDTO } from '../models/response-args-of-entity-dtocontainer-of-branch-dto'; @@ -30,6 +31,7 @@ class EISPublicService extends __BaseService { static readonly EISPublicGetDisplayInfosPath = '/eis/intranet/info'; static readonly EISPublicQueryDisplayInfoFilterPath = '/eis/intranet/filter'; static readonly EISPublicQueryDisplayInfoFilter2Path = '/eis/intranet/info/s/filter'; + static readonly EISPublicQueryDisplayInfoSettingsPath = '/eis/intranet/info/s/settings'; static readonly EISPublicQueryDisplayInfoPath = '/eis/intranet/info/s'; static readonly EISPublicGetDashboardInfosPath = '/eis/intranet/dashboard'; static readonly EISPublicGetCurrentBranchPath = '/eis/intranet/currentbranch'; @@ -166,6 +168,39 @@ class EISPublicService extends __BaseService { ); } + /** + * Filterwerte + */ + EISPublicQueryDisplayInfoSettingsResponse(): __Observable<__StrictHttpResponse> { + let __params = this.newParams(); + let __headers = new HttpHeaders(); + let __body: any = null; + let req = new HttpRequest( + 'GET', + this.rootUrl + `/eis/intranet/info/s/settings`, + __body, + { + headers: __headers, + params: __params, + responseType: 'json' + }); + + return this.http.request(req).pipe( + __filter(_r => _r instanceof HttpResponse), + __map((_r) => { + return _r as __StrictHttpResponse; + }) + ); + } + /** + * Filterwerte + */ + EISPublicQueryDisplayInfoSettings(): __Observable { + return this.EISPublicQueryDisplayInfoSettingsResponse().pipe( + __map(_r => _r.body as ResponseArgsOfQuerySettingsDTO) + ); + } + /** * Infos / Aufgaben suchen * @param params The `EISPublicService.EISPublicQueryDisplayInfoParams` containing the following parameters: @@ -353,7 +388,7 @@ class EISPublicService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'GET', - this.rootUrl + `/eis/intranet/info/${encodeURIComponent(String(params.infoId))}`, + this.rootUrl + `/eis/intranet/info/${encodeURIComponent(params.infoId)}`, __body, { headers: __headers, @@ -398,7 +433,7 @@ class EISPublicService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'PATCH', - this.rootUrl + `/eis/intranet/info/${encodeURIComponent(String(params.infoId))}/edit`, + this.rootUrl + `/eis/intranet/info/${encodeURIComponent(params.infoId)}/edit`, __body, { headers: __headers, @@ -443,7 +478,7 @@ class EISPublicService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'PATCH', - this.rootUrl + `/eis/intranet/info/${encodeURIComponent(String(params.infoId))}/reset`, + this.rootUrl + `/eis/intranet/info/${encodeURIComponent(params.infoId)}/reset`, __body, { headers: __headers, @@ -491,7 +526,7 @@ class EISPublicService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'PATCH', - this.rootUrl + `/eis/intranet/info/${encodeURIComponent(String(params.infoId))}/complete`, + this.rootUrl + `/eis/intranet/info/${encodeURIComponent(params.infoId)}/complete`, __body, { headers: __headers, @@ -538,7 +573,7 @@ class EISPublicService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'GET', - this.rootUrl + `/eis/intranet/info/${encodeURIComponent(String(params.infoId))}/file`, + this.rootUrl + `/eis/intranet/info/${encodeURIComponent(params.infoId)}/file`, __body, { headers: __headers, @@ -585,7 +620,7 @@ class EISPublicService extends __BaseService { if (params.download != null) __params = __params.set('download', params.download.toString()); let req = new HttpRequest( 'GET', - this.rootUrl + `/eis/intranet/file/${encodeURIComponent(String(params.fileId))}`, + this.rootUrl + `/eis/intranet/file/${encodeURIComponent(params.fileId)}`, __body, { headers: __headers, @@ -634,7 +669,7 @@ class EISPublicService extends __BaseService { if (params.download != null) __params = __params.set('download', params.download.toString()); let req = new HttpRequest( 'GET', - this.rootUrl + `/eis/intranet/info/${encodeURIComponent(String(params.infoId))}/image/teaser`, + this.rootUrl + `/eis/intranet/info/${encodeURIComponent(params.infoId)}/image/teaser`, __body, { headers: __headers, @@ -681,7 +716,7 @@ class EISPublicService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'GET', - this.rootUrl + `/eis/intranet/info/${encodeURIComponent(String(params.infoId))}/image`, + this.rootUrl + `/eis/intranet/info/${encodeURIComponent(params.infoId)}/image`, __body, { headers: __headers, @@ -726,7 +761,7 @@ class EISPublicService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'GET', - this.rootUrl + `/eis/intranet/info/${encodeURIComponent(String(params.infoId))}/article`, + this.rootUrl + `/eis/intranet/info/${encodeURIComponent(params.infoId)}/article`, __body, { headers: __headers, @@ -774,7 +809,7 @@ class EISPublicService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'GET', - this.rootUrl + `/eis/intranet/info/${encodeURIComponent(String(params.infoId))}/confirmation/${encodeURIComponent(String(params.confirmationId))}/file`, + this.rootUrl + `/eis/intranet/info/${encodeURIComponent(params.infoId)}/confirmation/${encodeURIComponent(params.confirmationId)}/file`, __body, { headers: __headers, @@ -824,7 +859,7 @@ class EISPublicService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'GET', - this.rootUrl + `/eis/intranet/branch/${encodeURIComponent(String(params.branchId))}/info/${encodeURIComponent(String(params.infoId))}/confirmation/file`, + this.rootUrl + `/eis/intranet/branch/${encodeURIComponent(params.branchId)}/info/${encodeURIComponent(params.infoId)}/confirmation/file`, __body, { headers: __headers, @@ -874,7 +909,7 @@ class EISPublicService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'GET', - this.rootUrl + `/eis/intranet/branch/${encodeURIComponent(String(params.branchId))}/info/${encodeURIComponent(String(params.infoId))}/comment`, + this.rootUrl + `/eis/intranet/branch/${encodeURIComponent(params.branchId)}/info/${encodeURIComponent(params.infoId)}/comment`, __body, { headers: __headers, @@ -924,7 +959,7 @@ class EISPublicService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'POST', - this.rootUrl + `/eis/intranet/info/${encodeURIComponent(String(params.infoId))}/comment`, + this.rootUrl + `/eis/intranet/info/${encodeURIComponent(params.infoId)}/comment`, __body, { headers: __headers, diff --git a/apps/swagger/eis/src/test.ts b/apps/swagger/eis/src/test.ts index 978c64fb8..ee9915d42 100644 --- a/apps/swagger/eis/src/test.ts +++ b/apps/swagger/eis/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, diff --git a/apps/swagger/eis/tsconfig.lib.json b/apps/swagger/eis/tsconfig.lib.json index 1c34c36b8..02b80298e 100644 --- a/apps/swagger/eis/tsconfig.lib.json +++ b/apps/swagger/eis/tsconfig.lib.json @@ -2,6 +2,7 @@ "extends": "../../../tsconfig.json", "compilerOptions": { "outDir": "../../../out-tsc/lib", + "declarationMap": true, "target": "es2015", "declaration": true, "inlineSources": true, diff --git a/apps/swagger/eis/tsconfig.lib.prod.json b/apps/swagger/eis/tsconfig.lib.prod.json index b1d501abb..2617a83e3 100644 --- a/apps/swagger/eis/tsconfig.lib.prod.json +++ b/apps/swagger/eis/tsconfig.lib.prod.json @@ -1,5 +1,8 @@ { "extends": "./tsconfig.lib.json", + "compilerOptions": { + "declarationMap": false + }, "angularCompilerOptions": { "enableIvy": false } diff --git a/apps/swagger/isa/src/test.ts b/apps/swagger/isa/src/test.ts index e11ff1c97..b1cc186b6 100644 --- a/apps/swagger/isa/src/test.ts +++ b/apps/swagger/isa/src/test.ts @@ -1,8 +1,8 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files import 'core-js/es7/reflect'; -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, diff --git a/apps/swagger/isa/tsconfig.lib.json b/apps/swagger/isa/tsconfig.lib.json index 26afc021c..396ad05d1 100644 --- a/apps/swagger/isa/tsconfig.lib.json +++ b/apps/swagger/isa/tsconfig.lib.json @@ -2,6 +2,7 @@ "extends": "../../../tsconfig.json", "compilerOptions": { "outDir": "../../../out-tsc/lib", + "declarationMap": true, "target": "es2015", "module": "es2015", "moduleResolution": "node", diff --git a/apps/swagger/isa/tsconfig.lib.prod.json b/apps/swagger/isa/tsconfig.lib.prod.json index b1d501abb..2617a83e3 100644 --- a/apps/swagger/isa/tsconfig.lib.prod.json +++ b/apps/swagger/isa/tsconfig.lib.prod.json @@ -1,5 +1,8 @@ { "extends": "./tsconfig.lib.json", + "compilerOptions": { + "declarationMap": false + }, "angularCompilerOptions": { "enableIvy": false } diff --git a/apps/swagger/oms/src/test.ts b/apps/swagger/oms/src/test.ts index e11ff1c97..b1cc186b6 100644 --- a/apps/swagger/oms/src/test.ts +++ b/apps/swagger/oms/src/test.ts @@ -1,8 +1,8 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files import 'core-js/es7/reflect'; -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, diff --git a/apps/swagger/oms/tsconfig.lib.json b/apps/swagger/oms/tsconfig.lib.json index 26afc021c..396ad05d1 100644 --- a/apps/swagger/oms/tsconfig.lib.json +++ b/apps/swagger/oms/tsconfig.lib.json @@ -2,6 +2,7 @@ "extends": "../../../tsconfig.json", "compilerOptions": { "outDir": "../../../out-tsc/lib", + "declarationMap": true, "target": "es2015", "module": "es2015", "moduleResolution": "node", diff --git a/apps/swagger/oms/tsconfig.lib.prod.json b/apps/swagger/oms/tsconfig.lib.prod.json index b1d501abb..2617a83e3 100644 --- a/apps/swagger/oms/tsconfig.lib.prod.json +++ b/apps/swagger/oms/tsconfig.lib.prod.json @@ -1,5 +1,8 @@ { "extends": "./tsconfig.lib.json", + "compilerOptions": { + "declarationMap": false + }, "angularCompilerOptions": { "enableIvy": false } diff --git a/apps/swagger/print/src/test.ts b/apps/swagger/print/src/test.ts index e11ff1c97..b1cc186b6 100644 --- a/apps/swagger/print/src/test.ts +++ b/apps/swagger/print/src/test.ts @@ -1,8 +1,8 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files import 'core-js/es7/reflect'; -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, diff --git a/apps/swagger/print/tsconfig.lib.json b/apps/swagger/print/tsconfig.lib.json index 26afc021c..396ad05d1 100644 --- a/apps/swagger/print/tsconfig.lib.json +++ b/apps/swagger/print/tsconfig.lib.json @@ -2,6 +2,7 @@ "extends": "../../../tsconfig.json", "compilerOptions": { "outDir": "../../../out-tsc/lib", + "declarationMap": true, "target": "es2015", "module": "es2015", "moduleResolution": "node", diff --git a/apps/swagger/print/tsconfig.lib.prod.json b/apps/swagger/print/tsconfig.lib.prod.json index b1d501abb..2617a83e3 100644 --- a/apps/swagger/print/tsconfig.lib.prod.json +++ b/apps/swagger/print/tsconfig.lib.prod.json @@ -1,5 +1,8 @@ { "extends": "./tsconfig.lib.json", + "compilerOptions": { + "declarationMap": false + }, "angularCompilerOptions": { "enableIvy": false } diff --git a/apps/swagger/remi/src/lib/models.ts b/apps/swagger/remi/src/lib/models.ts index 75a60ced8..6c8bd2251 100644 --- a/apps/swagger/remi/src/lib/models.ts +++ b/apps/swagger/remi/src/lib/models.ts @@ -127,7 +127,8 @@ export { QueryTokenDTO } from './models/query-token-dto'; export { OrderByDTO } from './models/order-by-dto'; export { BatchResponseArgsOfReturnItemDTOAndReturnItemDTO } from './models/batch-response-args-of-return-item-dtoand-return-item-dto'; export { KeyValuePairOfReturnItemDTOAndReturnItemDTO } from './models/key-value-pair-of-return-item-dtoand-return-item-dto'; -export { IResultOfReturnItemDTO } from './models/iresult-of-return-item-dto'; +export { ReturnValueOfReturnItemDTO } from './models/return-value-of-return-item-dto'; +export { ReturnValue } from './models/return-value'; export { KeyValuePairOfReturnItemDTOAndInteger } from './models/key-value-pair-of-return-item-dtoand-integer'; export { ResponseArgsOfInteger } from './models/response-args-of-integer'; export { ListResponseArgsOfReturnSuggestionDTO } from './models/list-response-args-of-return-suggestion-dto'; @@ -137,13 +138,16 @@ export { EntityDTOOfReturnSuggestionDTOAndIReturnSuggestion } from './models/ent export { ReadOnlyEntityDTOOfReturnSuggestionDTOAndIReturnSuggestion } from './models/read-only-entity-dtoof-return-suggestion-dtoand-ireturn-suggestion'; export { BatchResponseArgsOfReturnSuggestionDTOAndReturnSuggestionDTO } from './models/batch-response-args-of-return-suggestion-dtoand-return-suggestion-dto'; export { KeyValuePairOfReturnSuggestionDTOAndReturnSuggestionDTO } from './models/key-value-pair-of-return-suggestion-dtoand-return-suggestion-dto'; -export { IResultOfReturnSuggestionDTO } from './models/iresult-of-return-suggestion-dto'; +export { ReturnValueOfReturnSuggestionDTO } from './models/return-value-of-return-suggestion-dto'; export { KeyValuePairOfReturnSuggestionDTOAndInteger } from './models/key-value-pair-of-return-suggestion-dtoand-integer'; -export { ResponseArgsOfIEnumerableOfInputDTO } from './models/response-args-of-ienumerable-of-input-dto'; +export { ResponseArgsOfQuerySettingsDTO } from './models/response-args-of-query-settings-dto'; +export { QuerySettingsDTO } from './models/query-settings-dto'; +export { InputGroupDTO } from './models/input-group-dto'; export { InputDTO } from './models/input-dto'; export { InputType } from './models/input-type'; export { InputOptionsDTO } from './models/input-options-dto'; export { OptionDTO } from './models/option-dto'; +export { ResponseArgsOfIEnumerableOfInputDTO } from './models/response-args-of-ienumerable-of-input-dto'; export { ResponseArgsOfIEnumerableOfKeyValueDTOOfStringAndString } from './models/response-args-of-ienumerable-of-key-value-dtoof-string-and-string'; export { KeyValueDTOOfStringAndString } from './models/key-value-dtoof-string-and-string'; export { ResponseArgsOfIEnumerableOfValueTupleOfStringAndIntegerAndIntegerAndNullableIntegerAndString } from './models/response-args-of-ienumerable-of-value-tuple-of-string-and-integer-and-integer-and-nullable-integer-and-string'; diff --git a/apps/swagger/remi/src/lib/models/batch-response-args-of-return-item-dtoand-return-item-dto.ts b/apps/swagger/remi/src/lib/models/batch-response-args-of-return-item-dtoand-return-item-dto.ts index c3077399b..5aa62f795 100644 --- a/apps/swagger/remi/src/lib/models/batch-response-args-of-return-item-dtoand-return-item-dto.ts +++ b/apps/swagger/remi/src/lib/models/batch-response-args-of-return-item-dtoand-return-item-dto.ts @@ -1,16 +1,16 @@ /* tslint:disable */ import { ReturnItemDTO } from './return-item-dto'; import { KeyValuePairOfReturnItemDTOAndInteger } from './key-value-pair-of-return-item-dtoand-integer'; -import { IResultOfReturnItemDTO } from './iresult-of-return-item-dto'; +import { ReturnValueOfReturnItemDTO } from './return-value-of-return-item-dto'; import { KeyValuePairOfReturnItemDTOAndReturnItemDTO } from './key-value-pair-of-return-item-dtoand-return-item-dto'; export interface BatchResponseArgsOfReturnItemDTOAndReturnItemDTO { ambiguous?: Array; duplicates?: Array; error: boolean; - failed?: Array; + failed?: Array; invalidProperties?: {[key: string]: string}; message?: string; requestId?: number; successful?: Array; - unknown?: Array; + unknown?: Array; } diff --git a/apps/swagger/remi/src/lib/models/batch-response-args-of-return-suggestion-dtoand-return-suggestion-dto.ts b/apps/swagger/remi/src/lib/models/batch-response-args-of-return-suggestion-dtoand-return-suggestion-dto.ts index 312f35f89..1af11706a 100644 --- a/apps/swagger/remi/src/lib/models/batch-response-args-of-return-suggestion-dtoand-return-suggestion-dto.ts +++ b/apps/swagger/remi/src/lib/models/batch-response-args-of-return-suggestion-dtoand-return-suggestion-dto.ts @@ -1,16 +1,16 @@ /* tslint:disable */ import { ReturnSuggestionDTO } from './return-suggestion-dto'; import { KeyValuePairOfReturnSuggestionDTOAndInteger } from './key-value-pair-of-return-suggestion-dtoand-integer'; -import { IResultOfReturnSuggestionDTO } from './iresult-of-return-suggestion-dto'; +import { ReturnValueOfReturnSuggestionDTO } from './return-value-of-return-suggestion-dto'; import { KeyValuePairOfReturnSuggestionDTOAndReturnSuggestionDTO } from './key-value-pair-of-return-suggestion-dtoand-return-suggestion-dto'; export interface BatchResponseArgsOfReturnSuggestionDTOAndReturnSuggestionDTO { ambiguous?: Array; duplicates?: Array; error: boolean; - failed?: Array; + failed?: Array; invalidProperties?: {[key: string]: string}; message?: string; requestId?: number; successful?: Array; - unknown?: Array; + unknown?: Array; } diff --git a/apps/swagger/remi/src/lib/models/branch-dto.ts b/apps/swagger/remi/src/lib/models/branch-dto.ts index 05547fed3..04f74ba4f 100644 --- a/apps/swagger/remi/src/lib/models/branch-dto.ts +++ b/apps/swagger/remi/src/lib/models/branch-dto.ts @@ -6,7 +6,7 @@ import { EntityDTOContainerOfLabelDTO } from './entity-dtocontainer-of-label-dto export interface BranchDTO extends ReadOnlyEntityDTOOfBranchDTOAndIReadOnlyBranch{ address?: Address; branchNumber?: string; - branchType: BranchType; + branchType?: BranchType; isDefault?: string; isOnline?: boolean; key?: string; diff --git a/apps/swagger/remi/src/lib/models/input-group-dto.ts b/apps/swagger/remi/src/lib/models/input-group-dto.ts new file mode 100644 index 000000000..558455e56 --- /dev/null +++ b/apps/swagger/remi/src/lib/models/input-group-dto.ts @@ -0,0 +1,8 @@ +/* tslint:disable */ +import { InputDTO } from './input-dto'; +export interface InputGroupDTO { + description?: string; + group?: string; + input?: Array; + label?: string; +} diff --git a/apps/swagger/remi/src/lib/models/iresult-of-return-item-dto.ts b/apps/swagger/remi/src/lib/models/iresult-of-return-item-dto.ts deleted file mode 100644 index c47ffd3fa..000000000 --- a/apps/swagger/remi/src/lib/models/iresult-of-return-item-dto.ts +++ /dev/null @@ -1,5 +0,0 @@ -/* tslint:disable */ -import { ReturnItemDTO } from './return-item-dto'; -export interface IResultOfReturnItemDTO { - result?: ReturnItemDTO; -} diff --git a/apps/swagger/remi/src/lib/models/problem-details.ts b/apps/swagger/remi/src/lib/models/problem-details.ts index 370f918ed..3145234d6 100644 --- a/apps/swagger/remi/src/lib/models/problem-details.ts +++ b/apps/swagger/remi/src/lib/models/problem-details.ts @@ -1,7 +1,7 @@ /* tslint:disable */ export interface ProblemDetails { detail?: string; - extensions?: {[key: string]: any}; + extensions: {[key: string]: any}; instance?: string; status?: number; title?: string; diff --git a/apps/swagger/remi/src/lib/models/query-settings-dto.ts b/apps/swagger/remi/src/lib/models/query-settings-dto.ts new file mode 100644 index 000000000..6d58c4cc6 --- /dev/null +++ b/apps/swagger/remi/src/lib/models/query-settings-dto.ts @@ -0,0 +1,8 @@ +/* tslint:disable */ +import { InputGroupDTO } from './input-group-dto'; +import { OrderByDTO } from './order-by-dto'; +export interface QuerySettingsDTO { + filter?: Array; + input?: Array; + orderBy?: Array; +} diff --git a/apps/swagger/remi/src/lib/models/response-args-of-query-settings-dto.ts b/apps/swagger/remi/src/lib/models/response-args-of-query-settings-dto.ts new file mode 100644 index 000000000..1cf6c1a3b --- /dev/null +++ b/apps/swagger/remi/src/lib/models/response-args-of-query-settings-dto.ts @@ -0,0 +1,6 @@ +/* tslint:disable */ +import { ResponseArgs } from './response-args'; +import { QuerySettingsDTO } from './query-settings-dto'; +export interface ResponseArgsOfQuerySettingsDTO extends ResponseArgs{ + result?: QuerySettingsDTO; +} diff --git a/apps/swagger/remi/src/lib/models/return-info-dto.ts b/apps/swagger/remi/src/lib/models/return-info-dto.ts index 6675d652b..da1d2de6e 100644 --- a/apps/swagger/remi/src/lib/models/return-info-dto.ts +++ b/apps/swagger/remi/src/lib/models/return-info-dto.ts @@ -20,6 +20,11 @@ export interface ReturnInfoDTO { */ itemId?: number; + /** + * Warengruppe + */ + productgroup?: string; + /** * Zu remittieren */ diff --git a/apps/swagger/remi/src/lib/models/return-item-dto.ts b/apps/swagger/remi/src/lib/models/return-item-dto.ts index 06c401fca..ffd948cb8 100644 --- a/apps/swagger/remi/src/lib/models/return-item-dto.ts +++ b/apps/swagger/remi/src/lib/models/return-item-dto.ts @@ -47,4 +47,5 @@ export interface ReturnItemDTO extends EntityDTOOfReturnItemDTOAndIReturnItem{ stock?: EntityDTOContainerOfStockDTO; stockEntry?: EntityDTOContainerOfStockEntryDTO; stockItem?: EntityDTOContainerOfStockItemDTO; + supplierInfo?: string; } diff --git a/apps/swagger/remi/src/lib/models/return-value-of-return-item-dto.ts b/apps/swagger/remi/src/lib/models/return-value-of-return-item-dto.ts new file mode 100644 index 000000000..4b727e577 --- /dev/null +++ b/apps/swagger/remi/src/lib/models/return-value-of-return-item-dto.ts @@ -0,0 +1,6 @@ +/* tslint:disable */ +import { ReturnValue } from './return-value'; +import { ReturnItemDTO } from './return-item-dto'; +export interface ReturnValueOfReturnItemDTO extends ReturnValue{ + result?: ReturnItemDTO; +} diff --git a/apps/swagger/remi/src/lib/models/iresult-of-return-suggestion-dto.ts b/apps/swagger/remi/src/lib/models/return-value-of-return-suggestion-dto.ts similarity index 50% rename from apps/swagger/remi/src/lib/models/iresult-of-return-suggestion-dto.ts rename to apps/swagger/remi/src/lib/models/return-value-of-return-suggestion-dto.ts index 5c61ffb6d..ea1773eaf 100644 --- a/apps/swagger/remi/src/lib/models/iresult-of-return-suggestion-dto.ts +++ b/apps/swagger/remi/src/lib/models/return-value-of-return-suggestion-dto.ts @@ -1,5 +1,6 @@ /* tslint:disable */ +import { ReturnValue } from './return-value'; import { ReturnSuggestionDTO } from './return-suggestion-dto'; -export interface IResultOfReturnSuggestionDTO { +export interface ReturnValueOfReturnSuggestionDTO extends ReturnValue{ result?: ReturnSuggestionDTO; } diff --git a/apps/swagger/remi/src/lib/models/return-value.ts b/apps/swagger/remi/src/lib/models/return-value.ts new file mode 100644 index 000000000..d446a8ed4 --- /dev/null +++ b/apps/swagger/remi/src/lib/models/return-value.ts @@ -0,0 +1,6 @@ +/* tslint:disable */ +export interface ReturnValue { + error: boolean; + invalidProperties?: {[key: string]: string}; + message?: string; +} diff --git a/apps/swagger/remi/src/lib/models/stock-info-dto.ts b/apps/swagger/remi/src/lib/models/stock-info-dto.ts index 64ece4a1a..5713edd90 100644 --- a/apps/swagger/remi/src/lib/models/stock-info-dto.ts +++ b/apps/swagger/remi/src/lib/models/stock-info-dto.ts @@ -35,6 +35,21 @@ export interface StockInfoDTO { */ itemId?: number; + /** + * ZOB + */ + minStockCategoryManagement?: number; + + /** + * FOB + */ + minStockLocal?: number; + + /** + * Warengruppe + */ + productgroup?: string; + /** * Bereits aus dem Bestand genommen */ diff --git a/apps/swagger/remi/src/lib/services/package.service.ts b/apps/swagger/remi/src/lib/services/package.service.ts index 127c885fe..d11db4eec 100644 --- a/apps/swagger/remi/src/lib/services/package.service.ts +++ b/apps/swagger/remi/src/lib/services/package.service.ts @@ -142,7 +142,7 @@ class PackageService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'PUT', - this.rootUrl + `/inventory/package/${encodeURIComponent(params.packageId)}`, + this.rootUrl + `/inventory/package/${encodeURIComponent(String(params.packageId))}`, __body, { headers: __headers, @@ -190,7 +190,7 @@ class PackageService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'PATCH', - this.rootUrl + `/inventory/package/${encodeURIComponent(params.packageId)}`, + this.rootUrl + `/inventory/package/${encodeURIComponent(String(params.packageId))}`, __body, { headers: __headers, @@ -235,7 +235,7 @@ class PackageService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'DELETE', - this.rootUrl + `/inventory/package/${encodeURIComponent(params.packageId)}`, + this.rootUrl + `/inventory/package/${encodeURIComponent(String(params.packageId))}`, __body, { headers: __headers, diff --git a/apps/swagger/remi/src/lib/services/remi.service.ts b/apps/swagger/remi/src/lib/services/remi.service.ts index 68370f28a..d24231789 100644 --- a/apps/swagger/remi/src/lib/services/remi.service.ts +++ b/apps/swagger/remi/src/lib/services/remi.service.ts @@ -15,6 +15,7 @@ import { ResponseArgsOfInteger } from '../models/response-args-of-integer'; import { ListResponseArgsOfReturnSuggestionDTO } from '../models/list-response-args-of-return-suggestion-dto'; import { BatchResponseArgsOfReturnSuggestionDTOAndReturnSuggestionDTO } from '../models/batch-response-args-of-return-suggestion-dtoand-return-suggestion-dto'; import { ReturnSuggestionDTO } from '../models/return-suggestion-dto'; +import { ResponseArgsOfQuerySettingsDTO } from '../models/response-args-of-query-settings-dto'; import { ResponseArgsOfIEnumerableOfInputDTO } from '../models/response-args-of-ienumerable-of-input-dto'; import { ResponseArgsOfIEnumerableOfKeyValueDTOOfStringAndString } from '../models/response-args-of-ienumerable-of-key-value-dtoof-string-and-string'; import { ResponseArgsOfIEnumerableOfValueTupleOfStringAndIntegerAndIntegerAndNullableIntegerAndString } from '../models/response-args-of-ienumerable-of-value-tuple-of-string-and-integer-and-integer-and-nullable-integer-and-string'; @@ -28,6 +29,7 @@ class RemiService extends __BaseService { static readonly RemiDeleteNullgutschriftenPath = '/remi/stock/{stockId}/nullgutschriften'; static readonly RemiUeberlaufPath = '/remi/ueberlauf'; static readonly RemiCreateReturnSuggestionsPath = '/inventory/ueberlauf/item'; + static readonly RemiRemissionQuerySettingsPath = '/remi/stock/{stockId}/supplier/{supplierId}/{remiType}/settings'; static readonly RemiGetPflichtremissionFilterPath = '/remi/stock/{stockId}/filter'; static readonly RemiGetPflichtremissionFilter2Path = '/remi/stock/{stockId}/pflichtremission/filter'; static readonly RemiGetUeberlaufFilterPath = '/remi/stock/{stockId}/ueberlauf/filter'; @@ -150,7 +152,7 @@ class RemiService extends __BaseService { if (params.department != null) __params = __params.set('department', params.department.toString()); let req = new HttpRequest( 'DELETE', - this.rootUrl + `/remi/stock/${encodeURIComponent(params.stockId)}/nullgutschriften`, + this.rootUrl + `/remi/stock/${encodeURIComponent(String(params.stockId))}/nullgutschriften`, __body, { headers: __headers, @@ -271,6 +273,61 @@ class RemiService extends __BaseService { ); } + /** + * Remission-Filter + * @param params The `RemiService.RemiRemissionQuerySettingsParams` containing the following parameters: + * + * - `supplierId`: Remissionsziel/Liferant PK + * + * - `stockId`: Lager PK + * + * - `remiType`: Remissionsart (pflichtremission, abteilungsremission) + * + * - `locale`: Lokalisierung + */ + RemiRemissionQuerySettingsResponse(params: RemiService.RemiRemissionQuerySettingsParams): __Observable<__StrictHttpResponse> { + let __params = this.newParams(); + let __headers = new HttpHeaders(); + let __body: any = null; + + + + if (params.locale != null) __params = __params.set('locale', params.locale.toString()); + let req = new HttpRequest( + 'GET', + this.rootUrl + `/remi/stock/${encodeURIComponent(String(params.stockId))}/supplier/${encodeURIComponent(String(params.supplierId))}/${encodeURIComponent(String(params.remiType))}/settings`, + __body, + { + headers: __headers, + params: __params, + responseType: 'json' + }); + + return this.http.request(req).pipe( + __filter(_r => _r instanceof HttpResponse), + __map((_r) => { + return _r as __StrictHttpResponse; + }) + ); + } + /** + * Remission-Filter + * @param params The `RemiService.RemiRemissionQuerySettingsParams` containing the following parameters: + * + * - `supplierId`: Remissionsziel/Liferant PK + * + * - `stockId`: Lager PK + * + * - `remiType`: Remissionsart (pflichtremission, abteilungsremission) + * + * - `locale`: Lokalisierung + */ + RemiRemissionQuerySettings(params: RemiService.RemiRemissionQuerySettingsParams): __Observable { + return this.RemiRemissionQuerySettingsResponse(params).pipe( + __map(_r => _r.body as ResponseArgsOfQuerySettingsDTO) + ); + } + /** * Filter * @param params The `RemiService.RemiGetPflichtremissionFilterParams` containing the following parameters: @@ -290,7 +347,7 @@ class RemiService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'GET', - this.rootUrl + `/remi/stock/${encodeURIComponent(params.stockId)}/filter`, + this.rootUrl + `/remi/stock/${encodeURIComponent(String(params.stockId))}/filter`, __body, { headers: __headers, @@ -340,7 +397,7 @@ class RemiService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'GET', - this.rootUrl + `/remi/stock/${encodeURIComponent(params.stockId)}/pflichtremission/filter`, + this.rootUrl + `/remi/stock/${encodeURIComponent(String(params.stockId))}/pflichtremission/filter`, __body, { headers: __headers, @@ -390,7 +447,7 @@ class RemiService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'GET', - this.rootUrl + `/remi/stock/${encodeURIComponent(params.stockId)}/ueberlauf/filter`, + this.rootUrl + `/remi/stock/${encodeURIComponent(String(params.stockId))}/ueberlauf/filter`, __body, { headers: __headers, @@ -437,7 +494,7 @@ class RemiService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'GET', - this.rootUrl + `/remi/stock/${encodeURIComponent(params.stockId)}/productgroup`, + this.rootUrl + `/remi/stock/${encodeURIComponent(String(params.stockId))}/productgroup`, __body, { headers: __headers, @@ -485,7 +542,7 @@ class RemiService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'POST', - this.rootUrl + `/remi/stock/${encodeURIComponent(params.stockId)}/ueberlauf/required-capacity`, + this.rootUrl + `/remi/stock/${encodeURIComponent(String(params.stockId))}/ueberlauf/required-capacity`, __body, { headers: __headers, @@ -604,6 +661,32 @@ module RemiService { locale?: null | string; } + /** + * Parameters for RemiRemissionQuerySettings + */ + export interface RemiRemissionQuerySettingsParams { + + /** + * Remissionsziel/Liferant PK + */ + supplierId: number; + + /** + * Lager PK + */ + stockId: number; + + /** + * Remissionsart (pflichtremission, abteilungsremission) + */ + remiType: null | string; + + /** + * Lokalisierung + */ + locale?: null | string; + } + /** * Parameters for RemiGetPflichtremissionFilter */ diff --git a/apps/swagger/remi/src/lib/services/return.service.ts b/apps/swagger/remi/src/lib/services/return.service.ts index bc0966260..7093f8478 100644 --- a/apps/swagger/remi/src/lib/services/return.service.ts +++ b/apps/swagger/remi/src/lib/services/return.service.ts @@ -139,7 +139,7 @@ class ReturnService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'POST', - this.rootUrl + `/inventory/return/${encodeURIComponent(params.returnId)}/receipt`, + this.rootUrl + `/inventory/return/${encodeURIComponent(String(params.returnId))}/receipt`, __body, { headers: __headers, @@ -187,7 +187,7 @@ class ReturnService extends __BaseService { if (params.eagerLoading != null) __params = __params.set('eagerLoading', params.eagerLoading.toString()); let req = new HttpRequest( 'GET', - this.rootUrl + `/inventory/return/${encodeURIComponent(params.returnId)}/receipt`, + this.rootUrl + `/inventory/return/${encodeURIComponent(String(params.returnId))}/receipt`, __body, { headers: __headers, @@ -238,7 +238,7 @@ class ReturnService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'PATCH', - this.rootUrl + `/inventory/return/${encodeURIComponent(params.returnId)}/receipt/${encodeURIComponent(params.receiptId)}/finalize`, + this.rootUrl + `/inventory/return/${encodeURIComponent(String(params.returnId))}/receipt/${encodeURIComponent(String(params.receiptId))}/finalize`, __body, { headers: __headers, @@ -285,7 +285,7 @@ class ReturnService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'PATCH', - this.rootUrl + `/inventory/return/${encodeURIComponent(params.returnId)}/finalize`, + this.rootUrl + `/inventory/return/${encodeURIComponent(String(params.returnId))}/finalize`, __body, { headers: __headers, @@ -329,7 +329,7 @@ class ReturnService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'POST', - this.rootUrl + `/remi/return/${encodeURIComponent(params.returnId)}/transfer-to-bookhit`, + this.rootUrl + `/remi/return/${encodeURIComponent(String(params.returnId))}/transfer-to-bookhit`, __body, { headers: __headers, @@ -379,7 +379,7 @@ class ReturnService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'POST', - this.rootUrl + `/inventory/return/${encodeURIComponent(params.returnId)}/receipt/${encodeURIComponent(params.receiptId)}/addreturnitem`, + this.rootUrl + `/inventory/return/${encodeURIComponent(String(params.returnId))}/receipt/${encodeURIComponent(String(params.receiptId))}/addreturnitem`, __body, { headers: __headers, @@ -432,7 +432,7 @@ class ReturnService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'POST', - this.rootUrl + `/inventory/return/${encodeURIComponent(params.returnId)}/receipt/${encodeURIComponent(params.receiptId)}/addreturnsuggestion`, + this.rootUrl + `/inventory/return/${encodeURIComponent(String(params.returnId))}/receipt/${encodeURIComponent(String(params.receiptId))}/addreturnsuggestion`, __body, { headers: __headers, @@ -485,7 +485,7 @@ class ReturnService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'DELETE', - this.rootUrl + `/inventory/return/${encodeURIComponent(params.returnId)}/receipt/${encodeURIComponent(params.receiptId)}/items/${encodeURIComponent(params.receiptItemId)}`, + this.rootUrl + `/inventory/return/${encodeURIComponent(String(params.returnId))}/receipt/${encodeURIComponent(String(params.receiptId))}/items/${encodeURIComponent(String(params.receiptItemId))}`, __body, { headers: __headers, @@ -535,7 +535,7 @@ class ReturnService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'DELETE', - this.rootUrl + `/inventory/return/${encodeURIComponent(params.returnId)}/receipt/${encodeURIComponent(params.receiptId)}/cancel`, + this.rootUrl + `/inventory/return/${encodeURIComponent(String(params.returnId))}/receipt/${encodeURIComponent(String(params.receiptId))}/cancel`, __body, { headers: __headers, @@ -580,7 +580,7 @@ class ReturnService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'GET', - this.rootUrl + `/inventory/stock/${encodeURIComponent(params.stockId)}/return/reasons`, + this.rootUrl + `/inventory/stock/${encodeURIComponent(String(params.stockId))}/return/reasons`, __body, { headers: __headers, @@ -669,7 +669,7 @@ class ReturnService extends __BaseService { if (params.eagerLoading != null) __params = __params.set('eagerLoading', params.eagerLoading.toString()); let req = new HttpRequest( 'GET', - this.rootUrl + `/inventory/return/${encodeURIComponent(params.returnId)}`, + this.rootUrl + `/inventory/return/${encodeURIComponent(String(params.returnId))}`, __body, { headers: __headers, @@ -714,7 +714,7 @@ class ReturnService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'DELETE', - this.rootUrl + `/inventory/return/${encodeURIComponent(params.returnId)}/cancel`, + this.rootUrl + `/inventory/return/${encodeURIComponent(String(params.returnId))}/cancel`, __body, { headers: __headers, @@ -757,7 +757,7 @@ class ReturnService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'PATCH', - this.rootUrl + `/inventory/returngroup/${encodeURIComponent(params.returnGroup)}/finalize`, + this.rootUrl + `/inventory/returngroup/${encodeURIComponent(String(params.returnGroup))}/finalize`, __body, { headers: __headers, @@ -803,7 +803,7 @@ class ReturnService extends __BaseService { if (params.eagerLoading != null) __params = __params.set('eagerLoading', params.eagerLoading.toString()); let req = new HttpRequest( 'GET', - this.rootUrl + `/inventory/return/item/${encodeURIComponent(params.itemId)}`, + this.rootUrl + `/inventory/return/item/${encodeURIComponent(String(params.itemId))}`, __body, { headers: __headers, @@ -848,7 +848,7 @@ class ReturnService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'DELETE', - this.rootUrl + `/inventory/return/item/${encodeURIComponent(params.itemId)}`, + this.rootUrl + `/inventory/return/item/${encodeURIComponent(String(params.itemId))}`, __body, { headers: __headers, @@ -894,7 +894,7 @@ class ReturnService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'PATCH', - this.rootUrl + `/inventory/return/item/${encodeURIComponent(params.itemId)}/impediment`, + this.rootUrl + `/inventory/return/item/${encodeURIComponent(String(params.itemId))}/impediment`, __body, { headers: __headers, @@ -985,7 +985,7 @@ class ReturnService extends __BaseService { if (params.eagerLoading != null) __params = __params.set('eagerLoading', params.eagerLoading.toString()); let req = new HttpRequest( 'GET', - this.rootUrl + `/inventory/return/suggestion/${encodeURIComponent(params.itemId)}`, + this.rootUrl + `/inventory/return/suggestion/${encodeURIComponent(String(params.itemId))}`, __body, { headers: __headers, @@ -1033,7 +1033,7 @@ class ReturnService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'PATCH', - this.rootUrl + `/inventory/return/suggestion/${encodeURIComponent(params.itemId)}/impediment`, + this.rootUrl + `/inventory/return/suggestion/${encodeURIComponent(String(params.itemId))}/impediment`, __body, { headers: __headers, @@ -1081,7 +1081,7 @@ class ReturnService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'POST', - this.rootUrl + `/inventory/stock/${encodeURIComponent(params.stockId)}/return/item`, + this.rootUrl + `/inventory/stock/${encodeURIComponent(String(params.stockId))}/return/item`, __body, { headers: __headers, @@ -1129,7 +1129,7 @@ class ReturnService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'POST', - this.rootUrl + `/inventory/stock/${encodeURIComponent(params.stockId)}/return/suggestion`, + this.rootUrl + `/inventory/stock/${encodeURIComponent(String(params.stockId))}/return/suggestion`, __body, { headers: __headers, @@ -1177,7 +1177,7 @@ class ReturnService extends __BaseService { if (params.department != null) __params = __params.set('department', params.department.toString()); let req = new HttpRequest( 'DELETE', - this.rootUrl + `/inventory/stock/${encodeURIComponent(params.stockId)}/return/suggestion`, + this.rootUrl + `/inventory/stock/${encodeURIComponent(String(params.stockId))}/return/suggestion`, __body, { headers: __headers, @@ -1222,7 +1222,7 @@ class ReturnService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'DELETE', - this.rootUrl + `/inventory/return/suggestion/${encodeURIComponent(params.suggestionId)}`, + this.rootUrl + `/inventory/return/suggestion/${encodeURIComponent(String(params.suggestionId))}`, __body, { headers: __headers, @@ -1265,7 +1265,7 @@ class ReturnService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'GET', - this.rootUrl + `/inventory/stock/${encodeURIComponent(params.stockId)}/return/uncompleted`, + this.rootUrl + `/inventory/stock/${encodeURIComponent(String(params.stockId))}/return/uncompleted`, __body, { headers: __headers, @@ -1311,7 +1311,7 @@ class ReturnService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'POST', - this.rootUrl + `/inventory/stock/${encodeURIComponent(params.stockId)}/return`, + this.rootUrl + `/inventory/stock/${encodeURIComponent(String(params.stockId))}/return`, __body, { headers: __headers, @@ -1362,7 +1362,7 @@ class ReturnService extends __BaseService { if (params.eagerLoading != null) __params = __params.set('eagerLoading', params.eagerLoading.toString()); let req = new HttpRequest( 'GET', - this.rootUrl + `/inventory/return/${encodeURIComponent(params.returnId)}/receipt/${encodeURIComponent(params.receiptId)}`, + this.rootUrl + `/inventory/return/${encodeURIComponent(String(params.returnId))}/receipt/${encodeURIComponent(String(params.receiptId))}`, __body, { headers: __headers, @@ -1415,7 +1415,7 @@ class ReturnService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'POST', - this.rootUrl + `/inventory/return/${encodeURIComponent(params.returnId)}/receipt/${encodeURIComponent(params.receiptId)}/item`, + this.rootUrl + `/inventory/return/${encodeURIComponent(String(params.returnId))}/receipt/${encodeURIComponent(String(params.receiptId))}/item`, __body, { headers: __headers, @@ -1471,7 +1471,7 @@ class ReturnService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'PUT', - this.rootUrl + `/inventory/return/${encodeURIComponent(params.returnId)}/receipt/${encodeURIComponent(params.receiptId)}/item/${encodeURIComponent(params.receiptItemId)}`, + this.rootUrl + `/inventory/return/${encodeURIComponent(String(params.returnId))}/receipt/${encodeURIComponent(String(params.receiptId))}/item/${encodeURIComponent(String(params.receiptItemId))}`, __body, { headers: __headers, @@ -1529,7 +1529,7 @@ class ReturnService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'PATCH', - this.rootUrl + `/inventory/return/${encodeURIComponent(params.returnId)}/receipt/${encodeURIComponent(params.receiptId)}/item/${encodeURIComponent(params.receiptItemId)}`, + this.rootUrl + `/inventory/return/${encodeURIComponent(String(params.returnId))}/receipt/${encodeURIComponent(String(params.receiptId))}/item/${encodeURIComponent(String(params.receiptItemId))}`, __body, { headers: __headers, @@ -1584,7 +1584,7 @@ class ReturnService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'DELETE', - this.rootUrl + `/inventory/return/${encodeURIComponent(params.returnId)}/receipt/${encodeURIComponent(params.receiptId)}/item/${encodeURIComponent(params.receiptItemId)}`, + this.rootUrl + `/inventory/return/${encodeURIComponent(String(params.returnId))}/receipt/${encodeURIComponent(String(params.receiptId))}/item/${encodeURIComponent(String(params.receiptItemId))}`, __body, { headers: __headers, @@ -1634,7 +1634,7 @@ class ReturnService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'GET', - this.rootUrl + `/inventory/stock/${encodeURIComponent(params.stockId)}/receipt/${encodeURIComponent(params.receiptType)}/uncompleted`, + this.rootUrl + `/inventory/stock/${encodeURIComponent(String(params.stockId))}/receipt/${encodeURIComponent(String(params.receiptType))}/uncompleted`, __body, { headers: __headers, diff --git a/apps/swagger/remi/src/lib/services/stock.service.ts b/apps/swagger/remi/src/lib/services/stock.service.ts index a602a19ab..49b4d2fea 100644 --- a/apps/swagger/remi/src/lib/services/stock.service.ts +++ b/apps/swagger/remi/src/lib/services/stock.service.ts @@ -64,7 +64,7 @@ class StockService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'POST', - this.rootUrl + `/remi/stock/${encodeURIComponent(params.stockId)}/instock`, + this.rootUrl + `/remi/stock/${encodeURIComponent(String(params.stockId))}/instock`, __body, { headers: __headers, @@ -159,7 +159,7 @@ class StockService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'POST', - this.rootUrl + `/remi/branch/${encodeURIComponent(params.branchNumber)}/checkitemsforreturn`, + this.rootUrl + `/remi/branch/${encodeURIComponent(String(params.branchNumber))}/checkitemsforreturn`, __body, { headers: __headers, @@ -209,7 +209,7 @@ class StockService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'POST', - this.rootUrl + `/remi/stock/${encodeURIComponent(params.stockId)}/instockbyean`, + this.rootUrl + `/remi/stock/${encodeURIComponent(String(params.stockId))}/instockbyean`, __body, { headers: __headers, @@ -363,7 +363,7 @@ class StockService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'GET', - this.rootUrl + `/inventory/branch/${encodeURIComponent(params.branchId)}/stock`, + this.rootUrl + `/inventory/branch/${encodeURIComponent(String(params.branchId))}/stock`, __body, { headers: __headers, @@ -451,7 +451,7 @@ class StockService extends __BaseService { let req = new HttpRequest( 'GET', - this.rootUrl + `/inventory/stock/stockitem/${encodeURIComponent(stockItemId)}`, + this.rootUrl + `/inventory/stock/stockitem/${encodeURIComponent(String(stockItemId))}`, __body, { headers: __headers, @@ -524,7 +524,7 @@ class StockService extends __BaseService { if (params.itemId != null) __params = __params.set('itemId', params.itemId.toString()); let req = new HttpRequest( 'GET', - this.rootUrl + `/inventory/stock/${encodeURIComponent(params.stockId)}/order`, + this.rootUrl + `/inventory/stock/${encodeURIComponent(String(params.stockId))}/order`, __body, { headers: __headers, @@ -567,7 +567,7 @@ class StockService extends __BaseService { if (params.itemId != null) __params = __params.set('itemId', params.itemId.toString()); let req = new HttpRequest( 'GET', - this.rootUrl + `/inventory/stock/${encodeURIComponent(params.stockId)}/reservation`, + this.rootUrl + `/inventory/stock/${encodeURIComponent(String(params.stockId))}/reservation`, __body, { headers: __headers, diff --git a/apps/swagger/remi/src/lib/services/supplier.service.ts b/apps/swagger/remi/src/lib/services/supplier.service.ts index e4412bc3c..2079de2a1 100644 --- a/apps/swagger/remi/src/lib/services/supplier.service.ts +++ b/apps/swagger/remi/src/lib/services/supplier.service.ts @@ -42,7 +42,7 @@ class SupplierService extends __BaseService { if (params.locale != null) __params = __params.set('locale', params.locale.toString()); let req = new HttpRequest( 'GET', - this.rootUrl + `/inventory/stock/${encodeURIComponent(params.stockId)}/supplier`, + this.rootUrl + `/inventory/stock/${encodeURIComponent(String(params.stockId))}/supplier`, __body, { headers: __headers, diff --git a/apps/swagger/remi/src/test.ts b/apps/swagger/remi/src/test.ts index 303b32a22..52e55168e 100644 --- a/apps/swagger/remi/src/test.ts +++ b/apps/swagger/remi/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, diff --git a/apps/ui/autocomplete/src/test.ts b/apps/ui/autocomplete/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/ui/autocomplete/src/test.ts +++ b/apps/ui/autocomplete/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/ui/calendar/src/test.ts b/apps/ui/calendar/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/ui/calendar/src/test.ts +++ b/apps/ui/calendar/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/ui/checkbox/src/test.ts b/apps/ui/checkbox/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/ui/checkbox/src/test.ts +++ b/apps/ui/checkbox/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/ui/common/src/lib/common.module.ts b/apps/ui/common/src/lib/common.module.ts index 702c72760..c6a99f542 100644 --- a/apps/ui/common/src/lib/common.module.ts +++ b/apps/ui/common/src/lib/common.module.ts @@ -1,4 +1,5 @@ import { NgModule } from '@angular/core'; +import { DateAdapter } from '.'; import { UiAutofocusDirective } from './autofocus.directive'; import { BlobImageDirective } from './blob-image.directive'; import { UiClickOutsideDirective } from './click-outside.directive'; @@ -29,4 +30,16 @@ const pipes = [StripHtmlTagsPipe, SubstrPipe, GroupByPipe, ReplacePipe]; declarations: [...components, ...directives, ...pipes], providers: [], }) -export class UiCommonModule {} +export class UiCommonModule { + static forRoot() { + return { + ngModule: UiCommonModule, + providers: [ + { + provide: DateAdapter, + useClass: DateAdapter, + }, + ], + }; + } +} diff --git a/apps/ui/common/src/lib/date/date.adapter.ts b/apps/ui/common/src/lib/date/date.adapter.ts index a863d0bab..6c517a8ee 100644 --- a/apps/ui/common/src/lib/date/date.adapter.ts +++ b/apps/ui/common/src/lib/date/date.adapter.ts @@ -1,5 +1,5 @@ import { Inject, Injectable, LOCALE_ID } from '@angular/core'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@utils/common'; type Precision = 'year' | 'month' | 'day' | 'time'; diff --git a/apps/ui/common/src/test.ts b/apps/ui/common/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/ui/common/src/test.ts +++ b/apps/ui/common/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/ui/datepicker/src/lib/body/body.component.scss b/apps/ui/datepicker/src/lib/body/body.component.scss index f2e04b5f5..9039dae36 100644 --- a/apps/ui/datepicker/src/lib/body/body.component.scss +++ b/apps/ui/datepicker/src/lib/body/body.component.scss @@ -3,6 +3,7 @@ table { font-size: 16px; margin-top: 14px; margin-bottom: 14px; + border-collapse: separate !important; } th { @@ -14,6 +15,7 @@ th { vertical-align: middle; width: 41px; height: 41px; + cursor: pointer; } .cell-is-today { diff --git a/apps/ui/datepicker/src/lib/datepicker.component.html b/apps/ui/datepicker/src/lib/datepicker.component.html index fabcb4ff2..111a9da06 100644 --- a/apps/ui/datepicker/src/lib/datepicker.component.html +++ b/apps/ui/datepicker/src/lib/datepicker.component.html @@ -3,12 +3,12 @@ -
+
-
- diff --git a/apps/ui/datepicker/src/lib/datepicker.component.scss b/apps/ui/datepicker/src/lib/datepicker.component.scss index 1c39cdec7..dbb7a6b56 100644 --- a/apps/ui/datepicker/src/lib/datepicker.component.scss +++ b/apps/ui/datepicker/src/lib/datepicker.component.scss @@ -8,8 +8,8 @@ .hr { height: 1px; @apply bg-wild-blue-yonder; - margin-left: -18px; - margin-right: -18px; + margin-left: -20px; + margin-right: -20px; } .dp { diff --git a/apps/ui/datepicker/src/lib/datepicker.module.ts b/apps/ui/datepicker/src/lib/datepicker.module.ts index 1622b69df..cc567777d 100644 --- a/apps/ui/datepicker/src/lib/datepicker.module.ts +++ b/apps/ui/datepicker/src/lib/datepicker.module.ts @@ -4,10 +4,10 @@ import { UiDatepickerComponent } from './datepicker.component'; import { CommonModule } from '@angular/common'; import { UiDatepickerBodyComponent, UiDatepickerCellDirective, GetCellNamePipe } from './body'; import { UiDatepickerHeaderComponent } from './header'; -import { IconModule } from '@libs/ui'; +import { UiIconModule } from '@ui/icon'; @NgModule({ - imports: [CommonModule, IconModule], + imports: [CommonModule, UiIconModule], exports: [UiDatepickerComponent], declarations: [UiDatepickerComponent, UiDatepickerBodyComponent, UiDatepickerHeaderComponent, UiDatepickerCellDirective, GetCellNamePipe], providers: [], diff --git a/apps/ui/datepicker/src/lib/header/header.component.html b/apps/ui/datepicker/src/lib/header/header.component.html index c793102e4..e672206c2 100644 --- a/apps/ui/datepicker/src/lib/header/header.component.html +++ b/apps/ui/datepicker/src/lib/header/header.component.html @@ -1,13 +1,9 @@ -
- {{ monthName$ | async }} {{ yearName$ | async }} -
diff --git a/apps/ui/datepicker/src/test.ts b/apps/ui/datepicker/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/ui/datepicker/src/test.ts +++ b/apps/ui/datepicker/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/ui/dropdown/src/test.ts b/apps/ui/dropdown/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/ui/dropdown/src/test.ts +++ b/apps/ui/dropdown/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/ui/filter/src/lib/next/filter-group/filter-input-group-main/filter-input-group-main.component.scss b/apps/ui/filter/src/lib/next/filter-group/filter-input-group-main/filter-input-group-main.component.scss index 159baf09e..2241b39f6 100644 --- a/apps/ui/filter/src/lib/next/filter-group/filter-input-group-main/filter-input-group-main.component.scss +++ b/apps/ui/filter/src/lib/next/filter-group/filter-input-group-main/filter-input-group-main.component.scss @@ -3,7 +3,7 @@ } .info-tooltip-button { - @apply border-font-customer bg-white rounded-md text-base font-bold absolute; + @apply border-font-customer border-2 border-solid bg-white rounded-md text-base font-bold absolute; border-style: outset; width: 31px; height: 31px; diff --git a/apps/ui/filter/src/lib/next/filter-group/filter-input-group-main/filter-input-group-main.component.ts b/apps/ui/filter/src/lib/next/filter-group/filter-input-group-main/filter-input-group-main.component.ts index 92507ce28..ba0a6ec32 100644 --- a/apps/ui/filter/src/lib/next/filter-group/filter-input-group-main/filter-input-group-main.component.ts +++ b/apps/ui/filter/src/lib/next/filter-group/filter-input-group-main/filter-input-group-main.component.ts @@ -80,7 +80,7 @@ export class UiFilterInputGroupMainComponent implements OnInit, OnDestroy, After ) {} ngOnInit() { - this._autocompleteProvider = this.autocompleteProviders.find((provider) => !!provider); + this._autocompleteProvider = this.autocompleteProviders?.find((provider) => !!provider); this._scanProvider = this.scanProviders?.find((provider) => provider.for === 'main'); } diff --git a/apps/ui/filter/src/lib/next/tree/ui-filter.ts b/apps/ui/filter/src/lib/next/tree/ui-filter.ts index f90afeef4..7a1440814 100644 --- a/apps/ui/filter/src/lib/next/tree/ui-filter.ts +++ b/apps/ui/filter/src/lib/next/tree/ui-filter.ts @@ -114,7 +114,7 @@ export class UiFilter implements IUiFilter { } const inputsWithTheSameGroup = this.filter?.find((f) => f.group === group); - const inputsWithTargetInput = inputsWithTheSameGroup.input.filter((ig) => ig.selected && ig.target === 'input'); + const inputsWithTargetInput = inputsWithTheSameGroup?.input?.filter((ig) => ig.selected && ig.target === 'input'); if (inputsWithTargetInput?.length) { inputsWithTargetInput.forEach((ifk) => { diff --git a/apps/ui/filter/src/test.ts b/apps/ui/filter/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/ui/filter/src/test.ts +++ b/apps/ui/filter/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/ui/form-control/src/lib/ui-form-control.component.scss b/apps/ui/form-control/src/lib/ui-form-control.component.scss index 2f7effad6..4575af54c 100644 --- a/apps/ui/form-control/src/lib/ui-form-control.component.scss +++ b/apps/ui/form-control/src/lib/ui-form-control.component.scss @@ -36,18 +36,18 @@ button.clear { width: 1em; &[type='radio'] { - mask: url('/assets/radio.svg') no-repeat; + mask: url('/assets/images/radio.svg') no-repeat; &:checked { - mask: url('/assets/radio_checked.svg') no-repeat; + mask: url('/assets/images/radio_checked.svg') no-repeat; } } &[type='checkbox'] { - mask: url('/assets/checkbox.svg') no-repeat; + mask: url('/assets/images/checkbox.svg') no-repeat; &:checked { - mask: url('/assets/checkbox_checked.svg') no-repeat; + mask: url('/assets/images/checkbox_checked.svg') no-repeat; } } } diff --git a/apps/ui/form-control/src/lib/ui-form-first-error.pipe.ts b/apps/ui/form-control/src/lib/ui-form-first-error.pipe.ts index 2a036bc88..5b19c6d15 100644 --- a/apps/ui/form-control/src/lib/ui-form-first-error.pipe.ts +++ b/apps/ui/form-control/src/lib/ui-form-first-error.pipe.ts @@ -7,7 +7,6 @@ export class UiFormControlFirstErrorPipe implements PipeTransform { transform(errors: ValidationErrors, label: string): string { if (errors) { const error = Object.keys(errors)[0]; - switch (error) { case 'min': return `${label} wird benötigt`; // gender validation for create (upgrade) online customer with gender min value of 2 diff --git a/apps/ui/form-control/src/test.ts b/apps/ui/form-control/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/ui/form-control/src/test.ts +++ b/apps/ui/form-control/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/ui/icon/src/lib/icon-badge/icon-badge.component.html b/apps/ui/icon/src/lib/icon-badge/icon-badge.component.html index ed04fffb6..c9f102052 100644 --- a/apps/ui/icon/src/lib/icon-badge/icon-badge.component.html +++ b/apps/ui/icon/src/lib/icon-badge/icon-badge.component.html @@ -1,2 +1,2 @@ - + diff --git a/apps/ui/icon/src/test.ts b/apps/ui/icon/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/ui/icon/src/test.ts +++ b/apps/ui/icon/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/ui/input/src/lib/ui-input.directive.ts b/apps/ui/input/src/lib/ui-input.directive.ts index b785c8525..5eb40a48a 100644 --- a/apps/ui/input/src/lib/ui-input.directive.ts +++ b/apps/ui/input/src/lib/ui-input.directive.ts @@ -70,7 +70,7 @@ export class UiInputDirective extends UiFormControlDirective implements Con }, 0); } - @HostListener('keyup', ['$event.target.value']) + @HostListener('input', ['$event.target.value']) setValue(value: any, emitEvent = true) { if (this.value !== value) { this.currentValue = value; diff --git a/apps/ui/input/src/test.ts b/apps/ui/input/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/ui/input/src/test.ts +++ b/apps/ui/input/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/ui/loader/src/test.ts b/apps/ui/loader/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/ui/loader/src/test.ts +++ b/apps/ui/loader/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/ui/modal/src/lib/confirm-modal/confirm-modal.component.scss b/apps/ui/modal/src/lib/confirm-modal/confirm-modal.component.scss new file mode 100644 index 000000000..ee1522806 --- /dev/null +++ b/apps/ui/modal/src/lib/confirm-modal/confirm-modal.component.scss @@ -0,0 +1,23 @@ +:host { + @apply flex flex-col; +} + +.message { + @apply text-base text-center whitespace-pre-line break-words my-4; +} + +.actions { + @apply text-right; + + button { + @apply mx-2; + } + + button.cancel { + @apply px-5 py-3 bg-white text-brand text-cta-l rounded-full border-none outline-none; + } + + button.confirm { + @apply px-5 py-3 bg-brand text-white text-cta-l rounded-full border-none outline-none; + } +} diff --git a/apps/ui/modal/src/lib/confirm-modal/confirm-modal.component.ts b/apps/ui/modal/src/lib/confirm-modal/confirm-modal.component.ts new file mode 100644 index 000000000..1c7c624b4 --- /dev/null +++ b/apps/ui/modal/src/lib/confirm-modal/confirm-modal.component.ts @@ -0,0 +1,22 @@ +import { Component, OnInit } from '@angular/core'; +import { UiModalRef } from '../defs'; +import { ConfirmModalData } from './confirm-modal.data'; + +@Component({ + selector: 'ui-confirm-modal', + template: ` +

+ {{ modalRef.data.message }} +

+
+ + +
+ `, + styleUrls: ['./confirm-modal.component.scss'], +}) +export class UiConfirmModalComponent implements OnInit { + constructor(public modalRef: UiModalRef) {} + + ngOnInit() {} +} diff --git a/apps/ui/modal/src/lib/confirm-modal/confirm-modal.data.ts b/apps/ui/modal/src/lib/confirm-modal/confirm-modal.data.ts new file mode 100644 index 000000000..412250bd4 --- /dev/null +++ b/apps/ui/modal/src/lib/confirm-modal/confirm-modal.data.ts @@ -0,0 +1,5 @@ +export interface ConfirmModalData { + message: string; + rejectLabel?: string; + confirmLabel?: string; +} diff --git a/apps/ui/modal/src/lib/confirm-modal/index.ts b/apps/ui/modal/src/lib/confirm-modal/index.ts new file mode 100644 index 000000000..6871fa746 --- /dev/null +++ b/apps/ui/modal/src/lib/confirm-modal/index.ts @@ -0,0 +1,2 @@ +export * from './confirm-modal.component'; +export * from './confirm-modal.data'; diff --git a/apps/ui/modal/src/lib/dialog/dialog-modal.component.scss b/apps/ui/modal/src/lib/dialog/dialog-modal.component.scss index 1f42e775b..2dd522210 100644 --- a/apps/ui/modal/src/lib/dialog/dialog-modal.component.scss +++ b/apps/ui/modal/src/lib/dialog/dialog-modal.component.scss @@ -11,14 +11,14 @@ } .content { - @apply text-base text-center whitespace-pre; + @apply text-base text-center whitespace-pre mt-8; } .actions { - @apply text-center; + @apply text-center mt-8; button { - @apply border-2 border-solid border-brand bg-white text-brand rounded-full py-3 px-6 font-bold text-lg outline-none self-end whitespace-nowrap; + @apply border-2 border-solid border-brand bg-white text-brand rounded-full py-3 px-6 font-bold text-lg outline-none self-end whitespace-nowrap ml-4; &.selected { @apply bg-brand text-white; diff --git a/apps/ui/modal/src/lib/dialog/dialog-modal.component.ts b/apps/ui/modal/src/lib/dialog/dialog-modal.component.ts index d4d94651f..0e50ccd88 100644 --- a/apps/ui/modal/src/lib/dialog/dialog-modal.component.ts +++ b/apps/ui/modal/src/lib/dialog/dialog-modal.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit } from '@angular/core'; -import { UiModalRef } from '@ui/modal'; +import { UiModalRef } from '../defs/modal-ref'; import { DialogModel } from './dialog.model'; @Component({ @@ -17,5 +17,7 @@ export class UiDialogModalComponent implements OnInit { this.modalRef.close(); return; } + + this.modalRef.close(command); } } diff --git a/apps/ui/modal/src/lib/dialog/index.ts b/apps/ui/modal/src/lib/dialog/index.ts index 215d1fab8..62c081b66 100644 --- a/apps/ui/modal/src/lib/dialog/index.ts +++ b/apps/ui/modal/src/lib/dialog/index.ts @@ -1,3 +1,3 @@ -// start:ng42.barrel export * from './dialog-modal.component'; -// end:ng42.barrel +export * from './dialog.model'; +export * from './open-dialog.interceptor'; diff --git a/apps/ui/modal/src/lib/dialog/open-dialog.interceptor.ts b/apps/ui/modal/src/lib/dialog/open-dialog.interceptor.ts index dd7d8d6a7..f18512024 100644 --- a/apps/ui/modal/src/lib/dialog/open-dialog.interceptor.ts +++ b/apps/ui/modal/src/lib/dialog/open-dialog.interceptor.ts @@ -1,6 +1,7 @@ import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { UiDialogModalComponent, UiModalService } from '@ui/modal'; +import { UiModalService } from '../modal.service'; +import { UiDialogModalComponent } from './dialog-modal.component'; import { Observable, throwError } from 'rxjs'; import { catchError, tap } from 'rxjs/operators'; import { DialogModel } from './dialog.model'; @@ -13,7 +14,7 @@ export class OpenDialogInterceptor implements HttpInterceptor { return next.handle(req).pipe( tap((response) => { if (response instanceof HttpResponse) { - if (response.body.dialog?.area === 'dialog') { + if (response?.body?.dialog?.area === 'dialog') { this.openDialog(response.body.dialog); } } diff --git a/apps/ui/modal/src/lib/index.ts b/apps/ui/modal/src/lib/index.ts index 0081a2e48..1a0b46da2 100644 --- a/apps/ui/modal/src/lib/index.ts +++ b/apps/ui/modal/src/lib/index.ts @@ -1,10 +1,9 @@ -// start:ng42.barrel +export * from './confirm-modal'; export * from './defs'; -export * from './modal.component'; -export * from './debug-modal/debug-modal.component'; -export * from './message-modal.component'; export * from './dialog'; +export * from './error-modal'; +export * from './message-modal.component'; +export * from './modal.component'; export * from './modal.module'; export * from './modal.service'; -export * from './error-modal'; -// end:ng42.barrel +export * from './prompt-modal'; diff --git a/apps/ui/modal/src/lib/message-modal.component.scss b/apps/ui/modal/src/lib/message-modal.component.scss index 6ef2e76b2..1af1d0c32 100644 --- a/apps/ui/modal/src/lib/message-modal.component.scss +++ b/apps/ui/modal/src/lib/message-modal.component.scss @@ -2,12 +2,8 @@ @apply flex flex-col; } -.title { - @apply text-xl text-center; -} - .message { - @apply text-base text-center whitespace-pre; + @apply text-base text-center whitespace-pre my-8; } .actions { diff --git a/apps/ui/modal/src/lib/message-modal.component.ts b/apps/ui/modal/src/lib/message-modal.component.ts index 2a1c48860..429622b24 100644 --- a/apps/ui/modal/src/lib/message-modal.component.ts +++ b/apps/ui/modal/src/lib/message-modal.component.ts @@ -4,10 +4,9 @@ import { UiModalRef } from './defs'; @Component({ selector: 'ui-message-modal', template: ` -

{{ modalRef.data.title }}

{{ modalRef.data.message }}

- +
`, styleUrls: ['./message-modal.component.scss'], diff --git a/apps/ui/modal/src/lib/modal.component.scss b/apps/ui/modal/src/lib/modal.component.scss index 9df974282..0e2d0303a 100644 --- a/apps/ui/modal/src/lib/modal.component.scss +++ b/apps/ui/modal/src/lib/modal.component.scss @@ -26,7 +26,7 @@ } .modal-title { - @apply text-card-sub font-bold text-center pt-5; + @apply text-card-sub font-bold text-center py-5 whitespace-pre-wrap; } ::ng-deep .branch ui-modal .modal-close-btn { diff --git a/apps/ui/modal/src/lib/modal.module.ts b/apps/ui/modal/src/lib/modal.module.ts index a80af2ebe..56da79f4e 100644 --- a/apps/ui/modal/src/lib/modal.module.ts +++ b/apps/ui/modal/src/lib/modal.module.ts @@ -9,11 +9,12 @@ import { UiIconModule } from '@ui/icon'; import { UiDialogModalComponent } from './dialog/dialog-modal.component'; import { HTTP_INTERCEPTORS } from '@angular/common/http'; import { OpenDialogInterceptor } from './dialog/open-dialog.interceptor'; +import { UiPromptModalComponent } from './prompt-modal'; @NgModule({ imports: [CommonModule, OverlayModule, UiIconModule], - declarations: [UiModalComponent, UiDebugModalComponent, UiMessageModalComponent, UiDialogModalComponent], - exports: [UiModalComponent, UiDebugModalComponent, UiMessageModalComponent, UiDialogModalComponent], + declarations: [UiModalComponent, UiDebugModalComponent, UiMessageModalComponent, UiDialogModalComponent, UiPromptModalComponent], + exports: [UiModalComponent, UiDebugModalComponent, UiMessageModalComponent, UiDialogModalComponent, UiPromptModalComponent], }) export class UiModalModule { static forRoot(): ModuleWithProviders { diff --git a/apps/ui/modal/src/lib/prompt-modal/index.ts b/apps/ui/modal/src/lib/prompt-modal/index.ts new file mode 100644 index 000000000..69fcd16b1 --- /dev/null +++ b/apps/ui/modal/src/lib/prompt-modal/index.ts @@ -0,0 +1,2 @@ +export * from './prompt-modal.component'; +export * from './prompt-modal.data'; diff --git a/apps/ui/modal/src/lib/prompt-modal/prompt-modal.component.scss b/apps/ui/modal/src/lib/prompt-modal/prompt-modal.component.scss new file mode 100644 index 000000000..4a8cc584d --- /dev/null +++ b/apps/ui/modal/src/lib/prompt-modal/prompt-modal.component.scss @@ -0,0 +1,27 @@ +:host { + @apply flex flex-col; +} + +.message { + @apply text-base text-center whitespace-pre-line break-words my-4; +} + +input { + @apply text-lg my-4 mx-auto w-96 border-b-2 border-gray-300 focus:border-brand focus:outline-none px-3 py-1; +} + +.actions { + @apply text-center; + + button { + @apply mx-2; + } + + button.cancel { + @apply px-5 py-3 bg-white text-brand text-cta-l rounded-full border-none outline-none; + } + + button.confirm { + @apply px-5 py-3 bg-brand text-white text-cta-l rounded-full border-none outline-none; + } +} diff --git a/apps/ui/modal/src/lib/prompt-modal/prompt-modal.component.ts b/apps/ui/modal/src/lib/prompt-modal/prompt-modal.component.ts new file mode 100644 index 000000000..18096f4c6 --- /dev/null +++ b/apps/ui/modal/src/lib/prompt-modal/prompt-modal.component.ts @@ -0,0 +1,23 @@ +import { Component, OnInit } from '@angular/core'; +import { UiModalRef } from '../defs'; +import { PromptModalData } from './prompt-modal.data'; + +@Component({ + selector: 'ui-prompt-modal', + template: ` +

+ {{ modalRef.data.message }} +

+ +
+ + +
+ `, + styleUrls: ['./prompt-modal.component.scss'], +}) +export class UiPromptModalComponent implements OnInit { + constructor(public modalRef: UiModalRef) {} + + ngOnInit() {} +} diff --git a/apps/ui/modal/src/lib/prompt-modal/prompt-modal.data.ts b/apps/ui/modal/src/lib/prompt-modal/prompt-modal.data.ts new file mode 100644 index 000000000..dafa5b2c2 --- /dev/null +++ b/apps/ui/modal/src/lib/prompt-modal/prompt-modal.data.ts @@ -0,0 +1,7 @@ +export interface PromptModalData { + message: string; + placeholder?: string; + defaultValue?: string; + cancelText?: string; + confirmText?: string; +} diff --git a/apps/ui/modal/src/test.ts b/apps/ui/modal/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/ui/modal/src/test.ts +++ b/apps/ui/modal/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/ui/notes/src/lib/ui-notes.component.scss b/apps/ui/notes/src/lib/ui-notes.component.scss index 9089b4c04..76ba70f60 100644 --- a/apps/ui/notes/src/lib/ui-notes.component.scss +++ b/apps/ui/notes/src/lib/ui-notes.component.scss @@ -6,7 +6,7 @@ @apply flex flex-row items-center; textarea { - @apply flex-grow text-regular h-8 outline-none w-full px-3 border-disabled-branch border-solid border-2 rounded-card pt-3; + @apply flex-grow text-regular h-12 outline-none w-full px-3 border-disabled-branch border-solid border-2 rounded-card pt-3; } textarea::placeholder { diff --git a/apps/ui/notes/src/test.ts b/apps/ui/notes/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/ui/notes/src/test.ts +++ b/apps/ui/notes/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/ui/progress/src/test.ts b/apps/ui/progress/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/ui/progress/src/test.ts +++ b/apps/ui/progress/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/ui/quantity-dropdown/src/test.ts b/apps/ui/quantity-dropdown/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/ui/quantity-dropdown/src/test.ts +++ b/apps/ui/quantity-dropdown/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/ui/radio/src/test.ts b/apps/ui/radio/src/test.ts index 504678a21..06ab84cfa 100644 --- a/apps/ui/radio/src/test.ts +++ b/apps/ui/radio/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; diff --git a/apps/ui/scroll-container/src/lib/scroll-container.component.html b/apps/ui/scroll-container/src/lib/scroll-container.component.html index f2b700dfb..933b160fc 100644 --- a/apps/ui/scroll-container/src/lib/scroll-container.component.html +++ b/apps/ui/scroll-container/src/lib/scroll-container.component.html @@ -1,6 +1,7 @@
-
+